《Spring揭秘》---- IoC容器及Bean的生命周期
Posted coder为
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Spring揭秘》---- IoC容器及Bean的生命周期相关的知识,希望对你有一定的参考价值。
Spring的IoC容器会以某种方式加载配置信息,然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。实现以上功能,分为两个阶段:容器启动阶段和Bean实例化阶段。而且Spring的IoC容器在每个阶段都加入了相应的扩展点,以便根据具体场景的需要加入自定义的扩展逻辑。
1 容器启动阶段
首先会通过某种途径加载配置信息,大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的配置信息进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDifinitionRegistry,这样容器启动工作就完成了。
该阶段所作工作是准备性的,重点更加侧重于对象管理信息的收集,以及一些验证性的和辅助性的工作。
2 Bean实例化阶段
现在所有的bean定义信息都已经注册到了BeanDefinitionRegistry中,当某个请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,就会触发bean实例化。
在这一阶段,容器会先检查所请求的对象之前是否已经初始化,如果没有,会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。
3 干预容器启器
Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制,允许在容器实例化对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。
可以通过两种方式来应用BeanFactoryPostProcessor, 分别针对基本的IoC容器BeanFactory和较为先进的容器ApplicationContext.
对于BeanFactory来说,我们需要用手动方式应用所有的BeanFactoryPostProcessor:
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("...")); PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer(); propertyPostProcessor.setLocation(new ClassPathResource("...")); propertyPostProcessor.postProcessBeanFactory(beanFactory);
对于ApplicationContext来说,它会自动识别配置文件中的BeanFactoryPostProcessor并应用它,所以仅需要在XML中将这些BeanFacotryPostProcessor简单配置一下即可。
<beans> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>conf/jdbc.properties</value> <value>conf/mail.properties</value> </list> </property> </bean> ... </beans>
Spring提供的几个BeanFactoryPostProcessor
- PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer允许在XML配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。以数据源的配置为例:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url"> <value>${jdbc.url}</value> </property> <property name="driverClassName"> <value>${jdbc.driver}</value> </property> <property name="username"> <value>${jdbc.username}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property>
<property name="maxActive">
<value>100</value>
</property>
< /bean>
jdbc.properties文件如下:
jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932& failOverReadOnly=false&roundRobinLoadBalance=true jdbc.driver=com.mysql.jdbc.Driver jdbc.username=your username jdbc.password=your password
原理:
当BeanFactory在第一阶段加载完成所有配置信息时,BeanFactory中保存的对象的属性信息还只是以占位符的形式存在,如${jdbc.url}。当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换相应BeanDefinition中占位符表示的属性值。这样,当进入容器实现的第二个阶段实例化bean时,bean定义中的属性值就是最终替换完成后的了。
PropertyPlaceholderConfigurer不仅会从其配置的properties文件中加载配置项,同时还会检查System类中的Properties. PropertyPlaceholderConfigurer提供了SYSTEM_PROPERTIES_MODE_FALLBACK/SYSTEM_PROPERTIES_MODE_NEVER/SYSTEM_PROPERTIES_MODE_OVERRIDE三种模式,默认采用FALLBACK,即如果properties文件中找不到相应配置项,则到System的properties中查找。
- PropertyOverrideConfigurer
PropertyOverrideConfigurer可以对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。比如前一个例子中的dataSource,maxActive值为100, 如果想把这个值覆盖掉,改成200,就可以在一个properties文件中配置:
dataSource.maxActive=200
所以如果要对容器中某些bean的property信息进行覆盖,需要按照如下规则提供一个PropertyOverrideConfigurer使用的配置文件:
beanName.propertyName=value
也就是说,properties文件中的键是以XML中配置的bean定义的beanName为标志开始的(通常就是id指定的值),后面跟着相应被覆盖的property的名称。如下是PropertyOverridConfigurer在XML中的配置信息:
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> <property name="location" value="pool-adjustment.properties"/> </bean>
当容器中配置的多个PropertyOverrideConfigurer对同一个bean定义的同一个property值进行处理的时候,最后一个会生效。
4 Bean的生命周期
转载自 http://www.jianshu.com/p/3944792a5fff
- ApplicationContext Bean生命周期
1. Bean的实例化
首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化。
容器在内部实现的时候,采用“策略模式”来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。默认情况下,容器内部采用CglibSubclassingInstantiationStartegy。容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubclassingInstantiationStartegy以及不同的bean定义类型,就可以返回实例化完成的对象实例。但不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。这个BeanWrapper的实现类BeanWrapperImpl是对某个bean进行包裹,然后对包裹后的bean进行操作,比如设置或获取bean的相应属性值。
2. 设置对象属性
BeanWrapper继承了PropertyAccessor接口,可以以同一的方式对对象属性进行访问,同时又继承了PropertyEditorRegistry和TypeConverter接口,然后BeanWrapper就可以很方便地对bean注入属性了。
3. 如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该bean的id,此时该Bean就获得了自己在配置文件中的id。
4. 如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory.
5. 如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext, 这样该Bean就获得了自己所在的ApplicationContext.
6. 如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessBeforeInitialization()方法。
7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法。
8. 如果Bean配置了init-method方法,则会执行init-method配置的方法。
9. 如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessAfterInitialization方法。
10.经过9之后,就可以正式使用该Bean了,对于scope为singleton的Bean, Spring IoC容器会缓存一份该Bean的实例,而对于scope为prototype的Bean, 每次被调用都回new一个对象,而且生命周期也交给调用方管理了,不再是Spring容器进行管理了。
11. 容器关闭后,如果Bean实现了DisposableBean接口,则会调用该接口的destroy()方法。
12. 如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean生命周期结束。
示例
我们定义了一个Person类,该类实现了BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean五个接口,并且在applicationContext.xml文件中配置了该Bean的id为person1,并且配置了init-method和destroy-method,为该Bean配置了属性name为jack的值,然后定义了一个MyBeanPostProcessor方法,该方法实现了BeanPostProcessor接口,且在applicationContext.xml文件中配置了该方法的Bean
Person.class
package com.ivy.beanlifecycle; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean{ private String name; public Person() { System.out.println("Person constructor"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("setter() invoked"); } public void myInit() { System.out.println("myInit() invoked"); } public void myDestroy() { System.out.println("myDestroy() invoked"); } @Override public void setBeanName(String beanName) { System.out.println("setBeanName() invoked, beanName : " + beanName); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { // TODO Auto-generated method stub System.out.println("setBeanFactory() invoked, beanFactory : " + beanFactory); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("setApplicationContext() invoked"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet() invoked"); } @Override public void destroy() throws Exception { System.out.println("destroy() invoked"); } public String toString() { return "Person[name=" + name +"]"; } }
MyBeanPostProcessor.class
package com.ivy.beanlifecycle; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor{ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessAfterInitialization() invoked, beanName : " + beanName); return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization() invoked, beanName : " + beanName); return bean; } }
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="person1" class="com.ivy.beanlifecycle.Person" init-method="myInit" destroy-method="myDestroy"> <property name="name" value="ivy"></property> </bean> <bean id="myPostProcessor" class="com.ivy.beanlifecycle.MyBeanPostProcessor"></bean> </beans>
PersonServiceApplicationContextTest.class
package com.ivy.beanlifecycle; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceApplicationContextTest{ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("start init ioc container"); ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("end loading xml"); Person person = (Person)aContext.getBean("person1"); System.out.println(person); System.out.println("close container"); ((ClassPathXmlApplicationContext)aContext).close(); } }
运行结果:
start init ioc container log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. Person constructor setter() invoked setBeanName() invoked, beanName : person1 setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@5fa12d: defining beans [person1,myPostProcessor]; root of factory hierarchy setApplicationContext() invoked postProcessBeforeInitialization() invoked, beanName : person1 afterPropertiesSet() invoked myInit() invoked postProcessAfterInitialization() invoked, beanName : person1 end loading xml Person[name=ivy] close container destroy() invoked myDestroy() invoked
可以看出,在加载xml的时候ApplicationContext就实例化了所有的bean
- BeanFactory Bean生命周期
BeanFactoty容器中, Bean的生命周期如上图所示,与ApplicationContext相比,有如下几点不同:
1. BeanFactory容器中,不会调用ApplicationContextAware接口的setApplicationContext()方法
2. BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法不会自动调用,必须自己通过代码手动注册
3. BeanFactory容器启动的时候,不会去实例化所有bean,包括所有scope为singleton且非延迟加载的bean也是一样,而是在调用的时候去实例化。
还是以上边Person为示例
PersonServiceBeanFactoryTest.class
package com.ivy.beanlifecycle; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class PersonServiceBeanFactoryTest { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("start init ioc container"); ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); System.out.println("end loading xml"); beanFactory.addBeanPostProcessor(new MyBeanPostProcessor()); Person person = (Person)beanFactory.getBean("person1"); System.out.println(person); System.out.println("close container"); beanFactory.destroySingletons(); } }
运行结果:
start init ioc container log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. end loading xml Person constructor setter() invoked setBeanName() invoked, beanName : person1 setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.xml.XmlBeanFactory@ccd65d: defining beans [person1,myPostProcessor]; root of factory hierarchy postProcessBeforeInitialization() invoked, beanName : person1 afterPropertiesSet() invoked myInit() invoked postProcessAfterInitialization() invoked, beanName : person1 Person[name=ivy] close container destroy() invoked
可以看出,end loading xml之后才实例化的person。
以上是关于《Spring揭秘》---- IoC容器及Bean的生命周期的主要内容,如果未能解决你的问题,请参考以下文章
sping揭秘4某些无法注册到IOC容器的对象如何交给spring托管
死磕 Spring----- IOC 之开启 bean 的加载