概述
想必大家在项目中都用过@PostConstruct这个注解把,知道它会在应用启动的时候执行被这个注解标注的方法。其实它还有另外一个注解@PreDestroy,实在Bean销毁前执行,它们都是Bean生命周期的一环,那他们具体在什么阶段执行呢?我们从源码的角度带大家分析下。
注解介绍
@PostConstruct和@PreDestroy是JSR-250规范中定义的类两个类,表示Bean初始化后和销毁前指定的注解,位于javax.annotation包下,而不是spring jar中的类。
JSR-250, Java Specification Requests的缩写,意思是Java 规范提案。它是Java界共同制定的一个重要标准。它定义了一组通用的注解,比如@PostContruct, @Resource等,防止不同的J2EE组件比如Spring、JBoss、WebSphere等都各自实现一套注解。
Spring作为一个NB的框架,它也遵循上面的规范,实现了对JSR注解的支持。
@PostConstruct
@Documented@Retention (RUNTIME)@Target(METHOD)public @interface PostConstruct {}
- 该注解只能作用于方法上,执行依赖注入后执行任何初始化操作。必须在类投入服务之前调用此方法。
- 应用PostConstruct的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。
-
@PreDestroy
@Documented@Retention (RUNTIME)@Target(METHOD)public @interface PreDestroy {}
作用于方法上,在容器销毁Bean的时候回调执行。
- 被注解方法不能有任何参数。
- 应用PreDestroy的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。
实战案例
定义bean ```java @Slf4j @ToString public class LifeCycleBean implements InitializingBean {
private String prop;
public LifeCycleBean() {
log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 实例化");
}
public LifeCycleBean(String prop) {
this.prop = prop;
}
public String getProp() {
return prop;
}
@PostConstructprivate void postContruct() {log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean postContruct");}@PreDestroyprivate void preDestory() {log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean preDestory");}public void setProp(String prop) {this.prop = prop;}public void init() {log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 初始化");this.setProp("hello");}public void destroy() {log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean destroy");this.setProp("hello");}@Overridepublic void afterPropertiesSet() throws Exception {log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean afterPropertiesSet");}
}
```java@Bean(name = "lifeCycleBean", initMethod = "init", destroyMethod = "destroy")public LifeCycleBean createLifeCycleBean() {return new LifeCycleBean();};
定义了Bean初始化和销毁相关的方法,包括实现了InitializingBean接口,Bean配置了initMethod、destroyMethod属性,以及添加了@PostConstruct、@PreDestroy注解。
- 查看执行结果

- 小结
根据执行结果得知bean初始化和销毁的顺序:
@PostContruct注解对应的方法- 实现了
InitializingBean接口的afterPropertiesSet方法 - Bean
init-method属性对应的方法 @PreDestroy注解对应的方法-
源码解析

通过debug快速追踪到实在Bean的初始化阶段。 AbstractAutowireCapableBeanFactory的initializeBean()方法是bean的初始化入口。InitDestroyAnnotationBeanPostProcessorBean处理器中调用invokeInitMethods执行@PostContruct对应的方法。执行过程


在Bean的初始化过程前,会回调BeanPostProcessor的postProcessBeforeInitialization方法,这是Spring的一个扩展点,而我们的@PostConstruct就是通过这种扩展机制实现的,它对应的类是InitDestroyAnnotationBeanPostProcessor。
InitDestroyAnnotationBeanPostProcessor,顾名思义,它是用来处理初始化和销毁注解的一个Bean处理器,我们看下它的postProcessBeforeInitialization方法。
这个方法关键是上面的两步,第一步找出所有注解的方法,第二步执行对应的方法,第二步比较简单,就是调用了反射操作,我们重点关注在第一步findLifecycleMetadata方法。
/*** 查找指定类型的生命周期元数据.*/private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {// lifecycleMetadataCache为空,则重新创建生命周期元数据.if (this.lifecycleMetadataCache == null) {// Happens after deserialization, during destruction...return buildLifecycleMetadata(clazz);}// 采用双重检查锁机制来进行快速检查,尽量减少对锁的使用。// 首先进行快速检查,只需最少的锁竞争.// Quick check on the concurrent map first, with minimal locking.LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {// 加锁处理synchronized (this.lifecycleMetadataCache) {metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {// 关键方法,构建LifecycleMetadatametadata = buildLifecycleMetadata(clazz);this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;}
查看关键的方法buildLifecycleMetadata的源码如下:
/*** 创建生命周期元数据.*/private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {// 判断当前Bean是否包含@PostContruct,@PreDestroy,如果不包含,直接返回if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}// 初始化相关元数据List<LifecycleElement> initMethods = new ArrayList<>();// 销毁相关的元数据List<LifecycleElement> destroyMethods = new ArrayList<>();Class<?> targetClass = clazz;do {final List<LifecycleElement> currInitMethods = new ArrayList<>();final List<LifecycleElement> currDestroyMethods = new ArrayList<>();// 遍历类中的methodsReflectionUtils.doWithLocalMethods(targetClass, method -> {// 判断方法是有@PostConstruct注解if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {LifecycleElement element = new LifecycleElement(method);currInitMethods.add(element);if (logger.isTraceEnabled()) {logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);}}// 判断方法是有@PreDestroy注解if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {currDestroyMethods.add(new LifecycleElement(method));if (logger.isTraceEnabled()) {logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);}}});initMethods.addAll(0, currInitMethods);destroyMethods.addAll(currDestroyMethods);// 查找父类targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));}
该方法主要是遍历bean对应的class以及父class中包含@PostConstruct、@PreDestroy注解的方法,构建出LifecycleMetadata对象。
有一个问题,上面查找其实用的都是initAnnotationType、@PostConstruct、@PreDestroy属性,那他们是在上面时候设置为@PostConstruct、@PreDestroy呢?
我们可以看父类CommonAnnotationBeanPostProcessor的构造方法,它在实例化CommonAnnotationBeanPostProcessor这个bean的时候设置。
public CommonAnnotationBeanPostProcessor() {setOrder(Ordered.LOWEST_PRECEDENCE - 3);// 设置PostConstructsetInitAnnotationType(PostConstruct.class);setDestroyAnnotationType(PreDestroy.class);ignoreResourceType("javax.xml.ws.WebServiceContext");// java.naming module present on JDK 9+?if (jndiPresent) {this.jndiFactory = new SimpleJndiBeanFactory();}}
何时实例化CommonAnnotationBeanPostProcessor
那么CommonAnnotationBeanPostProcessor这个Bean处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。
- 在创建容器的时候,会创建所有的BeanPostProcessors Bean。
- 在创建
CommonAnnotationBeanPostProcessor这个Bean的时候,就会调用对应的构造方法,设置对应的PostConstruct注解。
那又是什么时候把**CommonAnnotationBeanPostProcessor**这个Bean的BeanDefinition加到BeanDefinition工厂中的呢?
只有BeanDefinition工厂中又对应的BeanDefinition才会创建出Bean。答案就是在AnnotationConfigUtils#registerAnnotationConfigProcessors方法中。
总结

上面时Bean初始化完整的过程,其实Bean初始化可以自定义的扩展点很多,大家可以根据实际需要扩展。
参考
https://securitit.blog.csdn.net/article/details/111353318
https://blog.csdn.net/securitit/article/details/111353676
https://www.cnblogs.com/lay2017/p/11735802.html
