一、程序世界的生死轮回

就像你刚入职时需要办理门禁卡、领取办公设备,离职时要交接工作一样,每个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配置方案 ★★☆

七、血的教训总结

  1. 执行顺序陷阱:接口方法 > 注解方法 > XML方法
  2. 异常传播:初始化方法抛出异常会导致Bean创建失败
  3. 原型Bean的特殊性:容器不管理原型Bean的销毁方法
  4. 上下文关闭的正确姿势:推荐使用ConfigurableApplicationContext#close()

八、智慧结晶

在微服务架构实践中,推荐采用@PostConstruct+@PreDestroy的组合拳,既能保持代码整洁又便于维护。对于遗留系统改造,XML配置方案是平稳过渡的利器。别忘了用BeanPostProcessor实现AOP式的统一管理。