概述

想必大家在项目中都用过@PostConstruct这个注解把,知道它会在应用启动的时候执行被这个注解标注的方法。其实它还有另外一个注解@PreDestroy,实在Bean销毁前执行,它们都是Bean生命周期的一环,那他们具体在什么阶段执行呢?我们从源码的角度带大家分析下。

注解介绍

@PostConstruct@PreDestroyJSR-250规范中定义的类两个类,表示Bean初始化后和销毁前指定的注解,位于javax.annotation包下,而不是spring jar中的类。

JSR-250, Java Specification Requests的缩写,意思是Java 规范提案。它是Java界共同制定的一个重要标准。它定义了一组通用的注解,比如@PostContruct, @Resource等,防止不同的J2EE组件比如Spring、JBoss、WebSphere等都各自实现一套注解。

Spring作为一个NB的框架,它也遵循上面的规范,实现了对JSR注解的支持。

@PostConstruct

  1. @Documented
  2. @Retention (RUNTIME)
  3. @Target(METHOD)
  4. public @interface PostConstruct {
  5. }
  • 该注解只能作用于方法上,执行依赖注入后执行任何初始化操作。必须在类投入服务之前调用此方法。
  • 应用PostConstruct的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。
  • 被注解方法不能有任何参数。

    @PreDestroy

    1. @Documented
    2. @Retention (RUNTIME)
    3. @Target(METHOD)
    4. public @interface PreDestroy {
    5. }
  • 作用于方法上,在容器销毁Bean的时候回调执行。

  • 被注解方法不能有任何参数。
  • 应用PreDestroy的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。

    实战案例

  1. 定义bean ```java @Slf4j @ToString public class LifeCycleBean implements InitializingBean {

    private String prop;

    public LifeCycleBean() {

    1. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 实例化");

    }

    public LifeCycleBean(String prop) {

    1. this.prop = prop;

    }

    public String getProp() {

    1. return prop;

    }

  1. @PostConstruct
  2. private void postContruct() {
  3. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean postContruct");
  4. }
  5. @PreDestroy
  6. private void preDestory() {
  7. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean preDestory");
  8. }
  9. public void setProp(String prop) {
  10. this.prop = prop;
  11. }
  12. public void init() {
  13. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 初始化");
  14. this.setProp("hello");
  15. }
  16. public void destroy() {
  17. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean destroy");
  18. this.setProp("hello");
  19. }
  20. @Override
  21. public void afterPropertiesSet() throws Exception {
  22. log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean afterPropertiesSet");
  23. }

}

  1. ```java
  2. @Bean(name = "lifeCycleBean", initMethod = "init", destroyMethod = "destroy")
  3. public LifeCycleBean createLifeCycleBean() {
  4. return new LifeCycleBean();
  5. };

定义了Bean初始化和销毁相关的方法,包括实现了InitializingBean接口,Bean配置了initMethoddestroyMethod属性,以及添加了@PostConstruct@PreDestroy注解。

  1. 查看执行结果

image.png

  1. 小结

根据执行结果得知bean初始化和销毁的顺序:

  1. @PostContruct注解对应的方法
  2. 实现了InitializingBean接口的afterPropertiesSet方法
  3. Beaninit-method属性对应的方法
  4. @PreDestroy注解对应的方法
  5. Beandestroy-method属性对应的方法

    源码解析

    image.png
    通过debug快速追踪到实在Bean的初始化阶段。

  6. AbstractAutowireCapableBeanFactoryinitializeBean()方法是bean的初始化入口。

  7. InitDestroyAnnotationBeanPostProcessorBean处理器中调用invokeInitMethods执行@PostContruct对应的方法。

    执行过程

    image.png
    image.png
    在Bean的初始化过程前,会回调BeanPostProcessorpostProcessBeforeInitialization方法,这是Spring的一个扩展点,而我们的@PostConstruct就是通过这种扩展机制实现的,它对应的类是InitDestroyAnnotationBeanPostProcessor

