第七节 Spring Bean 后置处理器

Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。

BeanPostProcessor ​接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。你也可以在 ​Spring​ 容器通过插入一个或多个 ​BeanPostProcessor​ 的实现来完成实例化,配置和初始化一个​bean​之后实现一些自定义逻辑回调方法。

你可以配置多个 ​BeanPostProcessor ​接口,通过设置 ​BeanPostProcessor ​实现的​ Ordered ​接口提供的​ order​ 属性来控制这些​ BeanPostProcessor​ 接口的执行顺序。

BeanPostProcessor​ 可以对​ bean​(或对象)实例进行操作,这意味着 ​Spring IoC​ 容器实例化一个 ​bean​ 实例,然后 ​BeanPostProcessor​ 接口进行它们的工作。

注意:

ApplicationContext​ 会自动检测由 ​BeanPostProcessor​ 接口的实现定义的 ​bean​,注册这些​ bean​ 为后置处理器,然后通过在容器中创建​ bean​,在适当的时候调用它。

在你自定义的​ BeanPostProcessor​ 接口实现类中,要实现以下的两个抽象方法  ​BeanPostProcessor.postProcessBeforeInitialization(Object, String)​ 和  ​BeanPostProcessor.postProcessAfterInitialization(Object, String)​ 和,注意命名要准确

否则会出现: ​“ The type InitHelloWorld must implement the inherited abstract method BeanPostProcessor.postProcessBeforeInitialization(Object, String) ”​之类的错误

例子:

下面的例子显示如何在 ​ApplicationContext​ 的上下文中编写,注册和使用 ​BeanPostProcessor​。

我们在适当的位置使用 Eclipse IDE,然后按照下面的步骤来创建一个 ​Spring ​应用程序:

步骤描述
1创建一个名称为 SpringExample 的项目,并且在创建项目的​ src ​文件夹中创建一个包 com.tutorialspoint
2使用​ Add External JARs ​选项,添加所需的​ Spring​ 库,解释见 ​Spring Hello World Example ​章节。
3在 ​com.tutorialspoint ​包中创建 Java 类 HelloWorld、​InitHelloWorld ​和 MainApp
4在​ src ​文件夹中创建​ Beans ​配置文件​ Beans.xml​。
5最后一步是创建的所有 Java 文件和 Bean 配置文件的内容,并运行应用程序,解释如下所示。

这里是 HelloWorld.java 文件的内容:

package com.tutorialspoint;
public class HelloWorld {
   private String message;
   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
   public void init(){
      System.out.println("Bean is going through init.");
   }
   public void destroy(){
      System.out.println("Bean will destroy now.");
   }
}

这是实现​ BeanPostProcessor ​的非常简单的例子,它在任何 ​bean​ 的初始化的之前和之后输入该 ​bean​ 的名称。你可以在初始化 ​bean ​的之前和之后实现更复杂的逻辑,因为你有两个访问内置 ​bean​ 对象的后置处理程序的方法。

这里是 InitHelloWorld.java 文件的内容:

package com.tutorialspoint;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InitHelloWorld implements BeanPostProcessor {
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      System.out.println("BeforeInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      System.out.println("AfterInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
}

下面是 MainApp.java 文件的内容。在这里,你需要注册一个在 ​AbstractApplicationContext​ 类中声明的关闭​ hook​ 的 ​registerShutdownHook() ​方法。它将确保正常关闭,并且调用相关的 ​destroy​ 方法。

package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
   public static void main(String[] args) {
      AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      context.registerShutdownHook();
   }
}

下面是 ​init​ 和 ​destroy​ 方法需要的配置文件 Beans.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
​
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
​
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld"
       init-method="init" destroy-method="destroy">
       <property name="message" value="Hello World!"/>
   </bean>
​
   <bean class="com.tutorialspoint.InitHelloWorld" />
​
</beans>

一旦你创建源代码和 ​bean​ 配置文件完成后,我们就可以运行该应用程序。如果你的应用程序一切都正常,将输出以下信息:

BeforeInitialization : helloWorld
Bean is going through init.
AfterInitialization : helloWorld
Your Message : Hello World!
Bean will destroy now.

