目录
1、实现 InitializingBean 接口和 DisposableBean 接口
2、使用 @Bean 注解的 initMethod/destroyMethod 属性
3、使用 @PostConstruct/@PreDestroy 注解
实现 Spring Bean 的生命周期的回调,有以下几种方法
- 实现 InitializingBean 接口重写其 afterPropertiesSet() 方法,实现 DisposableBean 接口重写 destroy() 方法
- 在配置类中使用 @Bean(initMethod = "init",destroyMethod = "destory") 注解指定
- 利用 java 的 JSR250 规范中的 @PostConstruct 标注在 init 方法上,@PreDestroy 标注在 destroy 方法上
对以上几种方式,下边将进行一一介绍
1、实现 InitializingBean 接口和 DisposableBean 接口
InitializingBean 接口为 bean 提供了初始化方法的方式,它只包括 afterPropertiesSet() 方法,凡是继承该接口的类,在初始化 bean 的时候都会执行该方法。// 准确的来说是属性赋值后。
实体类实现 InitializingBean 接口,如下:
@Component
public class TestInitializingBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("test InitializingBean");
}
public void initializingBeanHello(){
System.out.println("Hello InitializingBean");
}
}
测试方法
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class InitializingBeanTest {
@Autowired
TestInitializingBean testInitializingBean;
@Test
public void testInit() {
System.out.println("InitializingBeanTest...");
testInitializingBean.initializingBeanHello();
}
}
测试结果为:
2、使用 @Bean 注解的 initMethod/destroyMethod 属性
initMethod: 这个可选择的方法在 bean 实例化的时候调用,InitializationBean 接口允许 bean 在合适的时机通过设置注解的初始化属性从而调用初始化方法。
destroyMethod: 方法的可选择名称在调用 bean 示例在关闭上下文的时候,例如 JDBC 的 close() 方法,或者 SqlSession 的 close() 方法。
测试实例
public class Instance {
public Instance() {
System.out.println("Instance's Constructor..");
}
public void init(){
System.out.println("Instance's Init...");
}
public void destroy(){
System.out.println("Instance's Destroy...");
}
public void helloWorld(){
System.out.println("Instance's helloWorld...");
}
}
配置类
@Configuration
public class InstanceConfiguration {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Instance getInstance() {
return new Instance();
}
}
测试方法
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class InitializingBeanTest {
@Autowired
Instance instance;
@Test
public void testInit() {
System.out.println("InitializingBeanTest...");
instance.helloWorld();
}
}
测试结果:
Spring不推荐使用InitializationBean 来调用其初始化方法,因为它不必要地将代码耦合到Spring。Spring推荐使用 @PostConstruct 注解或者为 POJO 类指定其初始化方法这两种方式来完成初始化。
3、使用 @PostConstruct/@PreDestroy 注解
@PostConstruct 注解好多人以为是Spring提供的。其实是Java自己的注解。
Java中该注解的说明:
@PostConstruct 该注解被用来修饰一个非静态的 void() 方法。被 @PostConstruct 修饰的方法会在服务器加载 Servlet 的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init() 方法之前执行。
通常我们会是在Spring框架中使用到 @PostConstruct 注解 该注解的方法在整个 Bean 初始化中的执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
测试实例
@Component
public class Instance {
public Instance() {
System.out.println("Instance's Constructor..");
}
@PostConstruct // 使用java注解
public void PostConstruct(){
System.out.println("Instance's PostConstruct...");
}
public void helloWorld(){
System.out.println("Instance's helloWorld...");
}
}
测试结果:
如果三种方法一起配置,那么会得到什么结果呢?
测试方法
public class Instance implements InitializingBean {
public Instance() {
System.out.println("Instance's Constructor..");
}
public void init(){
System.out.println("Instance's Init...");
}
public void destroy(){
System.out.println("Instance's Destroy...");
}
@PostConstruct // 使用java注解
public void PostConstruct(){
System.out.println("Instance's PostConstruct...");
}
public void helloWorld(){
System.out.println("Instance's helloWorld...");
}
@Override // 实现InitializingBean接口
public void afterPropertiesSet() throws Exception {
System.out.println("Instance's InitializingBean...");
}
}
执行结果:
从结果可以看出,在Spring初始化bean的时候,如果该 bean 实现了 InitializingBean 接口,并且同时在配置文件中指定了 init-method,系统则是先调用 afterPropertieSet() 方法,然后再调用 init-method 中指定的方法。(此处忽略了@PostConstruct,请自行探索,或待后续更新)
那么这种方式在spring中是怎么实现的呢,通过查看 Spring 加载 bean 的源码类AbstractAutowireCapableBeanFactory,可以看出其中的奥妙,AbstractAutowireCapableBeanFactory 类中的 invokeInitMethods 说的非常清楚,如下:
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
// 判断该bean是否实现了实现了InitializingBean接口,
// 如果实现了InitializingBean接口,则只调用bean的afterPropertiesSet方法
boolean isInitializingBean = bean instanceof InitializingBean;
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
//直接调用afterPropertiesSet
AccessController.doPrivileged(() -> {
((InitializingBean)bean).afterPropertiesSet();
return null;
}, this.getAccessControlContext());
} catch (PrivilegedActionException var6) {
throw var6.getException();
}
} else {
//直接调用afterPropertiesSet
((InitializingBean)bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
//判断是否指定了init-method方法,
//如果指定了init-method方法,则再调用制定的init-method
if (StringUtils.hasLength(initMethodName) && (!isInitializingBean || !"afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) {
//进一步查看该方法的源码,
//可以发现init-method方法中指定的方法是通过反射实现
this.invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
总结:
- Spring 为 bean 提供了两种初始化 bean 的方式,实现 InitializingBean 接口,实现afterPropertiesSet 方法,或者在配置文件中通过 init-method 指定,两种方式可以同时使用。
- 实现 InitializingBean 接口是直接调用 afterPropertiesSet 方法,比通过反射调用 init-method 指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
- 如果调用 afterPropertiesSet() 方法时出错,则不调用 init-method 指定的方法。
关于 Spring Bean 生命周期的回调,这篇文章会讲得更加详细。