从启动日志看Spring IOC的初始化和Bean生命周期
一、Tomcat中启动IoC容器的日志
启动Tomcat等容器时,控制台每次都打印出一些日志。
最近刚好在研究Spring源码,所以换个角度,从启动日志来简单的看看Spring的初始化过程!
以下是Tomcat启动时日志,截取Spring部分。
//------------------------------------- //从这里开始Spring的初始化 十一月 10, 2015 8:52:03 上午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring root WebApplicationContext 十一月 10, 2015 8:52:03 上午 org.springframework.web.context.ContextLoader initWebApplicationContext //ApplicationContext的预处理prepareRefresh 信息: Root WebApplicationContext: initialization started 十一月 10, 2015 8:52:04 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing Root WebApplicationContext: startup date [Tue Nov 10 08:52:04 CST 2015]; root of context hierarchy //XmlBeanDefinitionReader的loadBeanDefinitions,加载bean 十一月 10, 2015 8:52:04 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions //<!-- bean定义的配置文件 --> //<context-param> // <param-name>contextConfigLocation</param-name> // <param-value>classpath:applicationContext.xml</param-value> //</context-param> 信息: Loading XML bean definitions from class path resource [applicationContext.xml] //初始化单例的bean //由于applicationContext.xml中定义了数据源所以有dataSource、jdbcTemplate,定义了事务所以有transactionManager、txAdvice 十一月 10, 2015 8:52:06 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6f1ad0: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,dataSource,jdbcTemplate,transactionManager,org.springframework.aop.config.internalAutoProxyCreator,serviceMethod,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0,txAdvice,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy //初始化C3P0数据库连接池 //mchange是C3P0的依赖包 十一月 10, 2015 8:52:06 上午 com.mchange.v2.log.MLog <clinit> 信息: MLog clients using java 1.4+ standard logging. 十一月 10, 2015 8:52:07 上午 com.mchange.v2.c3p0.C3P0Registry banner 信息: Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10] 十一月 10, 2015 8:52:08 上午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization completed in 4470 ms //到这里完成了WebApplicationContext的初始化 //-------------------------------------
二、大概流程:
1、初始化方法是在ContextLoader.initWebApplicationContext()中完成的;
2、首先进行的是预处理操作:AbstractApplicationContext.prepareRefresh();
3、然后从资源(这里是applicationContext.xml)中读取bean的解析、加载bean:XmlBeanDefinitionReader.loadBeanDefinitions();
4、实例化单例的bean,实例化scope="singleton"(默认)且无lazy-init="true"的bean:DefaultListableBeanFactory.preInstantiateSingletons();
5、注册C3P0数据库连接池:C3P0Registry.banner();
6、到此完成了WebApplicationContext的初始化!
从上面的步奏可以大概看出Spring初始化的步奏,当然每个方法都隐藏了很多细节,后面再逐个慢慢品味吧!
------------------------------------------------------------------------------------------------------
三、打开Debug日志,进一步观察:
默认的Spring自带了log4j,但是需要我们配置。如果不配置,像debug没有打印出来,下面我们将debug等信息输出到控制台,便于我们分析源码!
在web.xml中声明log4.properties,在资源文件中配置:(调到Debug级别)
log4j.rootLogger=DEBUG, R log4j.appender.R = org.apache.log4j.ConsoleAppender log4j.appender.R.Target = System.out log4j.appender.R.layout = org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern =[SSH] %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c %m(%L) %n
然后,我们就可以看到debug日志了!
关键日志分析:
可以结合Bean的生命周期一起分析:
为了便于观察,applicationContext.xml中只配置最基本的bean
<bean id="book" name="book" class="com.sky.vo.Book" init-method="productBook" destroy-method="destroyBook"> <property name="title" value="小王子"/> </bean> <bean id="myBeanFactoryPostFactory" class="com.sky.processor.MyBeanFactoryPostFactory"/> <bean id="myBeanPostProcessor" class="com.sky.processor.MyBeanPostProcessor"/>
四、下面是日志的分析过程:
搜索web.xml中的配置信息
Adding [servletContextInitParams] PropertySource with lowest search precedence(107)
XmlWebApplicationContext
XmlWebApplicationContext Refreshing Root WebApplicationContext: startup date [Thu Nov 19 11:18:22 CST 2015]; root of context hierarchy(513)
从XML中读取bean定义信息
XmlBeanDefinitionReader Loading XML bean definitions from class path resource [applicationContext.xml](315)
使用到了Reader,从applicationContext.xml中读到三个bean(book和myBeanPostProcessor,还有BeanFactoryPostFactory)
注:定义了后置处理器,除了实现BeanPostProcessor接口,然后像普通bean一样在xml中配置即可!
XmlBeanDefinitionReader Loaded 3 bean definitions from location pattern [classpath:applicationContext.xml](216)
给ApplicationContext配置BeanFactory,中找到<bean id="book">,<bean id="myBeanPostProcessor">的定义
XmlWebApplicationContext Bean factory for Root WebApplicationContext: org.springframework.beans.factory.support.DefaultListableBeanFactory@148d148: defining beans [book,myBeanFactoryPostFactory,myBeanPostProcessor]; root of factory hierarchy(543)
首先实例化单例的BeanFactoryPostProcessor,Spring容器后置处理器
DefaultListableBeanFactory Creating shared instance of singleton bean 'myBeanFactoryPostFactory'(215) DefaultListableBeanFactory Creating instance of bean 'myBeanFactoryPostFactory'(432)
调用了他的构造方法,于是控制台打印了:MyBeanFactoryPostProcessor构造初始化
为了解决bean的循环依赖,也把这个容器后置处理器像普通bean一样缓存起来了
Eagerly caching bean 'myBeanFactoryPostFactory' to allow for resolving potential circular references(506)
到这一步时,完成了Spring容器后置处理器的实例化
DefaultListableBeanFactory Finished creating instance of bean 'myBeanFactoryPostFactory'(460)
同时马上调用了容器后置处理器的方法,打印出了:MyBeanFactoryPostProcessor调用了postProcessBeanFactory
接下来,实例化BeanPostProcessor后置处理器
DefaultListableBeanFactory Creating shared instance of singleton bean 'myBeanPostProcessor'(215) DefaultListableBeanFactory Creating instance of bean 'myBeanPostProcessor'(432)
到这里时,完成了后置处理器的初始化,构造方法被调用了:
控制台输出了:这是自定义的BeanPostProcessor 初始化
然后,像普通的bean那样,缓存下来,以解决潜在的bean循环依赖的问题
DefaultListableBeanFactory Eagerly caching bean 'myBeanPostProcessor' to allow for resolving potential circular references(506)
继续执行,完成了BeanPostProcessor这个后置处理器的实例化
DefaultListableBeanFactory Finished creating instance of bean 'myBeanPostProcessor'(460)
给ApplicationContext配置其他,如国际化资源、ApplicationContext事件、主题,这里暂时不太明白作用
XmlWebApplicationContext Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@1fb4479](810)
XmlWebApplicationContext Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@1c57594](834)
UiApplicationContextUtils Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.ResourceBundleThemeSource@1feeacb](85)
预实例化单例bean
DefaultListableBeanFactory Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@fe4495: defining beans [book]; root of factory hierarchy(598)
创建book的bean
DefaultListableBeanFactory Creating shared instance of singleton bean 'book'(215) DefaultListableBeanFactory Creating instance of bean 'book'(432)
到这里后,开始调用了构造函数,输出了我写的sysout: 控制台:"调用了Book默认构造函数"
缓存id为book的bean,解决潜在的循环引用!
DefaultListableBeanFactory Eagerly caching bean 'book' to allow for resolving potential circular references(506)
到这里后,调用了setting注入属性title,控制台输出了我写在setTitle方法中语句:set注入了书本标题为:小王子
在setting注入后,随即输出的是:bean处理器:bean创建前,表示在注入后,调用了BeanPostProcessor的postProcessBeforeInitialization方法(看方法名也很直观)
调用InitializingBean接口的afterPropertiesSet方法
DefaultListableBeanFactory Invoking afterPropertiesSet() on bean with name 'book'
这时控制台输出写在afterPropertiesSet的语句:InitializingBean,相当于init-method
调用生命周期方法init-method,在ApplicationContext.xml中bean中配置:
DefaultListableBeanFactory Invoking init method 'productBook' on bean with name 'book'(1612)
这里输出了我自定义的init-method为birth方法的语句:书本初始化init-method
然后这里调用了Bean后置处理器的postProcessAfterInitialization方法,因为控制台打印了:bean处理器:bean创建后
到这里完成了bean的实例化
DefaultListableBeanFactory Finished creating instance of bean 'book'(460)
接下来和后置处理器相关,不太明白这里的返回是什么意思?
DefaultListableBeanFactory Returning cached instance of singleton bean 'myBeanFactoryPostFactory'(246) DefaultListableBeanFactory Returning cached instance of singleton bean 'myBeanPostProcessor'(246)
没有找到生命周期处理器,可能是我们没有定义,所以使用默认的。看不懂,不过看表述是这个意思。
XmlWebApplicationContext Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@dd7469](861)
返回缓存了这个处理器
DefaultListableBeanFactory Returning cached instance of singleton bean 'lifecycleProcessor'(246)
接下来的日志,更看不懂!总之,大概是JNDI相关的。。。
PropertySourcesPropertyResolver Searching for key 'spring.liveBeansView.mbeanDomain' in [servletConfigInitParams](81) PropertySourcesPropertyResolver Searching for key 'spring.liveBeansView.mbeanDomain' in [servletContextInitParams](81) PropertySourcesPropertyResolver Searching for key 'spring.liveBeansView.mbeanDomain' in [jndiProperties](81) JndiTemplate Looking up JNDI object with name [java:comp/env/spring.liveBeansView.mbeanDomain](150) PropertySourcesPropertyResolver Could not find key 'spring.liveBeansView.mbeanDomain' in any property source. Returning [null](103)
将Spring容器设置到Servlet容器(Web容器)中:
ContextLoader Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT](326)
五、正确的关闭Tomcat,而非kill时:
销毁单例的bean,从这里也可以看到,Spring容器对prototype的bean不理会
DefaultListableBeanFactory Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@148d148: defining beans [book,myBeanFactoryPostFactory,myBeanPostProcessor]; root of factory hierarchy(444)
调用DisposableBean接口的destroy方法
DisposableBeanAdapter Invoking destroy() on bean with name 'book'(226)
打印出来:DisposableBean,相当于destroy-method
调用bean的destroy-method
DisposableBeanAdapter Invoking destroy method 'destroyBook' on bean with name 'book'(302)
打印:书本被销毁destroy-method
六、几个不小问题
1、如果name="book"的bean设置lazy-init="true",则启动时不会实例化这个bean,日志中只会打印MyPostBeanProcessor实例化;
2、如果name="book"的bean设置scope="prototype",同样不会在启动时实例化,也不会缓存;什么时候实例化?getBean
而且prototype的bean,IoC容器只负责反射实例化,后续的生命周期不负责,相当于只new,当然像后置处理器等还是要执行的
3、BeanPostProcessor是IoC的一个扩展点,让用户有机会修改bean!而BeanFactoryPostProcessor是让用户有机会修改IoC容器的扩展点!
4、Spring的开闭原则:对扩展开放,对修改关闭??不太理解!
5、init-method和InitializingBean.afterPropertiesSet区别?
最终作用都一样bean构造后执行初始化的方法
由于是接口,命名是限定死的,init-method属性更灵活
其他同样作用的方法,一种特殊的BeanPostProcessor,系统自动的CommonAnnotationBeanPostProcessor
-由于用标签开启了,<context:annotation-config />,作用类同<bean class="...CommonAnnotationBeanPostProcessor"/>
其中Spring自带的注解@PostConstruct、@PreDestroy,用来修饰Book的另外两个方法,这样同时存在三种初始化方法!
CommonAnnotationBeanPostProcessor Invoking init method on bean 'book
初始时机:@PreDestroy----->InitializingBean------->init-method
注销时机:@PreDestroy----->DisposableBean----->destroy-method
6、另外,如果Bean需要持有BeanFactory或ApplicationContext,则可以通过实现BeanFactoryAware和ApplicationContextAware接口获取到,(通过@AutoWired应该也行吧!),调用时期:在实例化后setting的过程中:其实很容易理解,这和普通属性的注入是一样的嘛!
所以:打印
set注入了书本标题为:小王子 注入Bean工厂,通过BeanFactoryAware注入了BeanFactory 注入Spring应用上下文,通过ApplicationContextWare注入ApplicationContext
所以,生命周期图:
-------------------------------------------------------------------------------------------------------
七、覆盖Spring的源代码
按照网上很多博客,先从GitHub中下载Spring源码,然后使用Gradle编译转换成Eclipse可以导入的工程,但是貌似由于网络原因,一直失败,感觉非常麻烦!
后来想了想,要修改源码,其实还有一个小技巧,就是覆盖jar中文文件!!!
尝试1:直接将源码包下的单个文件夹spring-web下的org文件,复制到用来测试的Spring工程下(已经用pom.xml引入了Spring的所有包),引入之后,一大片报错!缺少很多相关的jar包,总不可能一个个找出来吧!只有放弃!
尝试2:如果我只是修改单个文件呢,如ContextLoader.java,单独复制这个文件会怎样?
结果是还是报错:
不过比想象中好很多,只有一个servlet包找不到。原来是之前发布的方式是Tomcat提供的,所以这里pom.xml中显示引入
<!-- 添加Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
重新编译后,终于不再报错了!
打开文件ContextLoader,修改几行代码,看看运行时是否执行了!
方便起见,修改几行日志文件:
//servletContext.log("Initializing Spring root WebApplicationContext"); servletContext.log("你正在启动Spring容器:WebApplicationContext!!!!!"); if (logger.isInfoEnabled()) { //logger.info("Root WebApplicationContext: initialization started"); logger.info("Spring容器开始初始化:Root WebApplicationContext: initialization started"); }
重新启动Tomcat,打印的日志:
从启动日志看Spring IOC的初始化和Bean生命周期的更多相关文章
- 【Spring Framework】Spring IOC详解及Bean生命周期详细过程
Spring IOC 首先,在此之前,我们就必须先知道什么是ioc,ioc叫做控制反转,也可以称为依赖注入(DI),实际上依赖注入是ioc的另一种说法, 1.谁控制谁?: 在以前,对象的创建和销毁都是 ...
- spring源码分析系列5:ApplicationContext的初始化与Bean生命周期
回顾Bean与BeanDefinition的关系. BeanFactory容器. ApplicationContext上下文. 首先总结下: 开发人员定义Bean信息:分为XML形式定义:注解式定义 ...
- Spring BeanFactory 初始化 和 Bean 生命周期
(version:spring-context-4.3.15.RELEASE) AbstractApplicationContext#refresh() public void refresh() t ...
- Spring Framework核心概念之Bean生命周期管理
目录 Spring Bean的生命周期 相关接口的分类 测试SpringBean生命周期的Demo程序 小结 Spring Bean的生命周期 Spring容器既Application或者WebApp ...
- Spring源码分析:Spring IOC容器初始化
概述: Spring 对于Java 开发来说,以及算得上非常基础并且核心的框架了,在有一定开发经验后,阅读源码能更好的提高我们的编码能力并且让我们对其更加理解.俗话说知己知彼,百战不殆.当你对Spri ...
- SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用
目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...
- 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean
让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...
- 整理在Spring IOC容器初始化后可以处理特定逻辑的多种实现方式
Spring框架的核心是依赖注入.切面:Spring Boot是在Spring框架的基础上为其提供许多默认配置.默认约定(约定优于配置),从而达到减少或减化配置进而可开箱即用.快速上手:Spring ...
- Spring Boot 启动源码解析结合Spring Bean生命周期分析
转载请注明出处: 1.SpringBoot 源码执行流程图 2. 创建SpringApplication 应用,在构造函数中推断启动应用类型,并进行spring boot自动装配 public sta ...
随机推荐
- 使用XSD校验Mybatis的SqlMapper配置文件(1)
这篇文章以前面对SqlSessionFactoryBean的重构为基础,先简单回顾一下做了哪些操作: 新建SqlSessionFactoryBean,初始代码和mybatis-spring相同: 重构 ...
- Topology Shapes of OpenCascade BRep
Topology Shapes of OpenCascade BRep eryar@163.com 摘要Abstract:通过对OpenCascade中的BRep数据的读写,理解边界表示法的概念及实现 ...
- toString()方法
前面的话 本文将介绍toString()方法,toString()方法返回反映这个对象的字符串 [1]undefined和null没有toString()方法 undefined.toString() ...
- jsp网站服务器配置
Jsp网站部署环境配置 首先解释一下,.jsp网站与.html网站有着很大的不同,html是一种静态网站开发脚本语言,jsp则是在html的基础上专门为开发动态网站设计的语言.所以jsp网站没办法直接 ...
- CLR执行模型
好好学习底层运行机制,从CLR via C# 开始. CLR的执行模型: CLR:Common Language Runtime,是一个可由多种编程语言使用的"运行时".CLR的核 ...
- HTML5移动Web开发(三)——在移动网站中使用HTML5
创建一个简单得HTML5页面ch01e2.html <html> <head> <meta name="viewport" content=" ...
- 轻松自动化---selenium-webdriver(python) (四)
本节要解决的问题: 如何定位一组元素? 场景 从上一节的例子中可以看出,webdriver可以很方便的使用findElement方法来定位某个特定的对象,不过有时候我们却需要定位一组对象, 这时候就需 ...
- 12个学习 CSS3 网站布局设计的优秀案例
网络上有很多的 CSS 画廊站点供大家对各类网站作品进行打分和评论,每天有数以百计的优秀网站被推荐上面,这对于网页设计师来说是很好的灵感来源.今天,我们选择了15赢得 CSS 设计大奖的优秀作品,帮助 ...
- 第20/24周 死锁(Deadlocking)
大家好,欢迎回到性能调优培训.今天讨论SQL Server里的死锁(Deadlocking),第5个月的培训就结束了.当2个查询彼此等待,没有查询可以继续它的工作就会发生死锁.第一步我会概括介绍下SQ ...
- Maven提高篇系列之(三)——使用自己的Repository(Nexus)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...