<think>我们正在讨论Spring Bean的生命周期。根据用户提供的引用,我们可以整合出一个详细的Spring Bean生命周期说明。注意,用户引用的内容主要来自四个来源,我们需要将这些内容融合起来,形成一个连贯的生命周期流程。 Spring Bean的生命周期可以分为以下几个主要阶段: 1. **实例化(Instantiation)**:通过构造函数或工厂方法创建Bean的实例。 2. **属性赋值(Population of Properties)**:设置Bean的属性值,包括对其他Bean的引用(依赖注入)。 3. **BeanPostProcessor前置处理**:调用`BeanPostProcessor`的`postProcessBeforeInitialization`方法。 4. **初始化(Initialization)**: - 如果Bean实现了`InitializingBean`接口,则调用`afterPropertiesSet`方法。 - 调用自定义的初始化方法(通过`init-method`指定)。 5. **BeanPostProcessor后置处理**:调用`BeanPostProcessor`的`postProcessAfterInitialization`方法。 6. **Bean就绪(Ready for Use)**:此时Bean已经可以使用,被放入应用上下文中。 7. **销毁(Destruction)**: - 如果Bean实现了`DisposableBean`接口,则调用`destroy`方法。 - 调用自定义的销毁方法(通过`destroy-method`指定)。 此外,还需要考虑一些特殊场景: - 对于`prototype`作用域的Bean,容器只负责到初始化完成(第6步),不负责销毁,需要由客户端代码自行销毁。 - 延迟初始化(`lazy-init`)的Bean,在容器启动时不会立即创建,而是在第一次请求时才会初始化。 下面我们详细展开每个阶段,并适当引用用户提供的资料。 ### 1. 实例化(Instantiation) Spring容器根据Bean的定义(如XML配置、Java配置或注解扫描)来创建Bean的实例。这通常通过反射调用构造函数完成,或者通过工厂方法创建。 ### 2. 属性赋值(Populate Properties) 容器将配置的属性值(包括基本类型、集合、引用等)设置到Bean实例中。这包括自动装配(autowiring)和显式设置属性值。 ### 3. BeanPostProcessor前置处理 如果容器中存在实现了`BeanPostProcessor`接口的Bean,那么每个Bean在初始化之前都会调用这些处理器的`postProcessBeforeInitialization`方法。这个方法允许我们对Bean进行一些前置处理,例如修改Bean的属性值或返回一个包装对象。 引用[4]中提到: > BeanPostProcessor.postProcessBeforeInitialization(): 将此BeanPostProcessor应用于给定的新Bean实例,在任何Bean初始化回调方法(像是InitializingBean.afterPropertiesSet或者自定义的初始化方法)之前调用。这个Bean将要填充属性的值。返回的Bean实例可能被普通对象包装,默认实现返回时一个Bean。[^4] ### 4. 初始化(Initialization) 这个阶段包括两个步骤: - **InitializingBean接口的afterPropertiesSet方法**:如果Bean实现了`InitializingBean`接口,Spring会调用其`afterPropertiesSet`方法。这个方法允许Bean在属性设置完成后执行自定义的初始化逻辑。 - **自定义初始化方法**:通过配置(如XML中的`init-method`属性,或Java配置中的`@Bean(initMethod = "init")`)指定的方法。这个方法与`afterPropertiesSet`类似,但它是可选的,并且不要求Bean实现特定接口。 ### 5. BeanPostProcessor后置处理 在初始化之后,会调用`BeanPostProcessor`的`postProcessAfterInitialization`方法。这个方法允许我们对初始化后的Bean进行进一步处理,例如生成代理对象(AOP就是在这个阶段实现的)。 ### 6. Bean就绪(Ready for Use) 此时,Bean已经完成了所有的初始化步骤,可以被应用程序使用了。它被放入Spring容器中,可以被其他Bean依赖或通过上下文获取。 ### 7. 销毁(Destruction) 当容器关闭时(例如,在`ApplicationContext`上调用`close()`方法),容器会管理Bean的销毁过程: - **DisposableBean接口的destroy方法**:如果Bean实现了`DisposableBean`接口,Spring会调用其`destroy`方法。 - **自定义销毁方法**:通过配置(如XML中的`destroy-method`属性,或Java配置中的`@Bean(destroyMethod = "cleanup")`)指定的方法。 引用[3]中提到: > 这个时候bean就已经初始化完成在spring容器中,可以被调用了。销毁bean:如果bean实现了DisposableBean, spring将调用它的destroy方法,如果bean使用destroyMethod声明了销毁方法,spring该方法也会销毁。[^3] ### 特殊场景 - **Prototype作用域的Bean**:容器只负责实例化、属性赋值和初始化(直到第6步),但不负责销毁。销毁的责任由客户端代码承担。引用[2]提到: > Prototype作用域Bean生命周期仅到"Bean就绪"阶段,容器不管理prototype bean的销毁,需客户端代码自行清理资源。[^2] - **延迟初始化(Lazy-init)**:如果Bean配置了`lazy-init="true"`,那么该Bean不会在容器启动时创建,而是在第一次被请求时才会初始化。 ### 总结流程图 我们可以用流程图来展示Spring Bean的生命周期: ```mermaid graph TD A[实例化] --> B[属性赋值] B --> C[BeanPostProcessor前置处理] C --> D[InitializingBean的afterPropertiesSet] D --> E[自定义初始化方法] E --> F[BeanPostProcessor后置处理] F --> G[Bean就绪] G --> H{容器关闭} H --> I[DisposableBean的destroy] I --> J[自定义销毁方法] ``` 注意:对于`prototype`作用域的Bean,流程到`G[Bean就绪]`就结束了,不会执行销毁阶段。 ### 注意事项 - 在BeanPostProcessor的前置和后置处理阶段,Spring容器会遍历所有的`BeanPostProcessor`,并按顺序应用它们。 - 循环依赖问题:在属性赋值阶段,如果Bean之间存在循环依赖,Spring会通过提前暴露未完全初始化的Bean(即三级缓存)来解决。但这只适用于单例作用域的Bean。 ### 相关问题 根据用户的要求,我们生成以下相关问题: §§相关问题§§ 1. Spring如何解决循环依赖问题? 2. BeanPostProcessor在Spring框架中有哪些具体应用场景? 3. 在Spring中如何自定义Bean的初始化和销毁方法? 4. 为什么Spring容器不管理prototype作用域Bean的销毁?</think>### Spring Bean 生命周期详解 Spring Bean 的生命周期是从 Bean 被创建、初始化到销毁的完整过程,包含多个关键阶段和扩展点。以下是详细的生命周期流程: #### 一、核心生命周期阶段 ```mermaid graph TD A[实例化] --> B[属性赋值] B --> C[BeanPostProcessor 前置处理] C --> D[初始化] D --> E[BeanPostProcessor 后置处理] E --> F[Bean 就绪] F --> G{容器关闭} G --> H[销毁] ``` 1. **实例化(Instantiation)** - 通过构造函数或工厂方法创建 Bean 实例 - 此时对象处于"半成品"状态,属性尚未注入[^1] 2. **属性赋值(Population of Properties)** - 依赖注入(DI):通过 setter 或字段注入填充属性值 - 解决循环依赖(通过三级缓存机制)[^1][^4] 3. **BeanPostProcessor 前置处理** ```java // BeanPostProcessor 接口方法 default Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; // 可在此修改或包装Bean } ``` - 调用所有注册的 `BeanPostProcessor` 的前置方法 - 典型应用:AOP 代理创建(如 `AnnotationAwareAspectJAutoProxyCreator`)[^4] 4. **初始化(Initialization)** - **Aware 接口回调**(按顺序): - `BeanNameAware` → `BeanClassLoaderAware` → `BeanFactoryAware` - **初始化回调**(两种方式): - 实现 `InitializingBean` 接口的 `afterPropertiesSet()` 方法 - 自定义初始化方法(通过 `@PostConstruct` 或 XML 的 `init-method`)[^1][^3] 5. **BeanPostProcessor 后置处理** ```java default Object postProcessAfterInitialization(Object bean, String beanName) { return bean; // 返回最终Bean(可能是代理对象) } ``` - 对初始化后的 Bean 进行最终增强(如 AOP 代理完成)[^4] 6. **Bean 就绪(Ready for Use)** - Bean 进入应用上下文,可被其他组件使用[^3] - 对于单例 Bean,此时已放入一级缓存(`singletonObjects`) 7. **销毁(Destruction)** - **销毁回调**(两种方式): - 实现 `DisposableBean` 接口的 `destroy()` 方法 - 自定义销毁方法(通过 `@PreDestroy` 或 XML 的 `destroy-method`)[^3] - 仅适用于单例 Bean,原型 Bean 需手动销毁 #### 二、特殊场景处理 1. **Prototype 作用域 Bean** - 生命周期仅到 **Bean 就绪** 阶段 - 容器不管理销毁,需客户端代码手动清理资源[^2] ```xml <bean id="protoBean" class="com.Example" scope="prototype"/> ``` 2. **延迟初始化(Lazy-init)** - 首次请求时才初始化,减少启动开销 ```xml <bean id="lazyBean" class="com.LazyBean" lazy-init="true"/> ``` 3. **循环依赖解决方案** - 三级缓存机制: - 一级缓存:完整单例对象 (`singletonObjects`) - 二级缓存:早期暴露对象 (`earlySingletonObjects`) - 三级缓存:对象工厂 (`singletonFactories`)[^1] #### 三、关键扩展点总结 | 扩展点 | 触发时机 | 典型应用场景 | |---------------------------|--------------------------|--------------------------| | `BeanPostProcessor` | 初始化前后 | AOP代理、属性修改 | | `BeanFactoryPostProcessor`| Bean定义加载后 | 修改Bean定义(如占位符) | | `InitializingBean` | 属性注入完成后 | 数据校验、资源初始化 | | `DisposableBean` | 容器关闭时 | 资源释放、连接关闭 | > ⚠️ **重要规则**: > 1. 构造函数中避免调用未初始化的属性 > 2. `@PostConstruct` 优先于 `InitializingBean.afterPropertiesSet()` > 3. 原型Bean的销毁方法需通过 `ConfigurableBeanFactory.destroyBean()` 手动触发 #### 四、完整生命周期流程图 ```mermaid graph LR A[容器启动] --> B[加载Bean定义] B --> C[实例化Bean] C --> D[属性赋值/DI] D --> E[调用Aware接口] E --> F[BeanPostProcessor前置处理] F --> G[初始化回调] G --> H[BeanPostProcessor后置处理] H --> I[Bean就绪] I --> J{是否单例?} J -- 是 --> K[加入单例池] J -- 否 --> L[等待请求] K --> M[容器关闭] L --> M M --> N[销毁回调] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值