从启动日志看Spring IOC的初始化和Bean生命周期
来源:互联网 发布:淘宝箱包排名 编辑:程序博客网 时间:2024/06/08 08:40
一、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, Rlog4j.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
-------------------------------------------------------------------------------------------------------
七、覆盖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的IOC、Spring对象初始化bean时机、Spring容器生命周期
- Spring的IOC、Spring对象初始化bean时机、Spring容器生命周期
- 从源码看Spring bean 生命周期
- 【Spring】Spring常用配置-Bean的初始化和销毁(生命周期)
- Spring IOC中bean的生命周期
- Spring IOC -bean对象的生命周期详解
- Spring IOC容器中Bean的生命周期
- [Spring]Bean在IOC容器的生命周期
- Spring IOC容器中Bean的生命周期
- Spring IOC -bean对象的生命周期详解
- Spring IOC -bean对象的生命周期详解
- Spring IOC -bean对象的生命周期详解
- Spring框架, bean的生命周期中,初始化和销毁.
- Spring中的IOC(三):bean 的其他属性及bean实例的生命周期和范围
- spring 8 bean配置--IOC容器中bean的生命周期
- spring bean初始化的完整生命周期
- spring-IOC容器bean生命周期
- 'inline' is the only legal storage class for constructors
- Chart图表
- Android面试之Activity生命周期
- HX711简介
- 序列化
- 从启动日志看Spring IOC的初始化和Bean生命周期
- 上传视频
- LeetCodeOJ——5.Longest Palindromic Substring
- Get与Post的区别
- 滑动窗口的最大值
- Python的拷贝
- REST API 并发控制
- C:LINUX如何来使用传入的参数
- 135. candy 贪心算法