ClassPathXMLApplicationContext上下文加载过程

今天看了一下《spring技术内幕》,看了下spring IOC容器的加载过程,但是里面的代码很杂,就自己用源码的测试用例debug了一下看了下过程

测试用例

    @Test
    public void testSingleConfigLocation() throws IOException {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(FQ_SIMPLE_CONTEXT);
        assertTrue(ctx.containsBean("someMessageSource"));
        LazyBean bean = ctx.getBean(LazyBean.class);
        Environment environment =ctx.getEnvironment();
        String[] profiles =environment.getDefaultProfiles();
        for(String s :profiles){
            System.out.println(s);
        }
        CollectionBean bean2 = (CollectionBean) ctx.getBean("collection");
        SingletonBean bean3 = ctx.getBean(SingletonBean.class);
        SingletonBean bean4 = ctx.getBean(SingletonBean.class);
        if (bean != null) {
            System.out.println(bean.getTestFiled());
        }
        if (bean2 != null) {
            System.out.println(bean2.getList());
        }
        if (bean3 != null) {
            bean3.process();
        }
        if (bean4 != null) {
            bean4.process();
        }
        System.out.println(ctx.getBeanDefinitionCount());
        /*
         * System.out.println(ctx.getBean("lazyBean"));
         * System.out.println(ctx.getBean("lazyBean"));
         */
        ctx.close();
    }

加载过程

  • 首先会调用父类AbstractApplicationContext的静态初始化块
    static {
        // Eagerly load the ContextClosedEvent class to avoid weird classloader issues
        // on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
        ContextClosedEvent.class.getName();
    }

大概就是说为了避免一些奇怪的问题会首先发布一个ContextClosedEvent事件,然后按照正常的初始化依次初始化父类。

  • 调用构造方法中的refresh方法,这个方法也是上下文初始化的开始
    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
  • refresh方法会调用AbstractRefreshableApplicationContext中的refreshBeanFactory方法创建一个DefaultListableBeanFactory实例,作为上下文的容器
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
  • 创建了容器之后就要从xml文件中加载和注册bean的定义,上面的loadBeanDefinition方法在AbstractXmlApplicationContext中的实现会被调用
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context‘s
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

    /**
     * Initialize the bean definition reader used for loading the bean
     * definitions of this context. Default implementation is empty.
     * <p>Can be overridden in subclasses, e.g. for turning off XML validation
     * or using a different XmlBeanDefinitionParser implementation.
     * @param reader the bean definition reader used by this context
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
     */
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
     * method; hence this method is just supposed to load and/or register bean definitions.
     * @param reader the XmlBeanDefinitionReader to use
     * @throws BeansException in case of bean registration errors
     * @throws IOException if the required XML document isn‘t found
     * @see #refreshBeanFactory
     * @see #getConfigLocations
     * @see #getResources
     * @see #getResourcePatternResolver
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }
  • 这两个方法会用来加载并注册BeanDefiniton,它们又会使用XmlBeanDefinitionReader来加载xml中的bean的定义,bean定义的解析会使用DefaultBeanDefinitionDocumentReader,它又会使用BeanDefinitionParserDelegate类委托解析bean的定义
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//委托BeanDefinitionParserDelegate进行解析 BeanDefinitionHolder bdHolder
= delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name ‘" + bdHolder.getBeanName() + "‘", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

上面黑体标注的是将解析的BeanDefinition注册到上下文中的过程,我之前没找到这个方法很奇怪loadBeanDefinition返回的都是int我就在想,BeanDefinition哪去了。静态方法会调用BeanDefinitionRegistry

接口的registerBeanDefinition方法。而DefaultListableBeanFactory实现了这个接口,

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

到此BeanDefinition就被注册到了ClassPathXmlApplicationContext中,还有很多细节没贴出来,自己debug一下很有意思的

 

ClassPathXMLApplicationContext上下文加载过程

上一篇:常用monkey_app稳定性


下一篇:Spring和MyBatis整合