Spring源码 - @Resource、@PostConstruct、@PreDestroy原理分析(CommonAnnotationBeanPostProcessor - 上)

目录

一、初始化

CommonAnnotationBeanPostProcessor的静态代码块初始化

CommonAnnotationBeanPostProcessor的无参数构造初始化

二、生命周期和回调时机

1、实现了BeanFactoryAware

2、继承自InitDestroyAnnotationBeanPostProcessor

1)、实现了PriorityOrdered排序接口

2)、实现了MergedBeanDefinitionPostProcessor(生命周期检查阶段)

3)、实现了DestructionAwareBeanPostProcessor接口(生命周期销毁阶段)

3、实现了顶层接口BeanPostProcessor(生命周期初始化阶段)

4、实现了接口InstantiationAwareBeanPostProcessor

总结


    CommonAnnotationBeanPostProcessor处理@Resource、javax.xml.ws.WebServiceRef、javax.ejb.EJB类型;而父类InitDestroyAnnotationBeanPostProcessorinitAnnotationTypedestroyAnnotationType分别存储自定义init方法(@PostConstruct)和@PreDestroy的方法。

 

一、初始化

CommonAnnotationBeanPostProcessor的静态代码块初始化

static {
    try {
        @SuppressWarnings("unchecked")
        Class<? extends Annotation> clazz = (Class<? extends Annotation>)
                ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
        webServiceRefClass = clazz;
    } catch (ClassNotFoundException ex) {
        webServiceRefClass = null;
    }
    try {
        @SuppressWarnings("unchecked")
        Class<? extends Annotation> clazz = (Class<? extends Annotation>)
                ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
        ejbRefClass = clazz;
    } catch (ClassNotFoundException ex) {
        ejbRefClass = null;
    }

    resourceAnnotationTypes.add(Resource.class);
    if (webServiceRefClass != null) {
        resourceAnnotationTypes.add(webServiceRefClass);
    }
    if (ejbRefClass != null) {
        resourceAnnotationTypes.add(ejbRefClass);
    }
}

 

CommonAnnotationBeanPostProcessor的无参数构造初始化

public CommonAnnotationBeanPostProcessor() {
    setOrder(Ordered.LOWEST_PRECEDENCE - 3);
    setInitAnnotationType(PostConstruct.class);
    setDestroyAnnotationType(PreDestroy.class);
    ignoreResourceType("javax.xml.ws.WebServiceContext");
}

 

二、生命周期和回调时机

先看一下CommonAnnotationBeanPostProcessor的层级结构,并且按照调用顺序进行分析

1、实现了BeanFactoryAware

    那么会回调设置BeanFactory

2、继承自InitDestroyAnnotationBeanPostProcessor

1)、实现了PriorityOrdered排序接口

    在无参数构造中设置了排序值,在ApplicationContextAwareProcessor之后。

2)、实现了MergedBeanDefinitionPostProcessor(生命周期检查阶段)

    那么在getBean时,第一次缓存中没有,在调用doCreateBean时第3步会对所有MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition方法进行回调。调用时机,详见:SpringIoc源码(十七)- BeanFactory(六)- getBean(doCreateBean总览)#3、MergedBeanDefinitionPostProcessor回调

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, 
    Class<?> beanType, String beanName) {
    // 父类处理@PostConstruct、@PreDestroy的生命周期相关(check、init、destory、has)
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
    // 子类处理@Resource(javax.xml.ws.WebServiceRef和javax.ejb.EJB不考虑了)
    InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
    metadata.checkConfigMembers(beanDefinition);
}

    父类和子类按照层级处理各自内部的注解,当然在postProcessMergedBeanDefinition方法中只是check阶段。

3)、实现了DestructionAwareBeanPostProcessor接口(生命周期销毁阶段)

    之前分析过在所有bean在getBean时,只要实现了DisposableBean接口,那么都会往Spring注销容器中添加适配模式的DisposableBeanAdapter对象。详细见SpringIoc源码(十七)- BeanFactory(六)- getBean(doCreateBean总览)#6、注册DisposableBean那么在Bean注销时则会调用所有DestructionAwareBeanPostProcessorpostProcessBeforeDestruction方法,执行生命阶段的销毁方法。

@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    // 省略try catch部分的代码
    metadata.invokeDestroyMethods(bean, beanName);
}

 

3、实现了顶层接口BeanPostProcessor(生命周期初始化阶段)

    则执行时机是该Bean生命周期回调阶段,会回调postProcessBeforeInitialization方法。执行时机在check之后,在destory之前。知道了定义的注解@Resource的个的生命周期,并且按层进行处理。那么主要的就是看生命周期中都这么进行处理的。

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    // 省略try catch部分代码
    metadata.invokeInitMethods(bean, beanName);
    return bean;
}

   

