Bean的生命周期,尤其是Bean从实例化到可以使用,过程较为复杂,所以建议自己coding一遍来加深理解。
该周期中涵盖了Spring Aware接口,Spring PostProcessor后置处理器等等,首次接触时可能会觉得晦涩难懂,结合应用场景更有助于理解。
生命周期
Spring容器将对其所管理的对象全部给予统一的生命周期管理,这些被管理的对象完全摆脱了原来那种“new完后被使用,脱离作用域后即被回收”的命运。
Q: Bean在什么时候会被创建?
A: 容器启动之后,并不会马上就实例化相应的bean定义。容器现在仅仅拥有所有对象的BeanDefinition来保存实例化阶段将要用的必要信息。只有当请求方通过BeanFactory的getBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动。BeanFactory的getBean()方法可以被客户端对象显式调用,也可以在容器内部隐式地被调用。隐式调用有如下两种情况:
- BeanFactory来说,对象实例化默认采用延迟初始化。通常情况下,当对象A被请求而需要第一次实例化的时候,如果它所依赖的对象B之前同样没有被实例化,那么容器会先实例化对象A所依赖的对象。这时容器内部就会首先实例化对象B,以及对象 A依赖的其他还没有实例化的对象。这种情况是容器内部调用getBean(),对于本次请求的请求方是隐式的。
- ApplicationContext启动之后会实例化所有单例bean定义。但ApplicationContext在实现的过程中依然遵循Spring容器实现流程的两个阶段,只不过它会在启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean定义的实例化方法getBean()。这就是为什么当你得到ApplicationContext类型的容器引用时,容器内所有对象已经被全部实例化完成。
之所以说getBean()方法是有可能触发Bean实例化阶段的活动,是因为只有当对应某个bean定义的getBean()方法第一次被调用时,不管是显式的还是隐式的,Bean实例化阶段的活动才会被触发,第二次被调用则会直接返回容器缓存的第一次实例化完的对象实例(prototype类型bean除外)。当getBean()方法内部发现该bean定义之前还没有被实例化之后,会通过createBean()方法来进行具体的对象实例化.
Bean Scope
scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁Spring 的 IoC 容器这些对象。
如果你不指定bean的scope,singleton便是容器默认的scope.
- singleton: 在Spring的IoC容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。
标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例;而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。
- propertype: 针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁。
对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己对应的一个对象实例。通常,声明为prototype的scope的bean定义类型,都是一些有状态的,比如保存每个顾客信息的对象。
另外三种scope类型,即request、session和global session类型,只有在支持Web应用的ApplicationContext中才能使用这三个scope。
- request: 为每一个HTTP请求创建一个全新的bean,当请求结束之后,该对象的生命周期即告结束。
- session: 为每一个独立的session创建一个全新的bean对象,session结束之后,该bean对象的生命周期即告结束。
- global session: global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的 session。如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session
类型的scope对待。
Q: Spring中的单例模式是否时线程安全的?
A: 关于单例bean的多线程行为,Spring框架没有做任何事情。开发人员有责任处理单例bean的并发问题和线程安全问题。 实际上,大多数Spring bean都没有可变状态,因此非常简单。但是如果你的bean具有可变状态,那么你需要确保线程安全。解决此问题最简单明了的方法是将可变bean的bean范围从“singleton”更改为“prototype".
Spring Aware接口
org.springframework.beans.factory.Aware
使得自定义Bean可以识别利用Spring容器的资源,比如,
BeanNameAware.setBeanName(String)
,可以在Bean中得到它在IOC容器中的Bean的实例的名字。BeanFactoryAware.setBeanFactory(BeanFactory)
,可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务。ApplicationContextAware.setApplicationContext(ApplicationContext)
,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用上下文的服务。- MessageSourceAware,在Bean中可以得到消息源。
- ApplicationEventPublisherAware,在bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件。
- ResourceLoaderAware,在Bean中可以得到ResourceLoader,从而在bean中使用ResourceLoader加载外部对应的Resource资源。
如非必要,Spring官方不推荐自定义Bean实现Aware接口,这会增加代码与Spring 框架的耦合性。
Spring PostProcessor
- BeanFactoryPostProcessor可以对bean的配置信息进行操作。结合Bean的生命周期,Spring IOC容器允许BeanFactoryPostProcessor读取配置信息并且能够在容器实例化任何其他bean之前改变配置信息。
- BeanPostProcessor可以在Spring完成Bean实例化,配置,初始化之后实现自己的业务逻辑。
BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段
Spring Aware和PostProcessor都是提供了用户利用Spring IoC添加业务逻辑进一步定制Bean。
实例
public class MyBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanClassLoaderAware,
InitializingBean, DisposableBean {
private String name;
private String description;
public MyBean() {
System.out.println("**MyBean** construct.");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("**MyBean** BeanClassLoaderAware.setBeanClassLoader: " + classLoader.getClass());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("**MyBean** BeanFactoryAware.setBeanFactory: " + beanFactory.getClass());
}
@Override
public void setBeanName(String s) {
System.out.println("**MyBean** BeanNameAware.getBeanName: " + s);
}
@Override
public void destroy() throws Exception {
System.out.println("**MyBean** DisposableBean.destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("**MyBean** InitializingBean.afterPropertiesSet");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("**MyBean** ApplicationContextAware.setApplicationContext");
}
@Override
protected void finalize() throws Throwable {
System.out.println("**MyBean** MyBean finalized.");
}
@PostConstruct
public void springPostConstruct() {
System.out.println("**MyBean** @PostConstruct");
}
// xml文件中的init-method
public void myInitMethod() {
System.out.println("**MyBean** init-method");
}
@PreDestroy
public void springPreDestroy() {
System.out.println("**MyBean** @PreDestroy");
}
// xml文件中的destroy-method
public void mydestroyMethod() {
System.out.println("**MyBean** destory-method");
}
@Autowired
public void setName(String name) {
this.name = name;
System.out.println("**MyBean** setName");
}
@Autowired
public void setDescription(String description) {
this.description = description;
System.out.println("**MyBean** setDescription");
}
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
super();
System.out.println("[MyBeanFactoryPostProcessor] constructor");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("[MyBeanFactoryPostProcessor] postProcessBeanFactory");
}
}
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
super();
System.out.println("[MyBeanPostProcessor] constructor.");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MyBeanPostProcessor] postProcessBeforeInitialization: " + bean.getClass() + ": " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MyBeanPostProcessor] postProcessAfterInitialization: " + bean.getClass() + ": " + beanName);
return bean;
}
}
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
public MyInstantiationAwareBeanPostProcessor() {
super();
System.out.println("[MyInstantiationAwareBeanPostProcessor] constructor.");
}
// 接口方法、实例化Bean之前调用
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[MyInstantiationAwareBeanPostProcessor] postProcessBeforeInstantiation");
return null;
}
// 接口方法、实例化Bean之前调用
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("[MyInstantiationAwareBeanPostProcessor] postProcessAfterInstantiation");
return true;
}
// 接口方法、设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("[MyInstantiationAwareBeanPostProcessor] postProcessPropertyValues");
// 注意返回结果,否则会无法正确Bean的属性
return pvs;
}
}
public class LifeCycleApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("lifecycle.xml");
System.out.println("[Application] before get bean");
MyBean bean = (MyBean) context.getBean("myBean");
System.out.println("[Application] after get bean");
System.out.println(bean);
((ClassPathXmlApplicationContext) context).registerShutdownHook();
}
}
lifecycle.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.LifeCycle"/>
<bean id="myBean" class="com.example.LifeCycle.MyBean" init-method="myInitMethod"
destroy-method="mydestroyMethod">
</bean>
<bean class="com.example.LifeCycle.MyBeanPostProcessor"/>
<bean class="com.example.LifeCycle.MyBeanFactoryPostProcessor"/>
<bean class="com.example.LifeCycle.MyInstantiationAwareBeanPostProcessor"/>
</beans>
运行结果:
...
[MyBeanFactoryPostProcessor] constructor
...
MyBeanFactoryPostProcessor] postProcessBeanFactory
...
[MyBeanPostProcessor] constructor
...
[MyInstantiationAwareBeanPostProcessor] constructor
[MyInstantiationAwareBeanPostProcessor] constructor
...
16:18:06.149 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'myBean'
**MyBean** construct.
16:18:06.154 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Found init method on class [com.example.LifeCycle.MyBean]: public void com.example.LifeCycle.MyBean.springPostConstruct()
16:18:06.154 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Found destroy method on class [com.example.LifeCycle.MyBean]: public void com.example.LifeCycle.MyBean.springPreDestroy()
16:18:06.154 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Registered init method on class [com.example.LifeCycle.MyBean]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@21a44a0a
16:18:06.154 [main] DEBUG org.springframework.context.annotation.CommonAnnotationBeanPostProcessor - Registered destroy method on class [com.example.LifeCycle.MyBean]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@5caf49c4
16:18:06.176 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Registered injected element on class [com.example.LifeCycle.MyBean]: AutowiredMethodElement for public void com.example.LifeCycle.MyBean.setDescription(java.lang.String)
16:18:06.176 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Registered injected element on class [com.example.LifeCycle.MyBean]: AutowiredMethodElement for public void com.example.LifeCycle.MyBean.setName(java.lang.String)
...
[MyInstantiationAwareBeanPostProcessor] postProcessAfterInstantiation
[MyInstantiationAwareBeanPostProcessor] postProcessPropertyValues
...
[MyInstantiationAwareBeanPostProcessor] postProcessAfterInstantiation
[MyInstantiationAwareBeanPostProcessor] postProcessPropertyValues
16:18:06.177 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected element of bean 'myBean': AutowiredMethodElement for public void com.example.LifeCycle.MyBean.setDescription(java.lang.String)
16:18:06.177 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected element of bean 'myBean': AutowiredMethodElement for public void com.example.LifeCycle.MyBean.setName(java.lang.String)
*MyBean** setName
**MyBean** setDescription
**MyBean** BeanNameAware.getBeanName: myBean
**MyBean** BeanClassLoaderAware.setBeanClassLoader: class sun.misc.Launcher$AppClassLoader
**MyBean** BeanFactoryAware.setBeanFactory: class org.springframework.beans.factory.support.DefaultListableBeanFactory
**MyBean** ApplicationContextAware.setApplicationContext
[MyBeanPostProcessor] postProcessBeforeInitialization: class com.example.LifeCycle.MyBean: myBean
...
**MyBean** init-method
...
MyBean{name='Gigi', description='description'}
...
**MyBean** @PreDestroy
**MyBean** DisposableBean.destroy
**MyBean** destory-method
一些建议
不建议使用InitializingBean, DisposableBean,因为这个会增加代码与Spring框架的耦合性。
@PostConstruct,@PreDestroy是JavaX的标准,而非Java,Spring定义的注解,使用时应该注意。
将Bean从Spring IoC中移除之前需要释放持有的资源,建议在destroy-method中写释放资源的代码。
应用
了解Bean的生命周期有利于我们根据业务需要对Bean进行相关的拓展工作。
举个例子,在Bean初始化前希望用户名和密码。倘若将这些信息硬编码到工厂代码中显然是不安全的,一般地,公司会有一个统一的密码存储服务,通过调用开放的API获取用户名和密码。此时,我们可以在Bean的init-method中加入这样的逻辑:调用公司的密码存储服务API获取用户名和密码,加载至Bean的相关字段中。
再举个例子,在Bean被销毁之前,释放Bean与数据库的连接,那么我们可以把释放数据库连接这段逻辑放到destroy-method中。
Ref: