1. 引言
本文将深入探讨Spring框架如何处理原型bean及其生命周期管理。掌握bean作用域的使用技巧是开发中必备的实用技能。我们将重点分析:是否需要手动销毁原型bean、何时需要以及具体实现方法。
尽管Spring提供了多种bean作用域,但本文将聚焦于原型作用域。
2. 原型bean及其生命周期
作用域决定了bean的生命周期和可见性。根据定义的作用域,IoC容器负责管理bean的生命周期。原型作用域规定:每次通过getBean()请求或注入其他bean时,容器都会创建一个全新的bean实例。 对于创建和初始化阶段,我们可以完全信任Spring。但销毁流程则完全不同。
在讨论销毁必要性前,先看如何定义原型bean:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeExample {
}
3. 原型bean需要手动销毁吗?
Spring不会自动销毁原型bean。与单例作用域(IoC容器管理完整生命周期)不同,原型bean在实例化、配置和组装后,容器就不再跟踪其状态。
在Java中,当对象不再被任何引用可达时,就会成为垃圾回收的目标。通常让原型bean实例在使用后被垃圾回收器处理就足够了。 简单说:大多数场景下无需手动销毁原型bean。
但需要警惕这些场景:当bean处理文件、数据库连接或网络等资源时。由于原型作用域每次使用都会创建新实例,意味着资源会被持续消耗。长期积累可能导致内存泄漏或连接池耗尽等问题。 因为我们从未释放这些资源,只是不断创建新实例。
⚠️ 踩坑提醒:使用原型bean时,必须确保在使用后正确销毁,关闭所有创建或使用的资源。
4. 如何销毁原型bean?
Spring提供了多种手动销毁bean的方式。注意:如果同时使用多种机制,容器会依次执行它们,但至少需选择一种。
*所有示例(除自定义方法外)都需要手动调用BeanFactory的destroyBean()方法**。我们从ApplicationContext获取BeanFactory*并执行销毁:
applicationContext.getBeanFactory().destroyBean(prototypeBean);
4.1. 使用*@PreDestroy*注解
@PreDestroy注解用于标记bean的销毁方法。该方法必须无参数且非静态:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PreDestroyBeanExample {
@PreDestroy
private void destroy() {
// 释放bean持有的所有资源
}
}
4.2. DisposableBean接口
DisposableBean接口包含唯一的回调方法*destroy()*。Spring官方不推荐此方式,因为它会强耦合代码到Spring框架。实现示例:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DisposableBeanExample implements DisposableBean {
@Override
public void destroy() {
// 释放bean持有的所有资源
}
}
4.3. DestructionAwareBeanPostProcessor接口
与其他BeanPostProcessor变体类似,DestructionAwareBeanPostProcessor可定制bean的初始化逻辑。关键区别在于它增加了在销毁前执行自定义逻辑的方法。
实现步骤:
- 确保bean有资源释放机制(如前例的DisposableBean或自定义方法)
- 实现接口并调用销毁方法:
@Component
public class CustomPostProcessor implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (bean instanceof PostProcessorBeanExample) {
((PostProcessorBeanExample) bean).destroy();
}
}
}
4.4. POJO的自定义方法
当需要将POJO定义为原型bean时,可在定义时通过destroyMethod属性指定销毁方法:
public class CustomMethodBeanExample {
public void destroy() {
// 释放bean持有的所有资源
}
}
@Configuration
public class DestroyMethodConfig {
@Bean(destroyMethod = "destroy")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public CustomMethodBeanExample customMethodBeanExample() {
return new CustomMethodBeanExample();
}
}
⚠️ 重要提醒:虽然我们成功标记了自定义销毁方法,但容器永远不会自动调用它! 因为容器只会对完全控制生命周期的bean执行销毁回调。这种场景下,需结合DestructionAwareBeanPostProcessor使用,或在使用原型bean后直接调用自定义销毁方法。
5. 总结
本文分析了Spring如何处理原型bean的初始化,却将销毁责任交给客户端。
虽然多数情况下无需手动销毁原型bean,但当它们处理文件、数据库连接或网络等资源时,强烈建议手动销毁。 因为每次请求都会创建新实例,资源消耗会迅速累积。为避免内存泄漏等问题,必须主动释放资源。
我们学习了四种销毁方式:*@PreDestroy注解、DisposableBean接口、DestructionAwareBeanPostProcessor接口和自定义方法。简单粗暴的选择建议:优先使用@PreDestroy,避免Spring强耦合;复杂场景可考虑DestructionAwareBeanPostProcessor*。
完整代码示例请查看GitHub仓库。