4、实现了接口InstantiationAwareBeanPostProcessor

    实现了InstantiationAwareBeanPostProcessor接口,那么在每个Bean的getBean时生命周期的SpringIoc源码(十九)- BeanFactory(八)- getBean(doCreateBean - populateBean属性填充)3、InstantiationAwareBeanPostProcessor#postProcessPropertyValues属性填充阶段回调。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    // 省略try catch部分
    metadata.inject(bean, beanName, pvs);
    return pvs;
}

可能会调用的postProcessPropertyValues回调方法,就是调用了该方法:

@Deprecated
@Override
public PropertyValues postProcessPropertyValues(
    PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {

    return postProcessProperties(pvs, bean, beanName);
}

总结

    CommonAnnotationBeanPostProcessor本类中处理了 @Resource注解,父类中处理了@PostConstruct、@PreDestroy注解。 父类中维护生命周期LifecycleMetadata定义的checkConfigMembers、invokeInitMethods、invokeDestroyMethods、hasDestroyMethods。本类中维护生命周期InjectionMetadata定义的checkConfigMembers、inject、clear。其定义的生命周期,与实现了BeanPostProcessor类型和回调方法关联。静态代码块和构造函数中配置了需要处理的类型,而这三个注解只是其中的一种类型,也保证了扩展性,回调的顺序入下:

1、MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

    LifecycleMetadata checkConfigMembers (间接调用了findLifecycleMetadata)

    InjectionMetadata checkConfigMembers (间接调用了findResourceMetadata

2、BeanPostProcessor#postProcessBeforeInitialization

    LifecycleMetadata#invokeInitMethods

3、InstantiationAwareBeanPostProcessor#postProcessProperties

    InjectionMetadata#inject

4、DestructionAwareBeanPostProcessor#postProcessBeforeDestruction

    LifecycleMetadata#invokeDestroyMethods

 

    知道了是按层对不同的注解进行处理,也知道注解定义了生命周期,并且知道了回调顺序、时机。并且都调用了findLifecycleMetadata方法和各种生命周期的方法,那么下面需要专门各自生命周期。下一篇专门分析

 

 

<think>首先,用户的问题是:“我想了解@PostConstruct@PreDestroy注解的执行顺序”,这意味着用户想知道这些注解在Spring生命周期中的执行顺序。 我收到了几个引用: - 引用[1]:提到@PostConstruct@PreDestroy是Java自带的注解,源于JSR-250。@PostConstruct在项目启动时执行,在Spring容器初始化时执行。 - 引用[2]:解释@PostConstruct在构造函数之后,Servlet的init()方法之前执行。 - 引用[3]:更详细地说明@PostConstruct在构造函数之后,init()方法之前运行,类似于init-method。 用户明确问执行顺序,所以我需要基于这些引用构建一个清晰的回答。 关键点: - @PostConstruct:在对象创建后执行,具体在构造函数之后,init方法(如Servlet的init())之前。 - @PreDestroy:在对象销毁前执行,类似于destroy方法。 用户的问题聚焦在“执行顺序”,所以我应该详细说明这些注解在Spring Bean生命周期中的位置。 系统指令: - 所有行内数学表达式用$...$(但这里可能不需要数学)。 - 独立公式用$$...$$(可能也不需要)。 - LaTeX语法正确(同样,可能不适用)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,逐步解决问题。 所以,回答要用中文。 结构回答: 1. **引言**:简要介绍@PostConstruct@PreDestroy注解。 2. **执行顺序**: - 对于@PostConstruct:在Bean实例化后执行。 - 顺序:构造函数 -> @PostConstruct方法 -> init方法(如果有)。 - 基于引用[2]和[3]:在构造函数之后,Servlet的init()方法之前。 - 对于@PreDestroy:在Bean销毁前执行。 - 顺序:在Spring容器关闭时执行,在destroy方法之前(具体细节)。 - 引用中较少提到@PreDestroy的顺序,但我可以推断:在Servlet的destroy()方法之前或类似。 3. **引用支持**:自然地引用来源。 4. **总结**:简述执行顺序。 5. **相关问题**:生成相关问题。 系统的示例展示了在回答末尾添加相关问题部分。 生成相关问题: - 基于主题:Spring生命周期、注解使用、其他相关注解、性能考虑等。 - 例如: 1. @PostConstruct@PreDestroy与其他Spring生命周期回调(如InitializingBean, DisposableBean)的顺序如何? 2. 如何配置多个@PostConstruct方法的执行顺序? 3. 在Spring Boot中,这些注解的行为有什么不同? 4. @PreDestroy注解的执行时机是什么? 5. 这些注解在多线程环境下的行为如何? 确保回答真实可靠:基于提供的引用,我可以整理如下: - @PostConstruct:在Bean的构造函数调用之后执行,在init方法之前。[^2][^3] - @PreDestroy:在Bean销毁之前执行,在destroy方法之前。引用[1]提到在项目启动时执行@PostConstruct,但@PreDestroy在容器关闭时执行。引用中较少详述@PreDestroy,但可以推断。 补充知识(如果安全): -Spring中,Bean的生命周期: 1. 实例化(构造函数) 2. 依赖注入 3. @PostConstruct方法 4. 初始化回调(如InitializingBean的afterPropertiesSet) 5. ... 使用 ... 6. 销毁回调(@PreDestroy) 7. destroy方法(如DisposableBean的destroy) 但严格基于引用:引用[3]提到@PostConstruct在构造函数之后,init方法之前;init方法可能指InitializingBean或XML定义的init-method。 回复框架: - 开头:直接回答用户问题。 - 主体:解释执行顺序,引用来源。 - 结尾:总结并添加相关问题。 用中文写。 </think>### @PostConstruct@PreDestroy注解的执行顺序 在Spring框架中,`@PostConstruct`和`@PreDestroy`是源于JSR-250(Java通用注解)的生命周期回调注解,用于管理Bean的初始化和销毁过程。下面我将基于提供的引用,清晰地解释它们的执行顺序。这些注解的执行顺序严格遵循Spring Bean的生命周期,确保初始化逻辑在对象创建后执行,而清理逻辑在对象销毁前执行。 #### 1. **@PostConstruct注解的执行顺序** - **位置**:`@PostConstruct`注解的方法在Bean的构造函数执行完毕后运行,但在Bean的初始化方法(如Servlet的`init()`方法或XML配置中的`init-method`)之前执行。 - **顺序细节**: - 第一步:Bean实例化(调用构造函数)。 - 第二步:依赖注入(Spring为Bean注入所需的依赖)。 - 第三步:执行`@PostConstruct`注解的方法(例如,用于初始化全局配置或资源加载)。 - 第四步:执行自定义初始化方法(如实现了`InitializingBean`接口的`afterPropertiesSet`方法,或XML中定义的`init-method`)。 - 引用支持:根据引用[2]和[3],`@PostConstruct`在构造函数之后、Servlet的`init()`方法之前运行,相当于替代或补充XML中的`init-method`配置[^2][^3]。例如: ```java public class MyBean { public MyBean() { System.out.println("构造函数执行"); } @PostConstruct public void init() { System.out.println("@PostConstruct方法执行"); // 在构造函数后,init-method前 } } ``` #### 2. **@PreDestroy注解的执行顺序** - **位置**:`@PreDestroy`注解的方法在Bean即将被销毁时执行,但在Bean的销毁方法(如Servlet的`destroy()`方法或XML配置中的`destroy-method`)之前运行。它用于释放资源(如关闭数据库连接)。 - **顺序细节**: - 第一步:Spring容器关闭或Bean被移除。 - 第二步:执行`@PreDestroy`注解的方法(例如,清理临时文件或释放内存)。 - 第三步:执行自定义销毁方法(如实现了`DisposableBean`接口的`destroy`方法,或XML中定义的`destroy-method`)。 - 第四步:Bean被垃圾回收。 - 引用支持:引用[1]提到`@PreDestroy`在项目关闭时执行,类似于`destroy-method`的逻辑[^1]。虽然具体顺序在引用中未详述,但基于Spring生命周期规范,`@PreDestroy`总是优先于其他销毁回调执行。 #### 总结执行顺序表 | 生命周期阶段 | 执行顺序 | |--------------|----------| | **初始化阶段** | 1. 构造函数 → 2. 依赖注入 → 3. `@PostConstruct`方法 → 4. 自定义`init-method` | | **销毁阶段** | 1. 容器关闭 → 2. `@PreDestroy`方法 → 3. 自定义`destroy-method` → 4. Bean销毁 | 关键注意事项: - `@PostConstruct`和`@PreDestroy`是Java标准注解,不依赖Spring特定API,但Spring会识别并顺序执行它们。 - 如果Bean中有多个`@PostConstruct`或`@PreDestroy`方法,Spring不保证顺序(通常按方法名或定义顺序),建议只定义一个以避免不确定性。 - 在Servlet环境中(如Spring Boot),顺序与Servlet生命周期集成:`@PostConstruct`在`Servlet.init()`前,`@PreDestroy`在`Servlet.destroy()`前[^2][^3]。 如果您在代码中遇到顺序问题,可以通过日志验证执行时机(如在每个生命周期方法中添加日志输出)。 --- ### 相关问题 基于您的查询,以下是可能的相关扩展问题: 1. `@PostConstruct`和`@PreDestroy`与其他Spring生命周期接口(如`InitializingBean`和`DisposableBean`)的执行顺序如何? 2. 在Spring Boot中,`@PostConstruct`和`@PreDestroy`的行为与标准Spring有何不同? 3. 如何确保多个`@PostConstruct`方法的执行顺序? 4. `@PreDestroy`注解在容器异常关闭时是否会被触发? 5. 这些注解在分布式系统或多线程环境下的行为有什么特殊考虑?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值