一、程序世界的生死轮回
就像你刚入职时需要办理门禁卡、领取办公设备,离职时要交接工作一样,每个Spring Bean在容器中也有自己的生命周期。这些看似不起眼的初始化和销毁操作,实际上直接影响着系统的健壮性——你可能遇到过数据库连接忘记释放导致连接池耗尽,或是缓存预热不当引发的冷启动性能问题。
二、启动准备的三板斧
2.1 注解派的优雅之道
@Service
public class CoffeeService {
private Map<String, Integer> menu;
// 就像咖啡师上班后要磨豆子
@PostConstruct
public void grindBeans() {
menu = new ConcurrentHashMap<>();
menu.put("美式", 20);
menu.put("拿铁", 25);
System.out.println(">>>> 菜单初始化完成");
}
// 此处省略业务方法...
}
// 测试类片段
@SpringBootTest
public class CoffeeTest {
@Autowired
private CoffeeService coffeeService;
@Test
void contextLoads() {
// 测试时会自动触发初始化
}
}
2.2 接口派的直球对决
@Component
public class CacheLoader implements InitializingBean {
private Map<String, Object> localCache;
@Override
public void afterPropertiesSet() throws Exception {
localCache = new LinkedHashMap<String, Object>() {
{
put("configA", loadFromDB("A"));
put("configB", loadFromDB("B"));
}
};
System.out.println(">>>> 缓存预热完成");
}
private Object loadFromDB(String key) {
// 模拟数据库读取...
return new Object();
}
}
2.3 XML配置的老兵坚守
<bean id="databaseConnector"
class="com.example.DatabaseConnector"
init-method="connect">
</bean>
对应的Java类中需要定义名为connect的初始化方法,方法与注解方案中的磨豆子方法类似。
三、善后处理的三种方式
3.1 优雅谢幕的注解方案
@Service
public class FileService {
private BufferedWriter logWriter;
@PreDestroy
public void closeResources() throws IOException {
if (logWriter != null) {
logWriter.flush();
logWriter.close();
System.out.println(">>>> 日志文件句柄已释放");
}
}
// 省略其他方法...
}
3.2 接口终结者
@Component
public class MessageConsumer implements DisposableBean {
private RabbitMQConnection connection;
@Override
public void destroy() throws Exception {
if (connection != null && connection.isOpen()) {
connection.close();
System.out.println(">>>> MQ连接已正常关闭");
}
}
}
3.3 XML的落幕配置
<bean id="resourcePool"
class="com.example.ResourcePool"
destroy-method="cleanup">
</bean>
四、生命周期背后的操盘手(BeanPostProcessor)
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("准备初始化:" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("完成初始化:" + beanName);
if(bean instanceof CoffeeService){
((CoffeeService) bean).displayMenu();
}
return bean;
}
}
五、真实世界的战斗位置
- 数据库连接池:初始化时建立最小连接数,销毁时释放所有连接
- 定时任务管理:初始化加载任务配置,销毁时关闭调度器
- 分布式锁管理:销毁时自动释放持有的锁
六、选择困难症的解药
方案类型 | 耦合度 | 灵活性 | 侵入性 | 维护成本 |
---|---|---|---|---|
注解方案 | 低 | 高 | 低 | ★★☆ |
接口实现方案 | 高 | 低 | 高 | ★★★ |
XML配置方案 | 中 | 中 | 无 | ★★☆ |
七、血的教训总结
- 执行顺序陷阱:接口方法 > 注解方法 > XML方法
- 异常传播:初始化方法抛出异常会导致Bean创建失败
- 原型Bean的特殊性:容器不管理原型Bean的销毁方法
- 上下文关闭的正确姿势:推荐使用ConfigurableApplicationContext#close()
八、智慧结晶
在微服务架构实践中,推荐采用@PostConstruct
+@PreDestroy
的组合拳,既能保持代码整洁又便于维护。对于遗留系统改造,XML配置方案是平稳过渡的利器。别忘了用BeanPostProcessor实现AOP式的统一管理。