• Java面试题:@PostConstruct、init-method和afterPropertiesSet执行顺序?


    在Spring框架中,@PostConstruct注解、init-method属性、以及afterPropertiesSet()方法通常用于初始化Bean的逻辑。它们都提供了在Bean创建和初始化完成后执行的方法,但执行顺序有所不同。

    想要知道@PostConstruct、init-method、afterPropertiesSet()的执行顺序,只要搞明白它们各自在什么时候被谁调用就行了。

    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import org.springframework.beans.factory.InitializingBean;
    import javax.annotation.PostConstruct;
     
    public class Foo implements InitializingBean {
     
        public void init(){
            System.out.println("执行了init生命周期的初始化回调");
        }
     
        @PostConstruct
        public void postConstruct(){
            System.out.println("执行了postConstruct生命周期的初始化回调");
        }
     
        @Override
        public void afterPropertiesSet() {
            System.out.println("执行了afterPropertiesSet生命周期的初始化回调");
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    @Configuration
    public class InitConfiguration {
        @Bean(initMethod = "init")
        public Foo getInitMethodBean() {
            return new Foo();
        }
    }

    执行启动类,可以看到在控制台中输出:

    1
    2
    3
    执行了postConstruct生命周期的初始化回调
    执行了afterPropertiesSet生命周期的初始化回调
    执行了init生命周期的初始化回调

    @PostConstruct是Java EE 5引入的一个注解,它用于标记一个方法,该方法会在依赖注入完成后自动执行。这意味着,一旦Spring容器完成了Bean的实例化和属性赋值,就会调用这个方法。通常,我们会在这个方法中做一些初始化工作,比如启动服务、初始化数据库连接等。

    init-method属性是Spring Bean的一个属性,它允许我们指定一个初始化方法。这个方法会在Bean实例化并完成属性注入后自动执行。与@PostConstruct注解不同的是,init-method属性并不依赖于Spring容器,因此可以在没有Spring的环境中运行。

    afterPropertiesSet是SpringFramework中的一个初始化方法,它属于 InitializingBean接口的一部分。当bean的所有属性被Spring容器设置之后,这个方法会被自动调用。它允许开发者在bean属性设置完成之后执行一些特定的操作,如数据库连接池的初始化等。这个方法是在执行其他初始化方法之前被调用的。

    源码分析:

    通过断点调试发现几个初始化方法都定位到AbstractAutowireCapableBeanFactory的initializeBean方法中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
          AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
              invokeAwareMethods(beanName, bean);
              return null;
            }
          }, getAccessControlContext());
        }
        else {
          invokeAwareMethods(beanName, bean);
        }
     
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
          // 此处执行的是@PostConstruct注解的方法 InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }
     
        try {
          // 执行的是afterPropertiesSet和init-method方法     
          invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
          throw new BeanCreationException(
              (mbd != null ? mbd.getResourceDescription() : null),
              beanName, "Invocation of init method failed", ex);
        }
     
        if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
      }

    执行afterPropertiesSet和init-method方法,在invokeInitMethods方法里面,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
          throws Throwable {
     
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
          if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
          }
          if (System.getSecurityManager() != null) {
            try {
              AccessController.doPrivileged(new PrivilegedExceptionAction() {
                @Override
                public Object run() throws Exception {
                  ((InitializingBean) bean).afterPropertiesSet();
                  return null;
                }
              }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
              throw pae.getException();
            }
          }
          else {
            // 执行afterPropertiesSet方法
            ((InitializingBean) bean).afterPropertiesSet();
          }
        }
     
        if (mbd != null) {
          String initMethodName = mbd.getInitMethodName();
          if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
              !mbd.isExternallyManagedInitMethod(initMethodName)) {
            // 执行自定义的init-method方法
            invokeCustomInitMethod(beanName, bean, mbd);
          }
        }
      }

     

    最终的结论是:@PostConstruct > afterPropertiesSet() > initMethod()的顺序

     

    往期面试题:

    Java面试题:SimpleDateFormat是线程安全的吗?使用时应该注意什么?

    Java面试题:细数ThreadLocal大坑,内存泄露本可避免

    Java面试题:请谈谈对ThreadLocal的理解?

    Java面试题:为什么HashMap不建议使用对象作为Key?

    Java面试题:你知道Spring的IOC吗?那么,它为什么这么重要呢?

     

  • 相关阅读:
    FastDFS在centos7上的配置
    【Linux修炼】开篇
    JavaScript 里的 Promise Chaining
    【GIS面试】GIS算法介绍
    Photoshop利用置换滤镜制作文字人像
    springboot使用flyway,使用介绍、个人总结及报错场景如何修改
    LVGL---基础对象的事件(events)
    6.MidBook项目经验之前端nuxt优化SEO和手机登录,微信登录
    echarts+node+ajax实现时间天气服务器
    iOS报错命名空间“std”中的“unary_function”
  • 原文地址:https://www.cnblogs.com/marsitman/p/18184050