前言
Spring容器帮我们维护Bean的生命周期,通过DI维护对象之间的关联,实现组件之间的松耦合。程序与框架之间的“协议”,是我们可以充分利用框架提供的功能,实现场景化需求的关键结合点。
对于Bean的创建,我们需要理解的是,一个bean在spring容器中的生命周期,Spring在bean生命周期提供给我们什么样的接口,或者说不同回调时机可供我们使用。
一
bean的XML定义:
<bean id="bean1" class="com.test.bean.Bean1" init-method="appInit" destroy-method="appDestroy"></bean>
Bean1.java
public class Bean1 implements BeanNameAware, BeanFactoryAware,ApplicationContextAware,BeanPostProcessor, InitializingBean, DisposableBean {
public void appInit() {
System.out.println("Bean1: appInit()");
}
public void announce() {
System.out.println("in executing bean1 method");
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean1: postProcessBeforeInitialization(): " + beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean1: postProcessAfterInitialization(): " + beanName);
return bean;
}
public void afterPropertiesSet() throws Exception {
System.out.println("Bean1: afterPropertiesSet()");
}
public void destroy() throws Exception {
System.out.println("Bean1: destroy()");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("Bean1: setBeanFactory(): " + beanFactory.toString());
}
public void setBeanName(String s) {
System.out.println("Bean1: setBeanName(): " + s);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("Bean1: setApplicationContext(): " + applicationContext.toString());
}
public void appDestroy() {
System.out.println("Bean1: appDestroy()");
}
}
创建Spring ApplicationContext
public class TestEntry {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-conf.xml");
Bean1 bean1 = context.getBean(Bean1.class);
bean1.announce();
context.close();
}
}
输出如下:
根据《Spring In Action,4th》中,我们归纳bean的生命周期为以下:
- Spring instantiates the bean.
Spring实例化 bean对象; - Spring injects values and bean references into the bean’s properties
Spring处理相关值的注入,以及引用成员的注入; - If the bean implements BeanNameAware, Spring passes the bean’s ID to the set-
BeanName() method
如果bean实现了BeanNameAware接口,则调用 setBeanName 方法; - If the bean implements BeanFactoryAware, Spring calls the setBeanFactory()
method, passing in the bean factory itself
如果bean实现了BeanFactoryAware接口,则调用setBeanFactory 方法; - If the bean implements ApplicationContextAware, Spring calls the set-
ApplicationContext() method, passing in a reference to the enclosing appli-
cation context
如果bean实现了 ApplicationContextAware 接口,则调用setApplicationContext
方法; - If the bean implements the BeanPostProcessor interface, Spring calls its post-
ProcessBeforeInitialization() method.
如果bean实现了BeanPostProcessor接口,则调用 postProcessBeforeInitialization - If the bean implements the InitializingBean interface, Spring calls its after-
PropertiesSet() method
如果bean实现了InitializingBean接口,则调用 afterPropertiesSet 方法 - if the bean was declared with an init-method, then the specified initialization method is called
如果bean定义了init-method,则调用bean的初始化方法(这里为appInit) - If the bean implements BeanPostProcessor, Spring calls its postProcess-
AfterInitialization() method
如果bean实现了BeanPostProcessor接口,则调用postProcessAfterInitialization方法 - At this point, the bean is ready to be used by the application and remains in the application context until the application context is destroyed
至此,bean已经可以在程序中使用,直到应用上下文关闭 - If the bean implements the DisposableBean interface, Spring calls its destroy() method
如果实现了 DisposableBean接口,则调用 destroy方法 - if the bean was declared with a destroy-method, the specified method is called
如果声明了destroy-method,则调用该方法(这里为appDestroy)
总结一下,
第一类,是框架通知程序的一些接口,即***Aware接口的;
第二类,Spring初始化bean的不同时机阶段
有个地方好像不对?!
我们的示例代码里,bean实现了BeanPostProcessor接口,但是并没有出现 6 和 9的相关调用日志。按照《Spring In Action》的相关描述,
按照字面意思理解,如果一个bean实现了BeanPostProcessor接口,Spring回调用相关的方法的。。但是。。。哪里出了问题?
让我们再添加一个bean
public class Bean3 implements
BeanPostProcessor, InitializingBean, DisposableBean {
public void init() {
System.out.println("Bean3: init method");
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean3: postProcessBeforeInitialization: " + beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean3: postProcessAfterInitialization: " + beanName);
return bean;
}
public void afterPropertiesSet() throws Exception {
System.out.println("Bean3: afterPropertiesSet: ");
}
public void destroy() throws Exception {
System.out.println("Bean3: destroy: ");
}
}
声明一下:
<bean class="com.test.bean.Bean3" init-method="init"></bean>
来看运行情况:
bean1先创建,bean3接着创建,还是没有出现期望的 BeanPostProcessor 两个回调。
What the FXCK happened?
以上创建的两个bean,都是实现了 BeanPostProcessor 接口的,我们再加一个未实现BeanPostProcessor接口的bean看看。。
public class Bean2 implements InitializingBean, DisposableBean {
public void init() {
System.out.println("Bean2: init method");
}
public void destroy() throws Exception {
System.out.println("Bean2:destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("Bean2:afterPropertiesSet");
}
}
声明一下,
<bean class="com.test.bean.Bean2" init-method="init"></bean>
看下运行日志,注意红框的部分!
实现了BeanPostProcessor接口的bean1和bean3,看到了我们期望中的回调日志!!
同时注意一下,回调时传入的Bean是谁,是Bean2!!!
总结:这是怎么回事?
首先,从实例运行情况看:
1. 如果所有的bean都实现了 BeanPostProcessor 接口,没有回调;
2. 如果还有其他的bean,比如实例中的bean2,其未实现 BeanPostProcessor 接口,那么Spring会回调所有实现了 BeanPostProcessor 接口的bean的相应方法,而且,传入的bean参数就是bean2;
从结果来看,画个简图总结下,
Bean1和Bean3实现了BeanPostProcessor接口,Bean2未实现该接口,Bean2在创建过程中,Spring回调了所有实现了 BeanPostProcessor 接口的Bean的方法,传入Bean2!!!
所以,BeanPostProcessor接口机制的使用场景:
如果一个Bean的创建(Bean2),想要通知到与其相关的其他Bean(如Bean1和Bean3),也就是让想知道这个事的Bean知道,那么就让这些Bean(Bean1和Bean3)注册BeanPostProcessor,也就是实现 BeanPostProcessor接口。这样,Bean2创建的时候,想知道的人都会收到来自Spring管家的回调通知啦。