Spring源码之ContextLoaderListener(2)

来源:互联网 发布:淘宝兼职被骗了怎么办 编辑:程序博客网 时间:2024/05/23 12:38

上一篇博文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容器中的,敬请期待!!!

0 0
原创粉丝点击