Spring源码之ContextLoaderListener(2)

上一篇博文Spring源码之ContextLoaderListener(1)介绍了ContextLoaderListener加载过程,最终要的部分就是如何加载我们指定的applicationContext.xml的。

ps:这部分内容比较复杂,因此我们先不用了解Spring的各个类的继承、引用关系,在本文中我将直接写那个类的那个方法被调用(贴出关键代码),一来是为了了解重点代码的位置,方便入门,以后如果定位问题的话能很快找到相关类的相关方法;二来能从宏观角度了解一下整个加载过程,等觉得对这个整体部分有了更深入的了解之后,在往更深层体味一下Spring的框架是怎么设计的。整个是一个循序渐进的过程。

屁话不多说,进入正题:

还记得ServletContextListener主要执行的contextInitalized(event)主要执行逻辑吗?

//读取ContextLoader.properties获取XmlWebApplicationContext
//通过反射获取webApplicationContext实例
this.context = this.createWebApplicationContext(servletContext);
//加载配置文件(最关键的、最复杂部分)
this.configureAndRefreshWebApplicationContext(err, servletContext);
//将WebApplicationContext放入到ServletContext中
servletContext.setAttribute("org.springframework.web.context.WebApplicationContext.ROOT", this.context);

//加载bean配置的方法
ContextLoader.configureAndRefreshWebApplicationContext(err, servletContext)方法的执行

web.xml中有一行配置,在这个方法中就有体现

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/spring/applicationContext*.xml</param-value>
</context-param>

主要代码如下

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//看到这行很激动吗?web.xml中配置的参数吗哈哈
String configLocationParam = sc.getInitParameter("contextConfigLocation");
//重头戏就是这个方法WebAcpplicationContext.refresh()
wac.refresh();
}

wac就是org.springframework.web.context.support.XmlWebApplicationContext

wac的一个父类AbstractApplicationContext
该类中定义的refresh()是一个模板方法,部分需要子类来重写

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            //①这一行就是wac的refresh()的重点方法了
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
               //...
            } finally {
                this.resetCommonCaches();
            }
        }
    }

①代码中的这行调用关系如下:
AbstractApplicationContext.refresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
AbstractApplicationContext.refreshBeanFactory();
AbstractRefreshableApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory ex)
AbstractXmlApplicationContext.loadBeanDefinitions(XmlBeanDefinitionReader );
你可能都看烦了,但是记住一点即可:再怎么调用都是在ApplicationContext父类与子类之间来回调用。
XmlBeanDefinitionReader .loadBeanDefinitions(String… configLocations); //不定长参数的
这一行是将加载bean的定义委托给XmlBeanDefinitionReader,终于从ApplicationContext的各种继承关系中转到了XmlBeanDefinitionReader中了,后续你会发现Spring使用同样的手段在父子类中来回掉,最后委托给另一个对象,看BeanDefinitionReader是怎么来回在父子类中跳转的。
AbstractBeanDefinitionReader.loadBeanDefinitions(String location);//父类中一个参数的
AbstractBeanDefinitionReader.loadBeanDefinitions(Resources… resources);//重载了一下而已
XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource);//又回到子类中了
XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource);//终于看到Document对象了(在父子类中跳转了多次),这个就是解析Xml的Document对象,来贴上点代码:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
  Document ex = this.doLoadDocument(inputSource, resource);
  return this.registerBeanDefinitions(ex, resource);
}

看到这两行代码感觉眼前一亮,有点快要看到怎么加载applicationContext.xml了,看到注册bean的方法了。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        //核心,转移到了BeanDefinitionDocumentReader了
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
}

又委托给了BeanDefinitionDocumentReader,再跟进去,去他父类中了
DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc,XmlReaderContext readContext);

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        //哎吆!看到root--beans就是<beans>标签
        Element root = doc.getDocumentElement();
        //下面的代码就是这个方法
        this.doRegisterBeanDefinitions(root);
    }

我还要在贴2段代码,让你激动一下,一步步看到怎么解析的

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        //
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if(this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if(StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
    //预处理  让子类来重写  默认是空实现
        this.preProcessXml(root);
        //核心代码
        this.parseBeanDefinitions(root, this.delegate);
        //后处理  让子类来重写  默认是空实现
        this.postProcessXml(root);
        this.delegate = parent;
    }

上面代码有个this.delegate即BeanDefinitionParserDelegate这个类
你打开看下会有惊喜:xml中对应的标签和属性都会在这里有声明过

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if(delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if(node instanceof Element) {
                    Element ele = (Element)node;
                    if(delegate.isDefaultNamespace(ele)) {
                    //通用的import bean 标签
                        this.parseDefaultElement(ele, delegate);
                    } else {
                    //定制的标签 tx等
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

再来一段

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if(delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if(delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if(delegate.nodeNameEquals(ele, "bean")) {
            this.processBeanDefinition(ele, delegate);
        } else if(delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }

    }

到这里我需要说什么。

总结:
1、AbstractApplicationContext.refresh()方法中的一行代码:
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
在各个父子类中跳转添加逻辑。
2、委托给XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource);将文件内容加载到Document对象。
3、在转给DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc,XmlReaderContext readContext);内部在调用
this.parseBeanDefinitions(root, this.delegate);方法来解析Bean的定义

到这里我们已经能看到beans bean import alias等Spring中默认的xml中有的标签了。
再次声明一下,本文只是为了找到关键代码,中间为什么这样设计不在本文范畴。
下一节将看看如何将bean的id name class 子标签放入到Spring容器中的,敬请期待!!!

猜你喜欢

转载自blog.csdn.net/mayongzhan_csdn/article/details/60329612