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容器中的,敬请期待!!!
- Spring源码之ContextLoaderListener(2)
- Spring源码之ContextLoaderListener(1)
- Spring源码之ContextLoaderListener(3)
- Spring-web源码解析之ContextLoaderListener
- Spring源码学习(二)ContextLoaderListener
- ContextLoaderListener类(spring源码解析)
- Spring ContextLoaderListener源码分析
- Spring ContextLoaderListener源码分析 .
- Spring ContextLoaderListener源码分析 .
- spring mvc 之ContextLoaderListener
- Spring之ContextLoaderListener
- 小读spring ioc源码(二)——ContextLoaderListener
- Spring之ContextLoaderListener的作用
- Spring之ContextLoaderListener的作用
- Spring之ContextLoaderListener的作用
- Spring之ContextLoaderListener的作用
- Spring之ContextLoaderListener的作用
- Spring之ContextLoaderListener的作用
- Navigation_ResourceNotFound: turtlebot_rviz_launchers或者[view_navigation.launch] is neither a launch
- Java中关于原子性
- iOS学习笔记-044.UIScrollView分页加强——连续滚动优化
- 动态规划(3)-最长公共子序列
- JAVA泛型中:? T K V E意义小结
- Spring源码之ContextLoaderListener(2)
- <opencv>滑动条的创建与使用
- 布隆过滤器
- CCF201612-1 中间数(解法三)(100分)
- RNN中输出端的sample采样
- 如何在andorid工程中嵌入开发者盈利广告
- Jquery——Day9(选项卡)
- Linux文件管理
- Java学习篇之源文件声明