FactoryBean 与回调接口 - 创建复杂 Bean 与生命周期回调
概述
Spring Framework 提供了两套强大的扩展机制:FactoryBean 用于定制复杂 Bean 的创建逻辑,回调接口族(Aware、InitializingBean、DisposableBean、SmartLifecycle 等)用于在 Bean 生命周期的各个阶段植入自定义行为。理解这些接口的设计原理和执行顺序,是掌握 Spring 容器底层运作的关键。
本文将基于 Spring Framework 5.3.x 源码,深入剖析这些接口的设计意图、执行流程与协作方式,并通过实战案例展示它们的典型应用场景。
1. FactoryBean 接口详解
1.1 接口定义
FactoryBean 是 Spring 容器中一类特殊的 Bean——它本身不是一个直接注入给调用者的对象,而是一个工厂,负责生产另一个 Bean。接口定义如下:
package org.springframework.beans.factory;
public interface FactoryBean<T> {
/**
* 返回由该 FactoryBean 创建的 Bean 实例。
* 如果 isSingleton() 返回 true,则该实例会被容器缓存。
*/
@Nullable
T getObject() throws Exception;
/**
* 返回由该 FactoryBean 创建的 Bean 的类型。
* 如果类型未知则返回 null。
*/
@Nullable
Class<?> getObjectType();
/**
* 返回由该 FactoryBean 创建的 Bean 是否以单例模式存在。
* 如果返回 true,则 getObject() 返回的实例会被容器缓存为单例。
* 默认返回 true。
*/
default boolean isSingleton() {
return true;
}
}1.2 三个核心方法
| 方法 | 说明 |
|---|---|
getObject() | 返回由该工厂创建的 Bean 实例。容器在解析依赖时会调用此方法获取实际对象。 |
getObjectType() | 返回由该工厂创建的 Bean 的类型。用于类型匹配和自动注入时的类型推断。 |
isSingleton() | 指示该工厂创建的 Bean 是否为单例。true 表示由容器缓存一份实例;false 表示每次调用 getObject() 都返回新实例。 |
1.3 用途与典型场景
- 创建复杂对象:当对象的构造过程涉及多个步骤、外部资源或配置参数时,将逻辑封装在
FactoryBean中。 - 创建代理对象:通过
FactoryBean返回 JDK 动态代理或 CGLIB 代理,Spring AOP 的底层机制正是基于此。 - 连接池管理:管理数据库连接池、HTTP 连接池等池化资源。
- 第三方 SDK 集成:封装阿里云、腾讯云等 SDK 的认证、客户端创建和生命周期管理。
Spring 内置的 FactoryBean 示例:
org.springframework.beans.factory.config.ProxyFactoryBean—— 为指定接口创建 AOP 代理。org.springframework.jndi.JndiObjectFactoryBean—— 从 JNDI 查找对象。org.springframework.transaction.jta.JtaTransactionManager的内部工厂。
1.4 自定义 FactoryBean 示例
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class MyConnectionFactoryBean implements FactoryBean<MyConnection> {
private String url;
private String username;
private String password;
// 通过 setter 注入配置
public void setUrl(String url) { this.url = url; }
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
@Override
public MyConnection getObject() throws Exception {
// 模拟复杂的连接创建过程
MyConnection conn = new MyConnection();
conn.setUrl(url);
conn.setUsername(username);
conn.setPassword(password);
conn.init(); // 初始化连接
return conn;
}
@Override
public Class<?> getObjectType() {
return MyConnection.class;
}
@Override
public boolean isSingleton() {
return false; // 每次获取都创建新连接
}
}XML 配置方式:
<bean id="myConnection" class="com.example.MyConnectionFactoryBean">
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="root"/>
<property name="password" value="secret"/>
</bean>2. FactoryBean 的注册与获取
2.1 & 前缀的含义
当一个 Bean 被定义为 FactoryBean 时,容器会注册两个名字:
| 名称 | 返回对象 |
|---|---|
myConnection | FactoryBean.getObject() 返回的实际对象 |
&myConnection | FactoryBean 本身 |
& 前缀用于从容器中获取 FactoryBean 实例本身,而不是它生产的对象。
2.2 使用示例
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FactoryBeanDemo {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 获取 FactoryBean 生产的 Bean
MyConnection conn = ctx.getBean("myConnection", MyConnection.class);
// 获取 FactoryBean 本身(使用 & 前缀)
Object factory = ctx.getBean("&myConnection");
System.out.println(factory instanceof MyConnectionFactoryBean); // true
// 另一种获取 FactoryBean 的方式
MyConnectionFactoryBean fb = (MyConnectionFactoryBean) ctx.getBean("&myConnection");
// 也可以使用 BeanFactory 的特定方法
if (ctx instanceof BeanFactory) {
BeanFactory bf = (BeanFactory) ctx;
FactoryBean<?> fb2 = (FactoryBean<?>) bf.getBean(BeanFactory.FACTORY_BEAN_PREFIX + "myConnection");
}
}
}2.3 源码分析:容器如何区分普通 Bean 与 FactoryBean
在 AbstractBeanFactory 中,核心逻辑位于 getBean(String name) 方法中:
// org.springframework.beans.factory.support.AbstractBeanFactory
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 1. 处理 & 前缀:如果 name 以 & 开头,则 extractedName 去掉前缀
String beanName = transformedBeanName(name);
// 2. 从缓存中获取单例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果 sharedInstance 是 FactoryBean,则调用 getObject()
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// ...
}关键方法 getObjectForBeanInstance 判断实例是否为 FactoryBean,如果是且调用者没有使用 & 前缀,则调用 getObject():
// org.springframework.beans.factory.support.AbstractBeanFactory
protected Object getObjectForBeanInstance(Object beanInstance, String name,
String beanName, @Nullable RootBeanDefinition mbd) {
// 如果 name 以 & 开头,说明调用者想要 FactoryBean 本身
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果是 & 前缀但 bean 不是 FactoryBean 类型,直接返回
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
return beanInstance;
}
// name 不带 & 前缀,且 beanInstance 是 FactoryBean
if (beanInstance instanceof FactoryBean) {
// 调用 getObject() 获取工厂生产的具体对象
return getCachedObjectForFactoryBean(beanName);
}
return beanInstance;
}2.4 核心源码流程:FactoryBeanRegistrySupport
FactoryBean 生产的对象缓存逻辑在 FactoryBeanRegistrySupport(AbstractBeanFactory 的父类)中:
// org.springframework.beans.factory.support.FactoryBeanRegistrySupport
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
protected Object getCachedObjectForFactoryBean(String beanName) {
return this.factoryBeanObjectCache.get(beanName);
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean postProcess) {
// 1. 如果是单例 FactoryBean
if (factory.isSingleton() && this. singletonObjects.containsKey(beanName)) {
// 尝试从缓存获取
Object bean = this.factoryBeanObjectCache.get(beanName);
if (bean == null) {
// 调用 getObject() 创建实际对象
bean = doGetObjectFromFactoryBean(factory, beanName);
// 将创建的对象放入缓存
this.factoryBeanObjectCache.put(beanName, bean);
}
return bean;
}
// 2. 非单例 FactoryBean,每次都创建新实例
return doGetObjectFromFactoryBean(factory, beanName);
}3. Aware 接口族
3.1 设计意图
Aware 接口是一种回调机制,允许 Bean 在初始化时获取容器内部的基础设施对象(如 BeanFactory、ApplicationContext、BeanName 等)。每个 Aware 接口都包含一个 setter 方法,容器在初始化 Bean 时会将对应的对象注入进来。
3.2 常用 Aware 接口
| 接口 | setter 方法 | 注入对象 | 用途 |
|---|---|---|---|
BeanFactoryAware | setBeanFactory(BeanFactory) | 当前 BeanFactory 实例 | 获取 BeanFactory 进行编程式 Bean 查找 |
ApplicationContextAware | setApplicationContext(ApplicationContext) | 当前 ApplicationContext 实例 | 获取完整的容器上下文(事件发布、资源加载等) |
BeanNameAware | setBeanName(String) | 当前 Bean 在容器中的名称 | 获取 Bean 的注册名称 |
EnvironmentAware | setEnvironment(Environment) | 当前 Environment 实例 | 获取环境配置(profile、属性源) |
ResourceLoaderAware | setResourceLoader(ResourceLoader) | 当前 ResourceLoader | 加载外部资源 |
ApplicationEventPublisherAware | setApplicationEventPublisher(ApplicationEventPublisher) | 事件发布器 | 发布应用事件 |
MessageSourceAware | setMessageSource(MessageSource) | 国际化消息源 | 国际化支持 |
ClassLoaderAware | setBeanClassLoader(ClassLoader) | Bean 的类加载器 | 使用特定类加载器加载类/资源 |
3.3 实现示例
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class MyAwareDemo implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, BeanClassLoaderAware {
private String beanName;
private BeanFactory beanFactory;
private ApplicationContext applicationContext;
private ClassLoader classLoader;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean 名称: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory 已注入");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("ApplicationContext 已注入");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("ClassLoader 已注入");
}
// 获取容器中所有 MessageSource 类型的 Bean
public void demoUsage() {
// 通过 BeanFactory 编程式获取 Bean
if (beanFactory.containsBean("someBean")) {
Object someBean = beanFactory.getBean("someBean");
}
// 通过 ApplicationContext 发布事件
applicationContext.publishEvent(new MyCustomEvent(this, "事件消息"));
// 获取 Resource
// Resource resource = applicationContext.getResource("classpath:config.properties");
}
}3.4 源码分析:Aware 回调的触发时机
Aware 接口的回调在 AbstractAutowireCapableBeanFactory 的 invokeAwareMethods 方法中执行:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
// 1. BeanNameAware
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// 2. BeanClassLoaderAware
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// 3. BeanFactoryAware
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}对于 ApplicationContextAware,回调发生在 ApplicationContextAwareProcessor 中,该处理器会在 Bean 初始化前后被调用:
// org.springframework.context.support.ApplicationContextAwareProcessor
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
return bean;
}
// 依次调用各 Aware 接口
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
return bean;
}3.5 执行时机总结
在 Bean 初始化过程中,Aware 回调的执行顺序为:
BeanNameAware.setBeanName()BeanClassLoaderAware.setBeanClassLoader()BeanFactoryAware.setBeanFactory()ApplicationContextAware.setApplicationContext()(在ApplicationContextAwareProcessor中执行)BeanPostProcessor.postProcessBeforeInitialization()(包含上面第 4 步)@PostConstruct/InitializingBean.afterPropertiesSet()/init-methodBeanPostProcessor.postProcessAfterInitialization()
4. InitializingBean 与 DisposableBean
4.1 InitializingBean
当一个 Bean 实现了 InitializingBean 接口,容器会在所有属性填充完成后、Bean 被正式使用之前,调用 afterPropertiesSet() 方法。
package org.springframework.beans.factory;
public interface InitializingBean {
/**
* 在 BeanFactory 设置了所有 Bean 属性后调用。
* 此方法允许 Bean 实例在所有的属性都被设置后执行内部初始化验证和配置。
*/
void afterPropertiesSet() throws Exception;
}4.2 实现示例
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class DatabaseConnector implements InitializingBean {
private String url;
private int maxConnections;
// 通过 setter 注入
public void setUrl(String url) { this.url = url; }
public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; }
@Override
public void afterPropertiesSet() throws Exception {
// 验证必要属性是否已设置
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("url 不能为空");
}
if (maxConnections <= 0) {
throw new IllegalArgumentException("maxConnections 必须大于 0");
}
// 执行初始化逻辑:建立连接池等
System.out.println("正在初始化数据库连接池: " + url);
// initConnectionPool(url, maxConnections);
}
}4.3 DisposableBean
当 Bean 实现了 DisposableBean 接口,容器在关闭时会调用 destroy() 方法,释放资源。
package org.springframework.beans.factory;
public interface DisposableBean {
/**
* 在 Bean 被销毁时调用。
* 用于释放由 Bean 管理的资源(如连接池、线程池等)。
*/
void destroy() throws Exception;
}4.4 实现示例
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class ConnectionPoolManager implements InitializingBean, DisposableBean {
private int poolSize;
@Override
public void afterPropertiesSet() throws Exception {
// 创建连接池
System.out.println("连接池初始化完成,大小: " + poolSize);
}
@Override
public void destroy() throws Exception {
// 关闭连接池
System.out.println("正在关闭连接池...");
// closeAllConnections();
}
}4.5 源码分析
初始化回调在 AbstractAutowireCapableBeanFactory.invokeInitMethods() 中实现:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 1. 检查是否为 InitializingBean
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 调用 afterPropertiesSet()
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 调用自定义 init-method(如果有且与 afterPropertiesSet 不同)
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName))) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}销毁回调在 DisposableBeanAdapter 中处理:
// org.springframework.beans.factory.support.DisposableBeanAdapter
@Override
public void destroy() {
// 1. 调用 DisposableBean.destroy()
if (this.invokeDisposableBean) {
((DisposableBean) this.bean).destroy();
}
// 2. 调用自定义 destroy-method
if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
}5. @PostConstruct / @PreDestroy 与 InitializingBean / DisposableBean 的执行顺序
5.1 整体初始化顺序
当一个 Bean 同时配置了多种初始化机制时,执行顺序如下:
1. 构造方法(Constructor)
2. BeanNameAware.setBeanName()
3. BeanClassLoaderAware.setBeanClassLoader()
4. BeanFactoryAware.setBeanFactory()
5. ApplicationContextAware.setApplicationContext()
6. BeanPostProcessor.postProcessBeforeInitialization()
└─ 其中包含 @PostConstruct 注解的处理
7. InitializingBean.afterPropertiesSet()
8. 自定义 init-method(@Bean(initMethod="...") 或 XML <bean init-method="...">)
9. BeanPostProcessor.postProcessAfterInitialization()5.2 整体销毁顺序
1. @PreDestroy 注解方法
2. DisposableBean.destroy()
3. 自定义 destroy-method(@Bean(destroyMethod="...") 或 XML <bean destroy-method="...">)5.3 源码验证:@PostConstruct 的处理
@PostConstruct 和 @PreDestroy 由 InitDestroyAnnotationBeanPostProcessor 处理,该 BeanPostProcessor 在 postProcessBeforeInitialization 阶段解析和执行 @PostConstruct 方法:
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
// (简化源码)
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 调用所有 @PostConstruct 标注的方法
metadata.invokeInitMethods(bean, beanName);
} catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "@PostConstruct 方法调用失败", ex.getCause());
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 销毁回调
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 调用所有 @PreDestroy 标注的方法
metadata.invokeDestroyMethods(bean, beanName);
} catch (InvocationTargetException ex) {
throw new BeanDestructionException(beanName, "@PreDestroy 方法调用失败", ex.getCause());
}
}5.4 执行顺序确认示例
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class LifecycleOrderDemo implements InitializingBean, DisposableBean {
public LifecycleOrderDemo() {
System.out.println("1. 构造方法");
}
@PostConstruct
public void postConstruct() {
System.out.println("2. @PostConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("3. afterPropertiesSet()");
}
public void customInit() {
System.out.println("4. custom init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("5. @PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("6. DisposableBean.destroy()");
}
public void customDestroy() {
System.out.println("7. custom destroy-method");
}
}配置:
@Configuration
public class LifecycleConfig {
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleOrderDemo lifecycleOrderDemo() {
return new LifecycleOrderDemo();
}
}启动输出:
1. 构造方法
2. @PostConstruct
3. afterPropertiesSet()
4. custom init-method
// ... 容器运行 ...
5. @PreDestroy
6. DisposableBean.destroy()
7. custom destroy-method6. SmartLifecycle 接口
6.1 Lifecycle 接口
Lifecycle 是最基础的容器生命周期接口,定义了启动和停止的统一契约:
package org.springframework.context;
public interface Lifecycle {
/**
* 启动组件。
* 如果组件已经在运行,则此调用不产生任何效果。
*/
void start();
/**
* 停止组件。
* 如果组件没有在运行,则此调用不产生任何效果。
*/
void stop();
/**
* 检查组件是否正在运行。
*/
boolean isRunning();
}6.2 Phased 接口
Phased 定义了 phase(阶段)值,用于控制多个 Lifecycle 组件的启动和停止顺序:
package org.springframework.context;
public interface Phased {
/**
* 返回此对象的 phase 值。
* 启动时按 phase 值升序执行(小 phase 先启动)。
* 停止时按 phase 值降序执行(大 phase 先停止)。
*/
int getPhase();
}6.3 SmartLifecycle 接口
SmartLifecycle 扩展了 Lifecycle 和 Phased,提供了更精细的生命周期控制:
package org.springframework.context;
public interface SmartLifecycle extends Lifecycle, Phased {
/**
* 返回组件是否应在容器刷新时自动启动。
* 默认实现返回 true。
*/
default boolean isAutoStartup() {
return true;
}
/**
* 在容器关闭时显式请求停止(非回调模式)。
* callback 参数是停止完成后需要调用的 Runnable,用于支持异步停止。
*/
void stop(Runnable callback);
/**
* 返回此组件的 phase 值。
* 默认返回 0。
*/
@Override
default int getPhase() {
return 0;
}
}6.4 SmartLifecycle 启动 / 停止顺序规则
| 阶段 | 行为 |
|---|---|
| 启动(start) | 按 getPhase() 升序执行。即 phase 值小的先启动,Integer.MIN_VALUE 最先启动,Integer.MAX_VALUE 最后启动。 |
| 停止(stop) | 按 getPhase() 降序执行。即 phase 值大的先停止,Integer.MAX_VALUE 最先停止,Integer.MIN_VALUE 最后停止。 |
| 非 SmartLifecycle | 普通 Lifecycle 的 phase 值为默认值 0,在 SmartLifecycle 之后启动 / 之前停止。 |
6.5 完整实现示例
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class CacheManager implements SmartLifecycle {
private volatile boolean running = false;
@Override
public void start() {
System.out.println("缓存管理器启动...");
// 初始化缓存
// initCache();
running = true;
}
@Override
public void stop() {
System.out.println("缓存管理器停止...");
// 清理缓存
// clearCache();
running = false;
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean isAutoStartup() {
return true; // 随容器自动启动
}
@Override
public void stop(Runnable callback) {
// 异步停止,完成后调用 callback
new Thread(() -> {
stop();
callback.run();
}).start();
}
@Override
public int getPhase() {
return Integer.MIN_VALUE; // 最先启动,最后停止
}
}6.6 多组件协作示例
@Component
public class DatabaseHealthChecker implements SmartLifecycle {
private volatile boolean running;
@Override
public void start() {
System.out.println("数据库健康检查器启动 (phase 0)");
running = true;
}
@Override
public void stop() {
System.out.println("数据库健康检查器停止 (phase 0)");
running = false;
}
@Override
public boolean isRunning() { return running; }
@Override
public int getPhase() { return 0; }
@Override
public boolean isAutoStartup() { return true; }
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
}
@Component
public class MessageQueueListener implements SmartLifecycle {
private volatile boolean running;
@Override
public void start() {
System.out.println("消息队列监听器启动 (phase 10)");
running = true;
}
@Override
public void stop() {
System.out.println("消息队列监听器停止 (phase 10)");
running = false;
}
@Override
public boolean isRunning() { return running; }
@Override
public int getPhase() { return 10; }
@Override
public boolean isAutoStartup() { return true; }
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
}
@Component
public class ConfigLoader implements SmartLifecycle {
private volatile boolean running;
@Override
public void start() {
System.out.println("配置加载器启动 (phase -10)");
running = true;
}
@Override
public void stop() {
System.out.println("配置加载器停止 (phase -10)");
running = false;
}
@Override
public boolean isRunning() { return running; }
@Override
public int getPhase() { return -10; }
@Override
public boolean isAutoStartup() { return true; }
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
}执行行为:
启动顺序:
配置加载器 (phase -10) ← 最先启动
数据库健康检查器 (phase 0)
消息队列监听器 (phase 10) ← 最后启动
停止顺序:
消息队列监听器 (phase 10) ← 最先停止
数据库健康检查器 (phase 0)
配置加载器 (phase -10) ← 最后停止6.7 源码分析:SmartLifecycle 的启动触发
SmartLifecycle 的启动由 DefaultLifecycleProcessor 管理,它在容器刷新阶段被调用:
// org.springframework.context.support.DefaultLifecycleProcessor
@Override
public void onRefresh() {
startBeans(true); // 只启动 isAutoStartup() 返回 true 的组件
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
// 1. 收集所有 Lifecycle Bean,按 phase 分组
Map<Integer, LifecycleGroup> phasedGroups = new HashMap<>();
// 对每个 Lifecycle Bean...
int phase = getPhase(lifecycleBean);
LifecycleGroup group = phasedGroups.get(phase);
if (group == null) {
group = new LifecycleGroup();
phasedGroups.put(phase, group);
}
group.add(lifecycleBean);
// 2. 按 phase 升序启动
List<Integer> phases = new ArrayList<>(phasedGroups.keySet());
Collections.sort(phases);
for (Integer phase : phases) {
phasedGroups.get(phase).start();
}
}停止流程在 stop() 方法中,按 phase 降序执行:
// org.springframework.context.support.DefaultLifecycleProcessor
@Override
public void stop() {
// 1. 先停止 SmartLifecycle(异步回调模式)
stopBeans();
// 2. 再停止普通 Lifecycle
this.lifecycleBeans.forEach((beanName, bean) -> {
if (!(bean instanceof SmartLifecycle)) {
bean.stop();
}
});
this.running = false;
}
private void stopBeans() {
// 按 phase 降序排列
List<Integer> phases = new ArrayList<>(this.smartMembers.keySet());
Collections.sort(phases, Collections.reverseOrder());
for (Integer phase : phases) {
// 调用 stop(Runnable callback)
this.smartMembers.get(phase).stop(/* callback */);
}
}7. 实战案例:用 FactoryBean 封装云短信 SDK
7.1 需求场景
假设项目中需要对接阿里云 SMS 和腾讯云 SMS 两家短信服务商。要求:
- 统一调用接口,调用方无需关心底层使用哪家 SDK。
- 短信客户端应复用连接(连接池),而非每次发送都创建新连接。
- 客户端关闭等生命周期行为由容器统一管理。
7.2 统一短信接口
package com.example.sms;
/**
* 统一短信发送接口
*/
public interface SmsSender {
/**
* 发送短信
* @param phoneNumber 手机号
* @param templateCode 模板 code
* @param params 模板参数
* @return 发送结果
*/
SmsResult send(String phoneNumber, String templateCode, String[] params);
/**
* 批量发送
* @param phoneNumbers 手机号列表
* @param templateCode 模板 code
* @param params 模板参数
* @return 发送结果
*/
SmsResult batchSend(String[] phoneNumbers, String templateCode, String[] params);
}
public class SmsResult {
private boolean success;
private String requestId;
private String message;
// 构造方法、getter/setter
public SmsResult(boolean success, String requestId, String message) {
this.success = success;
this.requestId = requestId;
this.message = message;
}
public boolean isSuccess() { return success; }
public String getRequestId() { return requestId; }
public String getMessage() { return message; }
public static SmsResult ok(String requestId) {
return new SmsResult(true, requestId, "发送成功");
}
public static SmsResult fail(String message) {
return new SmsResult(false, null, message);
}
}7.3 阿里云实现
package com.example.sms.aliyun;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.example.sms.SmsResult;
import com.example.sms.SmsSender;
/**
* 阿里云短信实现
*/
public class AliyunSmsSender implements SmsSender {
private final Client client;
private final String signName;
public AliyunSmsSender(Client client, String signName) {
this.client = client;
this.signName = signName;
}
@Override
public SmsResult send(String phoneNumber, String templateCode, String[] params) {
try {
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phoneNumber)
.setSignName(signName)
.setTemplateCode(templateCode);
if (params != null && params.length > 0) {
// 将参数数组转为 JSON 字符串
request.setTemplateParam(toJson(params));
}
SendSmsResponse response = client.sendSms(request);
String code = response.getBody().getCode();
if ("OK".equals(code)) {
return SmsResult.ok(response.getBody().getRequestId());
}
return SmsResult.fail(response.getBody().getMessage());
} catch (Exception e) {
return SmsResult.fail("阿里云短信发送异常: " + e.getMessage());
}
}
@Override
public SmsResult batchSend(String[] phoneNumbers, String templateCode, String[] params) {
return send(String.join(",", phoneNumbers), templateCode, params);
}
private String toJson(String[] params) {
StringBuilder sb = new StringBuilder("{");
for (int i = 0; i < params.length; i++) {
if (i > 0) sb.append(",");
sb.append("\"").append("param").append(i + 1).append("\":\"")
.append(params[i]).append("\"");
}
sb.append("}");
return sb.toString();
}
}7.4 腾讯云实现
package com.example.sms.tencent;
import com.example.sms.SmsResult;
import com.example.sms.SmsSender;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
/**
* 腾讯云短信实现
*/
public class TencentSmsSender implements SmsSender {
private final SmsClient client;
private final String appId;
private final String signName;
public TencentSmsSender(SmsClient client, String appId, String signName) {
this.client = client;
this.appId = appId;
this.signName = signName;
}
@Override
public SmsResult send(String phoneNumber, String templateCode, String[] params) {
try {
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumberSet(new String[]{"+86" + phoneNumber});
request.setTemplateId(templateCode);
request.setSignName(signName);
request.setSmsSdkAppId(appId);
request.setTemplateParamSet(params);
SendSmsResponse response = client.SendSms(request);
String sendStatus = response.getSendStatusSet()[0].getCode();
if ("Ok".equals(sendStatus)) {
return SmsResult.ok(response.getSendStatusSet()[0].getSerialNo());
}
return SmsResult.fail(response.getSendStatusSet()[0].getMessage());
} catch (TencentCloudSDKException e) {
return SmsResult.fail("腾讯云短信发送异常: " + e.getMessage());
}
}
@Override
public SmsResult batchSend(String[] phoneNumbers, String templateCode, String[] params) {
try {
SendSmsRequest request = new SendSmsRequest();
String[] phonesWithPrefix = new String[phoneNumbers.length];
for (int i = 0; i < phoneNumbers.length; i++) {
phonesWithPrefix[i] = "+86" + phoneNumbers[i];
}
request.setPhoneNumberSet(phonesWithPrefix);
request.setTemplateId(templateCode);
request.setSignName(signName);
request.setSmsSdkAppId(appId);
request.setTemplateParamSet(params);
SendSmsResponse response = client.SendSms(request);
return SmsResult.ok(response.getSendStatusSet()[0].getSerialNo());
} catch (TencentCloudSDKException e) {
return SmsResult.fail("腾讯云短信批量发送异常: " + e.getMessage());
}
}
}7.5 FactoryBean 封装:阿里云 SMS
package com.example.sms.aliyun;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.teaopenapi.models.Config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* 阿里云短信客户端的 FactoryBean。
* 负责创建 AliyunSmsSender,并管理底层 Client 的生命周期。
*/
public class AliyunSmsFactoryBean implements FactoryBean<SmsSender>, InitializingBean {
// ===== 由 Spring 注入的配置属性 =====
private String accessKeyId;
private String accessKeySecret;
private String endpoint = "dysmsapi.aliyuncs.com";
private String signName;
// ===== 内部状态 =====
private Client aliyunClient;
private SmsSender smsSender;
// ===== 配置属性的 setter =====
public void setAccessKeyId(String accessKeyId) { this.accessKeyId = accessKeyId; }
public void setAccessKeySecret(String accessKeySecret) { this.accessKeySecret = accessKeySecret; }
public void setEndpoint(String endpoint) { this.endpoint = endpoint; }
public void setSignName(String signName) { this.signName = signName; }
@Override
public void afterPropertiesSet() throws Exception {
// 1. 验证必要配置
if (accessKeyId == null || accessKeySecret == null) {
throw new IllegalArgumentException("阿里云 SMS 的 accessKeyId 和 accessKeySecret 不能为空");
}
if (signName == null) {
throw new IllegalArgumentException("阿里云 SMS 的 signName 不能为空");
}
// 2. 创建阿里云 SDK Client(可复用连接)
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret)
.setEndpoint(endpoint);
this.aliyunClient = new Client(config);
// 3. 创建 SmsSender 包装类
this.smsSender = new AliyunSmsSender(aliyunClient, signName);
}
@Override
public SmsSender getObject() throws Exception {
return smsSender;
}
@Override
public Class<?> getObjectType() {
return SmsSender.class;
}
@Override
public boolean isSingleton() {
return true; // 复用同一个 Client 实例
}
}7.6 FactoryBean 封装:腾讯云 SMS
package com.example.sms.tencent;
import com.example.sms.SmsSender;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* 腾讯云短信客户端的 FactoryBean。
* 负责创建 TencentSmsSender,并管理底层 Client 的生命周期。
*/
public class TencentSmsFactoryBean implements FactoryBean<SmsSender>, InitializingBean, DisposableBean {
// ===== 由 Spring 注入的配置属性 =====
private String secretId;
private String secretKey;
private String appId;
private String signName;
private String region = "ap-guangzhou";
// ===== 内部状态 =====
private SmsClient tencentClient;
private SmsSender smsSender;
// ===== 配置属性的 setter =====
public void setSecretId(String secretId) { this.secretId = secretId; }
public void setSecretKey(String secretKey) { this.secretKey = secretKey; }
public void setAppId(String appId) { this.appId = appId; }
public void setSignName(String signName) { this.signName = signName; }
public void setRegion(String region) { this.region = region; }
@Override
public void afterPropertiesSet() throws Exception {
// 1. 验证必要配置
if (secretId == null || secretKey == null) {
throw new IllegalArgumentException("腾讯云 SMS 的 secretId 和 secretKey 不能为空");
}
if (appId == null || signName == null) {
throw new IllegalArgumentException("腾讯云 SMS 的 appId 和 signName 不能为空");
}
// 2. 创建腾讯云 SDK Client
Credential cred = new Credential(secretId, secretKey);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("sms.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
this.tencentClient = new SmsClient(cred, region, clientProfile);
// 3. 创建 SmsSender 包装类
this.smsSender = new TencentSmsSender(tencentClient, appId, signName);
}
@Override
public SmsSender getObject() throws Exception {
return smsSender;
}
@Override
public Class<?> getObjectType() {
return SmsSender.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() throws Exception {
// 释放腾讯云客户端资源(如有需要)
System.out.println("正在关闭腾讯云 SMS 客户端...");
}
}7.7 Spring 配置
package com.example.sms.config;
import com.example.sms.SmsSender;
import com.example.sms.aliyun.AliyunSmsFactoryBean;
import com.example.sms.tencent.TencentSmsFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class SmsConfig {
/**
* 阿里云短信客户端(通过 FactoryBean 创建)
*/
@Bean
@Profile("aliyun")
public AliyunSmsFactoryBean aliyunSmsFactoryBean() {
AliyunSmsFactoryBean factoryBean = new AliyunSmsFactoryBean();
factoryBean.setAccessKeyId("${aliyun.sms.access-key-id}");
factoryBean.setAccessKeySecret("${aliyun.sms.access-key-secret}");
factoryBean.setSignName("${aliyun.sms.sign-name}");
factoryBean.setEndpoint("${aliyun.sms.endpoint:dysmsapi.aliyuncs.com}");
return factoryBean;
}
/**
* 实际注入的 SmsSender——注意这里注入的是 getObject() 返回的对象
*/
@Bean
@Profile("aliyun")
public SmsSender aliyunSmsSender() throws Exception {
// aliyunSmsFactoryBean() 返回的是 AliyunSmsFactoryBean 实例本身
// Spring 会自动识别 FactoryBean,因此 getBean("aliyunSmsSender") 会调用 getObject()
// 但这里我们显式调用 getObject() 获取 SmsSender
return aliyunSmsFactoryBean().getObject();
}
/**
* 腾讯云短信客户端(通过 FactoryBean 创建)
*/
@Bean
@Profile("tencent")
public TencentSmsFactoryBean tencentSmsFactoryBean() {
TencentSmsFactoryBean factoryBean = new TencentSmsFactoryBean();
factoryBean.setSecretId("${tencent.sms.secret-id}");
factoryBean.setSecretKey("${tencent.sms.secret-key}");
factoryBean.setAppId("${tencent.sms.app-id}");
factoryBean.setSignName("${tencent.sms.sign-name}");
factoryBean.setRegion("${tencent.sms.region:ap-guangzhou}");
return factoryBean;
}
@Bean
@Profile("tencent")
public SmsSender tencentSmsSender() throws Exception {
return tencentSmsFactoryBean().getObject();
}
}7.8 使用方代码
package com.example.sms.service;
import com.example.sms.SmsResult;
import com.example.sms.SmsSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
@Autowired
private SmsSender smsSender; // 具体是阿里云还是腾讯云,由 profile 决定
public void sendVerificationCode(String phoneNumber, String code) {
String[] params = {code, "5"};
SmsResult result = smsSender.send(phoneNumber, "SMS_TEMPLATE_CODE", params);
if (result.isSuccess()) {
System.out.println("验证码发送成功,requestId: " + result.getRequestId());
} else {
System.err.println("验证码发送失败: " + result.getMessage());
}
}
}7.9 总结:该方案的优势
| 特性 | 实现方式 |
|---|---|
| 统一抽象 | SmsSender 接口屏蔽了不同云厂商的 SDK 差异 |
| 连接复用 | FactoryBean.isSingleton() 返回 true,SDK Client 全局复用,避免频繁创建连接 |
| 配置外部化 | InitializingBean.afterPropertiesSet() 负责验证配置完整性,配置值通过 Placeholder 注入 |
| 资源释放 | DisposableBean.destroy() 在容器关闭时清理 SDK 客户端资源 |
| 多环境切换 | 通过 @Profile 区分阿里云/腾讯云环境,零代码切换 |
| 延迟初始化 | FactoryBean 本身由容器管理,在首次注入时才创建 SDK Client |
总结
Spring Framework 提供的工厂与回调接口构成了一个完善的 Bean 生命周期管理体系:
| 接口 | 回调时机 | 典型用途 |
|---|---|---|
FactoryBean | Bean 创建阶段 | 封装复杂对象的创建逻辑 |
BeanNameAware | 属性填充后 | 获取 Bean 在容器中的注册名 |
BeanFactoryAware | 属性填充后 | 编程式访问 BeanFactory |
ApplicationContextAware | 属性填充后 | 获取完整容器上下文 |
InitializingBean | 属性填充 + Aware 回调后 | 执行初始化验证和资源准备 |
DisposableBean | 容器关闭时 | 释放资源 |
@PostConstruct | 在 InitializingBean 之前 | 声明式初始化方法 |
@PreDestroy | 在 DisposableBean 之前 | 声明式销毁方法 |
SmartLifecycle | 容器刷新完成后 / 关闭时 | 控制组件的启动/停止顺序 |
理解这些接口的执行顺序与协作方式,能够帮助开发者编写出更加健壮、可维护的 Spring 应用程序,尤其是在处理第三方 SDK 集成、连接池管理、多阶段初始化等复杂场景时,能够充分利用 Spring 容器提供的能力。