概述
想必大家在项目中都用过@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
@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
注解。
- 查看执行结果
- 小结
根据执行结果得知bean初始化和销毁的顺序:
@PostContruct
注解对应的方法- 实现了
InitializingBean
接口的afterPropertiesSet
方法 - Bean
init-method
属性对应的方法 @PreDestroy
注解对应的方法- Bean
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<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<>(); // 遍历类中的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
那么CommonAnnotationBeanPostProcessor
这个Bean处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。
- 在创建容器的时候,会创建所有的BeanPostProcessors Bean。
- 在创建
CommonAnnotationBeanPostProcessor
这个Bean的时候,就会调用对应的构造方法,设置对应的PostConstruct
注解。
大家可以看下这篇文章了解BeanPostProcessor的详细过程SpringBoot扩展点——一文掌握BeanPostProcessor家族
那又是什么时候把CommonAnnotationBeanPostProcessor
这个Bean的BeanDefinition加到BeanDefinition工厂中的呢?
只有BeanDefinition工厂中又对应的BeanDefinition
才会创建出Bean。答案就是在AnnotationConfigUtils#registerAnnotationConfigProcessors
方法中。
总结
上面时Bean初始化完整的过程,其实Bean初始化可以自定义的扩展点很多,大家可以根据实际需要扩展。