想必大家在项目中都用过@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注解的支持。
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
@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;
}
@PostConstruct
private void postContruct() {
log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean postContruct");
}
@PreDestroy
private 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");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean afterPropertiesSet");
}
}
@Bean(name = "lifeCycleBean", initMethod = "init", destroyMethod = "destroy")
public LifeCycleBean createLifeCycleBean() {
return new LifeCycleBean();
};
定义了Bean初始化和销毁相关的方法,包括实现了InitializingBean
接口,Bean配置了initMethod
、destroyMethod
属性,以及添加了@PostConstruct
、@PreDestroy
注解。
代码地址:github.com/alvinlkk/sp…
根据执行结果得知bean初始化和销毁的顺序:
@PostContruct
注解对应的方法InitializingBean
接口的afterPropertiesSet
方法init-method
属性对应的方法@PreDestroy
注解对应的方法destroy-method
属性对应的方法通过debug快速追踪到实在Bean的初始化阶段。
AbstractAutowireCapableBeanFactory
的initializeBean()
方法是bean的初始化入口。InitDestroyAnnotationBeanPostProcessor
Bean处理器中调用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) {
// 关键方法,构建LifecycleMetadata
metadata = 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 initMethods = new ArrayList<>();
// 销毁相关的元数据
List destroyMethods = new ArrayList<>();
Class> targetClass = clazz;
do {
final List currInitMethods = new ArrayList<>();
final List currDestroyMethods = new ArrayList<>();
// 遍历类中的methods
ReflectionUtils.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);
// 设置PostConstruct
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
// java.naming module present on JDK 9+?
if (jndiPresent) {
this.jndiFactory = new SimpleJndiBeanFactory();
}
}
复制代码
那么CommonAnnotationBeanPostProcessor
这个Bean处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。
CommonAnnotationBeanPostProcessor
这个Bean的时候,就会调用对应的构造方法,设置对应的PostConstruct
注解。大家可以看下这篇文章了解BeanPostProcessor的详细过程SpringBoot扩展点——一文掌握BeanPostProcessor家族
那又是什么时候把CommonAnnotationBeanPostProcessor
这个Bean的BeanDefinition加到BeanDefinition工厂中的呢?
只有BeanDefinition工厂中又对应的BeanDefinition
才会创建出Bean。答案就是在AnnotationConfigUtils#registerAnnotationConfigProcessors
方法中。
上面时Bean初始化完整的过程,其实Bean初始化可以自定义的扩展点很多,大家可以根据实际需要扩展。
作者:JAVA旭阳
链接:https://juejin.cn/post/7111258070352658446
“做程序员,圈子和学习最重要”因为有有了圈子可以让你少走弯路,扩宽人脉,扩展思路,学习他人的一些经验及学习方法!同时在这分享一下是一直以来整理的Java后端进阶笔记文档和学习资料免费分享给大家!需要资料的朋友私信我扣【1】