《Spring》(八)---- IoC容器及Bean的生命周期
本文内容纲要:《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>
< /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生命周期
- Bean的实例化
首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化。
容器在内部实现的时候,采用“策略模式”来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。默认情况下,容器内部采用CglibSubclassingInstantiationStartegy。容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubclassingInstantiationStartegy以及不同的bean定义类型,就可以返回实例化完成的对象实例。但不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。这个BeanWrapper的实现类BeanWrapperImpl是对某个bean进行包裹,然后对包裹后的bean进行操作,比如设置或获取bean的相应属性值。
- 设置对象属性
BeanWrapper继承了PropertyAccessor接口,可以以同一的方式对对象属性进行访问,同时又继承了PropertyEditorRegistry和TypeConverter接口,然后BeanWrapper就可以很方便地对bean注入属性了。
如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该bean的id,此时该Bean就获得了自己在配置文件中的id。
如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory.
如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext, 这样该Bean就获得了自己所在的ApplicationContext.
如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessBeforeInitialization()方法。
7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法。
如果Bean配置了init-method方法,则会执行init-method配置的方法。
如果有一个Bean实现了BeanPostProcessor接口,并将该接口配置到配置文件中,则会调用该接口的postProcessAfterInitialization方法。
10.经过9之后,就可以正式使用该Bean了,对于scope为singleton的Bean, Spring IoC容器会缓存一份该Bean的实例,而对于scope为prototype的Bean, 每次被调用都回new一个对象,而且生命周期也交给调用方管理了,不再是Spring容器进行管理了。
容器关闭后,如果Bean实现了DisposableBean接口,则会调用该接口的destroy()方法。
如果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 containerlog4j: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相比,有如下几点不同:
BeanFactory容器中,不会调用ApplicationContextAware接口的setApplicationContext()方法
BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法不会自动调用,必须自己通过代码手动注册
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 containerlog4j: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的生命周期
原文链接:https://www.cnblogs.com/IvySue/p/6484599.html
以上是 《Spring》(八)---- IoC容器及Bean的生命周期 的全部内容, 来源链接: utcz.com/z/362442.html