- 浏览: 1826389 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (665)
- 闲话 (17)
- ruby (1)
- javascript (40)
- linux (7)
- android (22)
- 开发过程 (11)
- 哥也读读源代码 (13)
- JVM (1)
- ant (2)
- Hibernate (3)
- jboss (3)
- web service (17)
- https (4)
- java基础 (17)
- spring (7)
- servlet (3)
- 杂记 (39)
- struts2 (10)
- logback (4)
- 多线程 (2)
- 系统诊断 (9)
- UI (4)
- json (2)
- Java EE (7)
- eclipse相关 (4)
- JMS (1)
- maven (19)
- 版本管理 (7)
- sso (1)
- ci (1)
- 设计 (18)
- 戒烟 (4)
- http (9)
- 计划 (4)
- HTML5 (3)
- chrome extensions (5)
- tomcat源码阅读 (4)
- httpd (5)
- MongoDB (3)
- node (2)
最新评论
-
levin_china:
勾选了,还是找不到
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
我用的maven-3.5.0,还没有遇到这种情况,使用jar ...
用spring annotation声明的bean,当打包在jar中时,无法被扫描到 -
GGGGeek:
受益匪浅,从组织项目结构,到技术细节,讲的很到位,只是博主不再 ...
一个多maven项目聚合的实例 -
Aaron-Joe-William:
<?xml version="1.0" ...
hibernate逆向工程 -
li272355201:
http://archive.apache.org/dist/ ...
tomcat源码阅读(一)——环境搭建
实际开发中,比较多的项目是web项目,这时候加载spring,是在web.xml里配置一个Listener
这个ContextLoaderListener就是web应用中,加载spring的入口
如果不是web应用,一般通过实例化一个ApplicationContext的子类来加载spring。过程是大同小异的,这部分在后面的博客里再说,不是这篇的重点
接下来就从ContextLoaderListener说起:
入口是contextInitialized方法
这里面的createContextLoader()是一个@Deprecated方法,直接返回null。这里ContextLoaderListener是继承自ContextLoader,所以有一句看起来比较奇怪的代码
为什么这么做的原因,我想可能是这样的话,就可以把大部分的功能代码移到ContextLoader里,可以保持ContextLoaderListener自身的代码比较简单
然后接下来关键的代码
这个initWebApplicationContext()方法是在ContextLoader里定义的
比较长,可以分解开来看,首先是:
首先检查是不是已经注册了ApplicationContext作为ServletContext的一个属性,如果是的话,就抛出异常,避免重复注册
然后获取日志组件,这里其实有个问题,就是Common Logging实际上比较老,现在很多开源框架,都改用slf4j作为日志组件的facade,但是spring这里把它写死了,所以就很难无缝地引入slf4j了
然后记录了启动日志,并记录开始启动的时间
接下来就是本方法的实体,省略了try/catch,只看业务部分
这段代码里,有几个方法是比较重要的,createWebApplicationContext()实际创建了WebApplicationContext,configureAndRefreshWebApplicationContext()初始化了刚创建的WebApplicationContext
在创建和初始化完成之后,将其设置为ServletContext的一个属性,这是WebApplicationContextUtils能够获取到spring容器的基础
然后是对ClassLoader的一些处理
这段我没有看懂它是要干嘛
之后又是日志记录和时间记录
从上面我们可以看到,ContextLoader的加载,整个流程是很清晰的。其中的核心是WebApplicationContext的创建和初始化,然后将其设置为ServletContext的一个属性,这样就可以很容易地从工具类中获取到spring容器了
接下来就看一下WebApplicationContext是怎么创建和初始化的
这个方法是比较简单的,首先通过determineContextClass()方法,来确定WebApplicationContext的类型
如果没有在web.xml中特别配置ServletContext的InitParameter的话,会返回XmlWebApplicationContext,这也是99%会遇到的情况
然后就通过BeanUtils.instantiateClass()方法,创建XmlWebApplicationContext并返回。XmlWebApplicationContext是WebApplicationContext接口一个最下层的实现类,这条继承路径上,自上而下分别是WebApplicationContext-->ConfigurableWebApplicationContext-->AbstractRefreshableWebApplicationContext-->XmlWebApplicationContext。ApplicationContext接口的继承体系是比较复杂的,具体的可以看第一篇博客里的ea图
创建完毕之后,就要对刚创建的XmlWebApplicationContext进行初始化:
这个方法也比较长,需要拆开看
这个部分是给XmlWebApplicationContext设置一个id,作为一个标识,不是很重要
然后是设置继承体系:
这里也是依赖ServletContext的InitParameter,也就是说:一般来说不会发生
然后是给XmlWebApplicationContext设置ServletContext,这步很简单,但这样设置之后,从spring容器内部,就可以获取到ServletContext了,所以是很重要的
接下来是设置spring配置文件的路径
如果没有配置的话,之后就默认会在WEB-INF下寻找applicationContext.xml,如果配置了的话:
就会在指定的路径加载配置文件
然后是一个留给用户扩展的方法customizeContext(),大部分情况下,在这个方法内,什么事情也不会发生
最后就是核心方法
这个refresh()方法,是在ApplicationContext继承体系中,比较上层的ConfigurableApplicationContext里定义的,是XmlWebApplicationContext初始化的核心,在这个方法里,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作,在下一篇博客里,再详细介绍
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
这个ContextLoaderListener就是web应用中,加载spring的入口
如果不是web应用,一般通过实例化一个ApplicationContext的子类来加载spring。过程是大同小异的,这部分在后面的博客里再说,不是这篇的重点
接下来就从ContextLoaderListener说起:
入口是contextInitialized方法
public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); }
这里面的createContextLoader()是一个@Deprecated方法,直接返回null。这里ContextLoaderListener是继承自ContextLoader,所以有一句看起来比较奇怪的代码
this.contextLoader = this;
为什么这么做的原因,我想可能是这样的话,就可以把大部分的功能代码移到ContextLoader里,可以保持ContextLoaderListener自身的代码比较简单
然后接下来关键的代码
this.contextLoader.initWebApplicationContext(event.getServletContext());
这个initWebApplicationContext()方法是在ContextLoader里定义的
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext); } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
比较长,可以分解开来看,首先是:
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis();
首先检查是不是已经注册了ApplicationContext作为ServletContext的一个属性,如果是的话,就抛出异常,避免重复注册
然后获取日志组件,这里其实有个问题,就是Common Logging实际上比较老,现在很多开源框架,都改用slf4j作为日志组件的facade,但是spring这里把它写死了,所以就很难无缝地引入slf4j了
然后记录了启动日志,并记录开始启动的时间
接下来就是本方法的实体,省略了try/catch,只看业务部分
if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext); } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context;
这段代码里,有几个方法是比较重要的,createWebApplicationContext()实际创建了WebApplicationContext,configureAndRefreshWebApplicationContext()初始化了刚创建的WebApplicationContext
在创建和初始化完成之后,将其设置为ServletContext的一个属性,这是WebApplicationContextUtils能够获取到spring容器的基础
然后是对ClassLoader的一些处理
ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); }
这段我没有看懂它是要干嘛
之后又是日志记录和时间记录
从上面我们可以看到,ContextLoader的加载,整个流程是很清晰的。其中的核心是WebApplicationContext的创建和初始化,然后将其设置为ServletContext的一个属性,这样就可以很容易地从工具类中获取到spring容器了
接下来就看一下WebApplicationContext是怎么创建和初始化的
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); return wac; }
这个方法是比较简单的,首先通过determineContextClass()方法,来确定WebApplicationContext的类型
protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
如果没有在web.xml中特别配置ServletContext的InitParameter的话,会返回XmlWebApplicationContext,这也是99%会遇到的情况
然后就通过BeanUtils.instantiateClass()方法,创建XmlWebApplicationContext并返回。XmlWebApplicationContext是WebApplicationContext接口一个最下层的实现类,这条继承路径上,自上而下分别是WebApplicationContext-->ConfigurableWebApplicationContext-->AbstractRefreshableWebApplicationContext-->XmlWebApplicationContext。ApplicationContext接口的继承体系是比较复杂的,具体的可以看第一篇博客里的ea图
创建完毕之后,就要对刚创建的XmlWebApplicationContext进行初始化:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName())); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } } // Determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(sc); wac.setParent(parent); wac.setServletContext(sc); String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { wac.setConfigLocation(initParameter); } customizeContext(sc, wac); wac.refresh(); }
这个方法也比较长,需要拆开看
if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName())); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } }
这个部分是给XmlWebApplicationContext设置一个id,作为一个标识,不是很重要
然后是设置继承体系:
ApplicationContext parent = loadParentContext(sc); wac.setParent(parent);
这里也是依赖ServletContext的InitParameter,也就是说:一般来说不会发生
然后是给XmlWebApplicationContext设置ServletContext,这步很简单,但这样设置之后,从spring容器内部,就可以获取到ServletContext了,所以是很重要的
wac.setServletContext(sc);
接下来是设置spring配置文件的路径
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { wac.setConfigLocation(initParameter); }
如果没有配置的话,之后就默认会在WEB-INF下寻找applicationContext.xml,如果配置了的话:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> WEB-INF/beans.xml WEB-INF/cxf.xml </param-value> </context-param>
就会在指定的路径加载配置文件
然后是一个留给用户扩展的方法customizeContext(),大部分情况下,在这个方法内,什么事情也不会发生
最后就是核心方法
wac.refresh();
这个refresh()方法,是在ApplicationContext继承体系中,比较上层的ConfigurableApplicationContext里定义的,是XmlWebApplicationContext初始化的核心,在这个方法里,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作,在下一篇博客里,再详细介绍
评论
3 楼
kyfxbl
2012-08-06
哦,理解错了,以为你问这个方法在哪调用的
这个方法的定义,没记错的话是在AbstractApplicationContext里
这个方法的定义,没记错的话是在AbstractApplicationContext里
2 楼
kyfxbl
2012-08-06
在ContextLoader里
1 楼
liuwuhen
2012-08-06
lz configureAndRefreshWebApplicationContext这个方法是那个类中?
发表评论
-
小读spring ioc源码(五)——BeanDefinitionDocumentReader
2012-07-28 13:11 2784上一篇博客说到,BeanDefinition的解析,已经走到了 ... -
小读spring ioc源码(四)——BeanDefinitionReader
2012-07-24 19:02 2414上一篇博客说到,ApplicationContext将解析Be ... -
小读spring ioc源码(三)——XmlWebApplicationContext初始化的整体过程
2012-07-12 16:54 2892上一篇说到,ContextLoader ... -
小读spring ioc源码(一)——整体介绍
2012-06-18 22:59 2573最近在读spring ioc的源码,用EA画了几张比较清楚的类 ... -
读logback源码系列文章(八)——记录日志的实际工作类Encoder
2011-10-17 20:34 2960本系列的博客从logback怎么对接slf4j开始,逐步介绍了 ... -
读logback源码系列文章(七)——配置的实际工作类Action
2011-10-11 22:59 3511上篇博客介绍了ContextInitializer类如何把框架 ... -
读logback源码系列文章(六)——ContextInitializer
2011-10-09 21:46 6765放了一个长假回来啦,继续写本系列博客 这篇博客我们接着上一篇 ... -
读logback源码系列文章(五)——Appender
2011-09-16 21:03 13794明天要带老婆出国旅游 ... -
读logback源码系列文章(四)——记录日志
2011-09-12 02:22 4389今天晚上本来想来写一下Logger怎么记录日志,以及Appen ... -
读logback源码系列文章(三)——创建Logger
2011-09-07 21:35 6159上一篇博客介绍了logback的StaticLoggerBin ... -
读logback源码系列文章(二)——提供ILoggerFactory
2011-09-07 20:07 6605上篇博客介绍了logback是 ... -
读logback源码系列文章(一)——对接slf4j
2011-08-29 02:43 7463以前也读过一些开源项目的源码,主要是spring和ant,不过 ...
相关推荐
Spring IoC源码深度剖析开源架构源码2021.pdf
SpringIOC源码解说,以及对应的代码解读。
NULL 博文链接:https://kyfxbl.iteye.com/blog/1563532
个人整理SpringIOC源码解读,望大婶们指点指点,后续继续添加
老王读 Spring IoC 系列文章的源码分析&测试代码 Spring IoC 源码分析,帮助大家学习 Spring 源码,更加了解 Spring 的底层 博客专栏地址:https://blog.csdn.net/wang489687009/category_11269905.html
这是spring_ioc部分的内容。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。...
Spring源码分析_Spring_IOC
1.Spring入门和IOC介绍 2.对象依赖 3.AOP入门 4.JDBCTemplate和Spring事务 5.Spring事务原理 6.Spring事务的一个线程安全问题 7.IOC再回顾和面试题 8.AOP再回顾
Spring4 IOC 示例源码, 包含演示代码,以及所有涉及到的jar包。
spring ioc指的是控制反转,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由Spring容器统一进行管理,从而实现松耦合
Spring IoC 入门实例,包括了多种依赖注入方式,适合Spring初学者学习使用。实例里有详细的注释,一看就懂。
spring ioc容器初始化流程图 spring ioc容器依赖注入流程图 spring aop实现原理流程图
Java进阶之SpringIoC源码深度剖析共19页.pdf.zip
我们从一个简单的容器开始,一步步的重构,最后实现一个基本的Spring框架的雏形,为了帮助我们更加深入的理解Spring的IoC的原理和源码。 详细内容见博文: 【SSH进阶之路】一步步重构容器实现Spring的IoC——从一个...
springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC...
spring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demo...
本文深入探讨了Spring框架中IoC容器的源码机制,涵盖了容器的初始化、Bean工厂的实例化、Bean定义的读取及Spring Bean的生命周期管理。通过精细的分析,本文揭示了AnnotationConfigApplicationContext的实例化过程,...
SpringIoc示例代码,SpringIoc示例代码,SpringIoc示例代码,SpringIoc示例代码
1、 需要去构造一个Spring容器:ClassPathXmlApplicationContext; 2、 需要注解定义两个注解:@Service、@Resource; 3、 使用反射的手段读取指定目录下的class信息,解析Class信息; 4、 对Class信息的注解做处理...
spring Ioc容器配置 IOC容器数据源配置 <!-- 配置数据源 --> destroy-method="close"> <value>org.gjt.mm.mysql.Driver <value>jdbc:mysql://localhost:3306/demo <value>root ...