从启动日志看Spring IOC的初始化和Bean生命周期

来源:互联网 发布:淘宝箱包排名 编辑:程序博客网 时间:2024/06/08 08:40

 http://www.cnblogs.com/gnidoc/p/4978074.html

一、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,打印的日志:

0 0
原创粉丝点击