前言
理清Spring容器管理Bean的过程有助于我们更好地根据需求制定更合理的设计方案,提升系统扩展性和执行效率。
Bean生命周期
下面通过简单的示例来理清Bean在Spring容器中的生命周期。
我们从整体到细节,首先是启动容器加载Bean,这是一个整体的过程。
/**
* 测试 Spring Bean 生命周期
* @author zyj
*/
public class LifeCycleTest {
@Test
public void testLifeCycle() {
// 通过注解开启容器,并加载Bean
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(GlobalConfig.class);
// 关闭容器
context.close();
}
}
@Configuration
public class GlobalConfig {
@Bean
public Calculator calculator() {
return new Calculator();
}
/**
* @Order:加载到容器的优先级,越小代表优先级越高
* @return
*/
@Order(0)
@Bean
public FullyBeanPostProcessor fullyBeanPostProcessor() {
return new FullyBeanPostProcessor();
}
}
接着看看容器在加载Bean的时候都做了哪些处理。
我们需要对Bean加入Spring提供的接口,这些接口可以看做是给开发者干预加载Bean的过程的入口。
public class Calculator implements BeanNameAware, ApplicationContextAware {
public Calculator() {
System.out.println("<<<构造器执行>>>");
}
@Override
public void setBeanName(String name) {
System.out.println("<<<BeanNameAware.setBeanName执行>>>");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("<<<ApplicationContextAware.setApplicationContext执行>>>");
}
@PostConstruct
public void init() {
System.out.println("<<<@PostConstruct执行>>>");
}
@PreDestroy
public void destroy() {
System.out.println("<<<@PreDestroy执行>>>");
}
}
从代码可以看到这里有以下几个接口以及注解:
BeanNameAware
:获取当前Bean在容器中的名字ApplicationContextAware
:为当前Bean注入容器@PostConstruct
:初始化方法(相当于Spring配置文件中的init-method、接口InitializingBean
)@PreDestroy
:销毁方法(相当于Spring配置文件中的destroy-method、接口DisposableBean
)
执行顺序
三月 06, 2020 3:09:52 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f47d241: startup date [Fri Mar 06 15:09:52 CST 2020]; root of context hierarchy
三月 06, 2020 3:09:52 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'globalConfig' of type [class com.zyj.spring.config.GlobalConfig$$EnhancerBySpringCGLIB$$5aa1013d] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
<<<构造器执行>>>
<<<BeanNameAware.setBeanName执行>>>
<<<ApplicationContextAware.setApplicationContext执行>>>
<<<BeanPostProcessor.postProcessBeforeInitialization执行>>>
<<<@PostConstruct执行>>>
<<<BeanPostProcessor.postProcessAfterInitialization执行>>>
三月 06, 2020 3:09:52 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f47d241: startup date [Fri Mar 06 15:09:52 CST 2020]; root of context hierarchy
<<<@PreDestroy执行>>>
Process finished with exit code 0
细心的小伙伴肯定注意到了BeanPostProcessor
的执行,我在Calculator
中没有实现该接口,为什么加载的时候会打印呢?
一开始我理所应当地认为应该由每个Bean来实现BeanPostProcessor
,这样就可以在@PostConstruct
执行前后进行处理了。
但是我错了,验证的结果就是在加载当前Bean的时候并没有执行它所实现的BeanPostProcessor
。
在找了一些资料之后,才了解BeanPostProcessor
的用法。
请看上面的配置类,你可能漏掉了GlobalConfig
中的FullyBeanPostProcessor
。
扫描二维码关注公众号,回复:
10219771 查看本文章
public class FullyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Calculator) {
System.out.println("<<<BeanPostProcessor.postProcessBeforeInitialization执行>>>");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Calculator) {
System.out.println("<<<BeanPostProcessor.postProcessAfterInitialization执行>>>");
}
return bean;
}
}
BeanPostProcessor
由单独一个类来执行,统一由这个类来判断对哪些Bean执行哪些处理。
应用场景
ApplicationContextAware
作用:通过它,可以获取到Spring容器
应用场景:当实例并没有交给Spring管理时,我们就不能使用自动注入了。比如Utils使用Dao,这个时候就可以通过一个持有Spring容器的组件来获取Dao。
@Component
public class SpringJobBeanFactory implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringJobBeanFactory.applicationContext=applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
if (applicationContext == null){
return null;
}
return (T)applicationContext.getBean(name);
}
}
使用:
TypeDao typeDao = SpringJobBeanFactory.getBean("typeDao");