Spring Bean 初始化之InitializingBean, init-method 和 PostConstruct

概述

从接口的名字上不难发现,InitializingBean 的作用就是在 bean 初始化后执行定制化的操作。

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:

  • 通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
  • 通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 /销毁之前调用的操作方法;
  • 在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

注:以下源码分析基于spring 5.0.4

InitializingBean vs init-method

接口定义如下:

    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }

接口只有一个方法afterPropertiesSet,此方法的调用入口是负责加载 spring bean 的AbstractAutowireCapableBeanFactory,源码如下:

         protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
                throws Throwable {

            boolean isInitializingBean = (bean instanceof InitializingBean);
            if (isInitializingBean &amp;&amp; (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
                }
                if (System.getSecurityManager() != null) {
                    try {
                        AccessController.doPrivileged((PrivilegedExceptionAction&lt;Object&gt;) () -&gt; {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }, getAccessControlContext());
                    }
                    catch (PrivilegedActionException pae) {
                        throw pae.getException();
                    }
                }
                else {
                    ((InitializingBean) bean).afterPropertiesSet();
                }
            }

从这段源码可以得出以下结论:

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

@PostContruct

通过 debug 和调用栈找到类InitDestroyAnnotationBeanPostProcessor, 其中的核心方法,即 @PostConstruct 方法调用的入口:

     @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
            try {
                metadata.invokeInitMethods(bean, beanName);
            }
            catch (InvocationTargetException ex) {
                throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
            }
            return bean;
        }

从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。想到了什么?在也谈Spring容器的生命周期中,提到过BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被调用的。另外通过跟踪,@PostConstruct方法的调用方式也是通过发射机制。

总结

  1. spring bean的初始化执行顺序:构造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具体可以参考例子
  2. afterPropertiesSet通过接口实现方式调用(效率上高一点),@PostConstructinit-method都是通过反射机制调用

例子

直接执行单测com.skyarthur.springboot.common.bean.InitSequenceBeanTest, 请戳代码下载地址

核心代码如下:

@Slf4j
 public class InitSequenceBean implements InitializingBean {

     public InitSequenceBean() {
         log.info("InitSequenceBean: construct");
     }

     @Override
     public void afterPropertiesSet() throws Exception {
         log.info("InitSequenceBean: afterPropertiesSet");
     }

     @PostConstruct
     public void postConstruct() {
         log.info("InitSequenceBean: postConstruct");
     }

     public void initMethod() {
         log.info("InitSequenceBean: initMethod");
     }
 }

 @Configuration
 public class SystemConfig {

     @Bean(initMethod = "initMethod", name = "initSequenceBean")
     public InitSequenceBean initSequenceBean() {
         return new InitSequenceBean();
     }
 }

 @Slf4j
 public class InitSequenceBeanTest extends ApplicationTests {

     @Autowired
     private InitSequenceBean initSequenceBean;

     @Test
     public void initSequenceBeanTest() {
         log.info("Finish: {}", initSequenceBean.toString());
     }
 }

转载自:https://segmentfault.com/a/1190000014105687

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值