核心概念:
Spring Framework 提供了一系列接口,允许您定制 Bean 的特性。这些接口主要分为以下几类:生命周期回调、Aware 接口 和 Bean 作用域(本页主要涵盖前两者)。
一、生命周期回调 (Lifecycle Callbacks)
Spring 容器管理 Bean 的生命周期,允许您通过回调方法在 Bean 初始化和销毁时执行特定操作。
-
实现方式:
-
Spring 专用接口:
InitializingBean和DisposableBean。InitializingBean要求实现void afterPropertiesSet() throws Exception;方法。容器在设置完 Bean 的所有必要属性后调用此方法。DisposableBean要求实现void destroy() throws Exception;方法。当包含该 Bean 的容器销毁时调用此方法。- 注意: Spring 不推荐使用这两个接口,因为它们会将代码与 Spring API 耦合。
-
JSR-250 标准注解 (推荐):
@PostConstruct: 标注在方法上,效果等同于afterPropertiesSet(),用于初始化后操作。@PreDestroy: 标注在方法上,效果等同于destroy(),用于销毁前操作。- 优点: 使用标准注解解耦了代码与 Spring 特定接口,是现代 Spring 应用的最佳实践。
-
Bean 定义元数据:
init-method和destroy-method属性。-
XML 配置: 在
<bean>元素上指定init-method="方法名"和/或destroy-method="方法名"。方法需是public void 方法名()形式(无参数)。<bean id="exampleBean" class="com.example.Bean" init-method="myInit" destroy-method="myCleanup"/>
-
Java 配置 (
@Bean): 使用@Bean(initMethod = "myInit", destroyMethod = "myCleanup")。 -
优点: 解耦,不依赖 Spring 特定接口或注解。
-
-
底层机制: Spring 内部使用
BeanPostProcessor实现来处理找到的所有回调接口并调用相应方法。如需自定义生命周期行为,可自行实现BeanPostProcessor。
-
-
初始化回调详解 (
@PostConstruct/init-method/afterPropertiesSet):-
调用时机:在 Bean 的所有依赖项被注入(属性设置完成)之后。
-
@PostConstruct和常规初始化方法在容器的单例创建锁内执行。Bean 实例只有在@PostConstruct方法返回后才被视为完全初始化并可发布给其他对象使用。 -
重要限制: 这些初始化方法仅应用于验证配置状态或基于给定配置准备数据结构。禁止在其中进行访问外部 Bean 的复杂活动,否则可能导致初始化死锁。
-
昂贵初始化操作: 如果需要在初始化后执行耗时操作(如异步数据库准备),应选择:
- 实现
SmartInitializingSingleton.afterSingletonsInstantiated()方法。 - 监听上下文刷新事件:实现
ApplicationListener<ContextRefreshedEvent>或使用@EventListener(ContextRefreshedEvent.class)。 - 这些方式在所有常规单例初始化完成后执行,不受单例创建锁限制。
- 替代方案:实现
(Smart)Lifecycle接口以集成到容器的整体生命周期管理中(见下文)。
- 实现
-
-
销毁回调详解 (
@PreDestroy/destroy-method/destroy):-
调用时机:当包含该 Bean 的容器被销毁时。
-
Spring 支持推断销毁方法:自动检测 Bean 类上的
public void close()或public void shutdown()方法。- Java 配置 (
@Bean) 默认支持此推断,且会自动匹配java.lang.AutoCloseable或java.io.Closeable实现。 - XML 配置:将
<bean>的destroy-method属性设置为特殊值(inferred)以启用自动检测。 - XML 默认配置:在顶级
<beans>元素上设置default-destroy-method="(inferred)"可将此行为应用于该文件中的所有 Bean。
- Java 配置 (
-
扩展的关闭阶段: 实现
Lifecycle接口可以在任何单例 Bean 的销毁方法被调用之前接收一个“停止”信号。实现SmartLifecycle可以进行有时间限制的停止步骤(容器会等待该步骤完成再执行销毁方法)。
-
-
默认初始化和销毁方法:
- 可以在顶级
<beans>元素上设置default-init-method="init"和default-destroy-method="destroy"属性。 - 作用:容器会检查该文件中定义的每个 Bean 类,如果存在指定名称的方法(如
init()或destroy()),就会在相应生命周期阶段调用它。 - 优点: 促进项目内命名一致性,减少重复配置。
- 覆盖默认: 如果某个 Bean 的回调方法命名与默认不同,可以在其
<bean>定义中显式使用init-method或destroy-method属性指定方法名。
- 可以在顶级
-
回调执行顺序 (组合机制):
-
如果一个 Bean 配置了多种生命周期机制且方法名不同,则按以下固定顺序执行:
-
初始化:
- 用
@PostConstruct注解的方法 InitializingBean.afterPropertiesSet()- 自定义配置的初始化方法 (如
init-method指定)
- 用
-
销毁:
- 用
@PreDestroy注解的方法 DisposableBean.destroy()- 自定义配置的销毁方法 (如
destroy-method指定)
- 用
-
-
如果为同一个生命周期事件(如初始化)配置了多个机制但方法名相同(如都叫
init()),该方法只会被执行一次。
-
-
启动和停止回调 (
Lifecycle接口):-
Lifecycle接口 (void start(); void stop(); boolean isRunning();) 适用于拥有自身生命周期需求的对象(如启停后台进程)。 -
传播机制: 当
ApplicationContext本身收到启动 (start()) 或停止 (stop()) 信号(例如运行时停止/重启场景)时,它会将这些调用级联传播给其上下文内定义的所有Lifecycle实现。这是通过委托给LifecycleProcessor实现的。 -
LifecycleProcessor接口扩展了Lifecycle,增加了void onRefresh(); void onClose();方法,用于响应上下文的刷新和关闭事件。 -
重要: 基本的
Lifecycle接口只提供显式的启动/停止通知,不意味着在上下文刷新时自动启动。 -
精细控制 (
SmartLifecycle接口): 对于对自动启动和优雅停止(包括启动和停止阶段)的精细控制,应实现SmartLifecycle接口。它扩展了Lifecycle和Phased接口。-
Phased接口 (int getPhase();):定义阶段 (Phase) 值。 -
SmartLifecycle增加方法:boolean isAutoStartup(); void stop(Runnable callback); -
执行顺序控制 (Phase):
- 启动 (
start()):phase值最低的 Bean 最先启动。 - 停止 (
stop()):phase值最高的 Bean 最先停止(与启动顺序相反)。 - 默认值: 未实现
SmartLifecycle的普通LifecycleBean 的phase默认为0。 - 负值: 启动更早 (
<0),停止更晚 (<0意味着在顺序中更靠后停止)。 - 正值: 启动更晚 (
>0),停止更早 (>0意味着在顺序中更靠前停止)。
- 启动 (
-
优雅停止 (
stop(Runnable callback)): 实现者必须在自己的停止过程完成后调用callback.run()。这支持必要的异步关闭。默认的DefaultLifecycleProcessor会等待每个阶段内的所有对象调用此回调,默认等待每个阶段 30 秒。-
配置超时: 可以通过在上下文中定义一个名为
lifecycleProcessor的 Bean 来覆盖默认处理器或修改超时。<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> <property name="timeoutPerShutdownPhase" value="10000"/> <!-- 10秒超时 --> </bean>
-
-
自动启动 (
isAutoStartup()): 在上下文刷新完成后(所有 Bean 实例化并初始化后),如果SmartLifecycleBean 的isAutoStartup()返回true,则该 Bean 会在此时自动启动,而无需显式调用上下文或其自身的start()方法。启动顺序同样由phase值和依赖关系决定。
-
-
-
在非 Web 应用中优雅关闭 Spring IoC 容器:
-
仅适用于非 Web 应用。 (Spring Web 应用的
ApplicationContext已有内置关闭逻辑) -
在非 Web 环境(如桌面应用)中使用 Spring IoC 容器时,需要向 JVM 注册一个关闭钩子 (Shutdown Hook) 来确保容器优雅关闭,并调用单例 Bean 的销毁方法释放资源。
-
注册方法: 调用
ConfigurableApplicationContext的registerShutdownHook()方法。-
Java 示例:
public final class Boot { public static void main(final String[] args) throws Exception { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); ctx.registerShutdownHook(); // 注册关闭钩子 // ... 应用运行逻辑 ... // main 方法退出时,关闭钩子会被调用,触发容器优雅关闭 } } -
Kotlin 示例:
fun main() { val ctx = ClassPathXmlApplicationContext("beans.xml") ctx.registerShutdownHook() // 注册关闭钩子 // ... 应用运行逻辑 ... // main 方法退出时,关闭钩子会被调用,触发容器优雅关闭 }
-
-
-
线程安全性与可见性 (Thread Safety and Visibility):
- Spring 核心容器以线程安全的方式发布创建的单例实例,通过单例锁保护访问并保证对其他线程的可见性。
- 初始化状态可见性: 应用提供的 Bean 类无需关心其初始化状态的可见性问题。只要配置字段仅在初始化阶段被修改,它们无需标记为
volatile。Spring 提供的可见性保证类似于final字段,即使对于该阶段可变的基于 setter 的配置状态也是如此。 - 初始化后修改: 如果在 Bean 创建阶段及其后续的初始发布之后修改字段,则访问这些字段时需要将其声明为
volatile或使用公共锁进行保护。 - 单例访问: 在单例 Bean 实例(如控制器、仓库)中对这种配置状态进行并发访问,在容器完成安全初始发布后是完全线程安全的。这包括在常规单例锁内处理的
FactoryBean实例。 - 销毁与运行时状态: 销毁回调期间的配置状态仍然是线程安全的。但在初始化和销毁之间累积的任何运行时状态,应按照常规 Java 准则保存在线程安全的结构中(或对于简单情况使用
volatile字段)。更深入的生命周期集成(如SmartLifecycle中的Runnable字段)需要声明为volatile。 - 生命周期顺序与状态: 虽然常规回调遵循顺序(如
start()保证在初始化完成后调用,stop()在start()之后),但在“在销毁前停止”的常规安排中存在特殊情况:建议任何此类 Bean 的内部状态也应允许在没有前置stop()的情况下立即响应destroy()回调,因为这在引导取消后的异常关闭或由另一个 Bean 导致的停止超时情况下可能发生。
二、ApplicationContextAware 和 BeanNameAware
-
ApplicationContextAware:
- 如果一个对象实现了
org.springframework.context.ApplicationContextAware接口,当ApplicationContext创建它时,会通过setApplicationContext(ApplicationContext applicationContext)方法将自身引用注入给该对象。 - 用途:允许 Bean 以编程方式操作创建它的
ApplicationContext(例如获取其他 Bean、访问资源、发布事件等)。 - 注意: 这种方式将代码与 Spring API 耦合,违反了控制反转(IoC)原则(依赖应作为属性注入)。通常建议避免使用,除非是基础架构 Bean 确实需要编程式容器访问。
- 替代方案: 使用自动装配 (Autowiring)。可以通过构造器、setter 方法、字段或方法参数自动装配
ApplicationContext类型的依赖(配合@Autowired注解是更灵活的方式)。
- 如果一个对象实现了
-
BeanNameAware:
- 如果一个对象实现了
org.springframework.beans.factory.BeanNameAware接口,当ApplicationContext创建它时,会通过setBeanName(String name)方法将其在 Bean 定义中配置的名称注入给该对象。 - 调用时机:在 Bean 的普通属性被设置之后,但在任何初始化回调(如
InitializingBean.afterPropertiesSet()或自定义init-method)之前。
- 如果一个对象实现了
三、其他 Aware 接口 (Other Aware Interfaces)
除了 ApplicationContextAware 和 BeanNameAware,Spring 还提供了广泛的 Aware 回调接口,让 Bean 向容器表明它们需要特定的基础设施依赖。接口名称通常表明了依赖类型。
| 接口名称 (Name) | 注入的依赖 (Injected Dependency) | 说明 (Explained in...) |
|---|---|---|
| ApplicationContextAware | 声明该 Bean 的 ApplicationContext | 本页 |
| ApplicationEventPublisherAware | 封闭 ApplicationContext 的事件发布器 | ApplicationContext 的附加功能 |
| BeanClassLoaderAware | 用于加载 Bean 类的类加载器 | 实例化 Beans |
| BeanFactoryAware | 声明该 Bean 的 BeanFactory | BeanFactory API |
| BeanNameAware | 声明该 Bean 的名称 | 本页 |
| LoadTimeWeaverAware | 用于在加载时处理类定义的定义织入器 (weaver) | Spring Framework 中使用 AspectJ 进行加载时织入 |
| MessageSourceAware | 配置的用于解析消息的策略(支持参数化和国际化) | ApplicationContext 的附加功能 |
| NotificationPublisherAware | Spring JMX 通知发布器 | 通知 (Notifications) |
| ResourceLoaderAware | 配置的用于低级访问资源的加载器 | 资源 (Resources) |
| ServletConfigAware | 容器运行所在的当前 ServletConfig (仅限 Web 环境) | Spring MVC |
| ServletContextAware | 容器运行所在的当前 ServletContext (仅限 Web 环境) | Spring MVC |
- 再次注意: 使用这些
Aware接口会将代码与 Spring API 绑定,不符合控制反转原则。建议仅将其用于需要编程式访问容器的基础设施 Bean。