InitDestroyAnnotationBeanPostProcessor,顾名思义,它是用来处理初始化和销毁注解的一个Bean处理器,我们看下它的postProcessBeforeInitialization方法。
image.png
这个方法关键是上面的两步,第一步找出所有注解的方法,第二步执行对应的方法,第二步比较简单,就是调用了反射操作,我们重点关注在第一步findLifecycleMetadata方法。

  1. /**
  2. * 查找指定类型的生命周期元数据.
  3. */
  4. private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
  5. // lifecycleMetadataCache为空,则重新创建生命周期元数据.
  6. if (this.lifecycleMetadataCache == null) {
  7. // Happens after deserialization, during destruction...
  8. return buildLifecycleMetadata(clazz);
  9. }
  10. // 采用双重检查锁机制来进行快速检查,尽量减少对锁的使用。
  11. // 首先进行快速检查,只需最少的锁竞争.
  12. // Quick check on the concurrent map first, with minimal locking.
  13. LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
  14. if (metadata == null) {
  15. // 加锁处理
  16. synchronized (this.lifecycleMetadataCache) {
  17. metadata = this.lifecycleMetadataCache.get(clazz);
  18. if (metadata == null) {
  19. // 关键方法,构建LifecycleMetadata
  20. metadata = buildLifecycleMetadata(clazz);
  21. this.lifecycleMetadataCache.put(clazz, metadata);
  22. }
  23. return metadata;
  24. }
  25. }
  26. return metadata;
  27. }

查看关键的方法buildLifecycleMetadata的源码如下:

  1. /**
  2. * 创建生命周期元数据.
  3. */
  4. private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
  5. // 判断当前Bean是否包含@PostContruct,@PreDestroy,如果不包含,直接返回
  6. if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
  7. return this.emptyLifecycleMetadata;
  8. }
  9. // 初始化相关元数据
  10. List<LifecycleElement> initMethods = new ArrayList<>();
  11. // 销毁相关的元数据
  12. List<LifecycleElement> destroyMethods = new ArrayList<>();
  13. Class<?> targetClass = clazz;
  14. do {
  15. final List<LifecycleElement> currInitMethods = new ArrayList<>();
  16. final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
  17. // 遍历类中的methods
  18. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  19. // 判断方法是有@PostConstruct注解
  20. if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
  21. LifecycleElement element = new LifecycleElement(method);
  22. currInitMethods.add(element);
  23. if (logger.isTraceEnabled()) {
  24. logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
  25. }
  26. }
  27. // 判断方法是有@PreDestroy注解
  28. if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
  29. currDestroyMethods.add(new LifecycleElement(method));
  30. if (logger.isTraceEnabled()) {
  31. logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
  32. }
  33. }
  34. });
  35. initMethods.addAll(0, currInitMethods);
  36. destroyMethods.addAll(currDestroyMethods);
  37. // 查找父类
  38. targetClass = targetClass.getSuperclass();
  39. }
  40. while (targetClass != null && targetClass != Object.class);
  41. return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
  42. new LifecycleMetadata(clazz, initMethods, destroyMethods));
  43. }

该方法主要是遍历bean对应的class以及父class中包含@PostConstruct@PreDestroy注解的方法,构建出
LifecycleMetadata对象。

有一个问题,上面查找其实用的都是initAnnotationType@PostConstruct@PreDestroy属性,那他们是在上面时候设置为@PostConstruct@PreDestroy呢?
我们可以看父类CommonAnnotationBeanPostProcessor的构造方法,它在实例化CommonAnnotationBeanPostProcessor这个bean的时候设置。
image.png

  1. public CommonAnnotationBeanPostProcessor() {
  2. setOrder(Ordered.LOWEST_PRECEDENCE - 3);
  3. // 设置PostConstruct
  4. setInitAnnotationType(PostConstruct.class);
  5. setDestroyAnnotationType(PreDestroy.class);
  6. ignoreResourceType("javax.xml.ws.WebServiceContext");
  7. // java.naming module present on JDK 9+?
  8. if (jndiPresent) {
  9. this.jndiFactory = new SimpleJndiBeanFactory();
  10. }
  11. }

何时实例化CommonAnnotationBeanPostProcessor

那么CommonAnnotationBeanPostProcessor这个Bean处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。
image.png

  1. 在创建容器的时候,会创建所有的BeanPostProcessors Bean。
  2. 在创建CommonAnnotationBeanPostProcessor这个Bean的时候,就会调用对应的构造方法,设置对应的PostConstruct注解。

那又是什么时候把**CommonAnnotationBeanPostProcessor**这个Bean的BeanDefinition加到BeanDefinition工厂中的呢?
只有BeanDefinition工厂中又对应的BeanDefinition才会创建出Bean。答案就是在AnnotationConfigUtils#registerAnnotationConfigProcessors方法中。
image.png

总结

Spring生命周期——@PostContruct, @PreDestroy - 图9
上面时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