一、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生命周期的更多相关文章

  1. 【Spring Framework】Spring IOC详解及Bean生命周期详细过程

    Spring IOC 首先,在此之前,我们就必须先知道什么是ioc,ioc叫做控制反转,也可以称为依赖注入(DI),实际上依赖注入是ioc的另一种说法, 1.谁控制谁?: 在以前,对象的创建和销毁都是 ...

  2. spring源码分析系列5:ApplicationContext的初始化与Bean生命周期

    回顾Bean与BeanDefinition的关系. BeanFactory容器. ApplicationContext上下文. 首先总结下: 开发人员定义Bean信息:分为XML形式定义:注解式定义 ...

  3. Spring BeanFactory 初始化 和 Bean 生命周期

    (version:spring-context-4.3.15.RELEASE) AbstractApplicationContext#refresh() public void refresh() t ...

  4. Spring Framework核心概念之Bean生命周期管理

    目录 Spring Bean的生命周期 相关接口的分类 测试SpringBean生命周期的Demo程序 小结 Spring Bean的生命周期 Spring容器既Application或者WebApp ...

  5. Spring源码分析:Spring IOC容器初始化

    概述: Spring 对于Java 开发来说,以及算得上非常基础并且核心的框架了,在有一定开发经验后,阅读源码能更好的提高我们的编码能力并且让我们对其更加理解.俗话说知己知彼,百战不殆.当你对Spri ...

  6. SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用

    目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...

  7. 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean

    让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...

  8. 整理在Spring IOC容器初始化后可以处理特定逻辑的多种实现方式

    Spring框架的核心是依赖注入.切面:Spring Boot是在Spring框架的基础上为其提供许多默认配置.默认约定(约定优于配置),从而达到减少或减化配置进而可开箱即用.快速上手:Spring ...

  9. Spring Boot 启动源码解析结合Spring Bean生命周期分析

    转载请注明出处: 1.SpringBoot 源码执行流程图 2. 创建SpringApplication 应用,在构造函数中推断启动应用类型,并进行spring boot自动装配 public sta ...

随机推荐

  1. HashSet源码详解

    序言 在写了HashMap文章后,隔了几天才继续这一系列的文章,因为要学的东西实在是太多了,写一篇要花费的时间很多,所以导致隔了几天才来写.不过希望自己坚持下去.终有一天会拨开云雾见青天的.学Hash ...

  2. 【WP开发】实现“摇一摇”功能

    尽管我的微信是每八个月登录一次,但我相信各位玩得比我多.微信有一个“摇一摇”功能,这个功能其实是利用了加速度传感器来实现的,这个传感器,我估计再低端的手机都会有的,这是严重基本的传感器. 重力加速度既 ...

  3. WPF/Silverlight 下的图片局部放大

    最近的项目中也要用到一个局部图片放大的功能,园子里面一搜,发现(菩提下的杨过)杨大侠已经实现了. 请参见这里:http://www.cnblogs.com/yjmyzz/archive/2009/12 ...

  4. Cocos2d-x 3.2 学习笔记(十三)CocoStudio UI编辑器 by 保卫萝卜

    关于编辑器部分研究的不多,但基本能使用.最近时间不是很多,因此写blog的次数越来越少了.自从玩了<保卫萝卜>时候一直想要写一下,同时练下手感.基本的结构已经写的差不多了,主要完善写UI和 ...

  5. Android中的内存储、外存储概念、文件操作与PC端的有些不同

    其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理.初学者在这部分感到很容易混 ...

  6. 安装android studio报错Failed to install Intel HAXM.

    在安装android studio的过程中,安装到android的模拟器加速器(intel HAXM)这一步时,报错: HAXM是用来管理硬件加速的,估计是用了这个东西模拟器就能告别Eclipse的龟 ...

  7. zmNgFrameWork 架构升级ng1.5和md5静态资源缓存方案【angular1.x】

    前言: 在我前面的博客,angular项目总结——angular + browserify + gulp + bower + less 架构分享  把我开发angular的架构进行了分享,并上传到了g ...

  8. Dagger2 生成代码学习

    接上一篇文章介绍了Dagger2的初步使用,相信刚接触的人会觉得很奇怪,怎么会有很多自己没有定义的代码出现,为什么Component的创建方式是那样的.为了搞清楚这些东西,我们需要查看一下Dagger ...

  9. Httpd运维日志:通过apxs添加模块

    Brief 在部署Httpd时为方便管理和安全等原因,我们仅会安装所需的模块,那么后期功能扩展时则需要通过Httpd内置提供的apxs程序来进行模块添加. 而apxs程序则位于apache/bin目录 ...

  10. 使用Python将Excel中的数据导入到MySQL

    使用Python将Excel中的数据导入到MySQL 工具 Python 2.7 xlrd MySQLdb 安装 Python 对于不同的系统安装方式不同,Windows平台有exe安装包,Ubunt ...