Spring Bean 的生命周期回调方法示例

目录

1、实现 InitializingBean 接口和 DisposableBean 接口

2、使用 @Bean 注解的 initMethod/destroyMethod 属性

3、使用 @PostConstruct/@PreDestroy 注解


实现 Spring Bean 的生命周期的回调,有以下几种方法

  1. 实现 InitializingBean 接口重写其 afterPropertiesSet() 方法,实现 DisposableBean 接口重写 destroy() 方法
  2. 在配置类中使用 @Bean(initMethod = "init",destroyMethod = "destory") 注解指定
  3. 利用 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);
            }
        }
    }

总结:

  1. Spring 为 bean 提供了两种初始化 bean 的方式,实现 InitializingBean 接口,实现afterPropertiesSet 方法,或者在配置文件中通过 init-method 指定,两种方式可以同时使用。
  2. 实现 InitializingBean 接口是直接调用 afterPropertiesSet 方法,比通过反射调用 init-method 指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
  3. 如果调用 afterPropertiesSet() 方法时出错,则不调用 init-method 指定的方法。

关于 Spring Bean 生命周期的回调,这篇文章会讲得更加详细。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值