2024-12-18  阅读(69)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/7134283980

回答

在 Spring 中,@PostConstructinit-methodafterPropertiesSet 都是用于在 Bean 初始化阶段执行自定义初始化逻辑的方法。他们的执行顺序如下:

  • @PostConstruct:首先是 @PostConstruct 执行。@PostConstruct 属于 JSR-250 标准注解,为 Java EE 标准中的注解。Spring 管理 Bean 的生命周期时会自动识别 @PostConstruct,并在属性注入完成之后执行。所以,@PostConstruct 执行时机比其他 Spring 专有的初始化方法更早。
  • afterPropertiesSet:是 Spring 专门为 Bean 的初始化设计的一个方法,定义在 InitializingBean 接口中,用来定义一些初始化逻辑。Spring 在创建 Bean 的过程中会检查 Bean 是否实现了 InitializingBean 接口,如果实现了则在 @PostConstruct 方法执行完毕后调用 afterPropertiesSet 方法。
  • init-method:在 Spring 配置文件(如 XML 配置)或者 @Bean 注解中指定的方法,该方法会在前两个初始化步骤执行完毕后调用。

所以,三者的执行顺序为:@PostConstruct > InitializingBean > init-method

详解

使用一个例子来看看:

public class BeanInitTest implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet 执行");
    }

    @PostConstruct
    public void postConstructMethod() {
        System.out.println("@PostConstruct 执行");
    }

    public void initMethod(){
        System.out.println("init-method 执行");
    }

    @Configuration
    static class InitConfiguration {
        @Bean(initMethod = "initMethod")
        public BeanInitTest getInitMethodBean() {
            return new BeanInitTest();
        }
    }
}

然后启动下应用程序,查看执行结果:

我们再从源码层次来看,通过 debug 的跟踪,可以发现这几个初始化的方法都是在 AbstractAutowireCapableBeanFactory#initializeBean() 中:

  protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
        invokeAwareMethods(beanName, bean);
        return null;
      }, getAccessControlContext());
    }
    else {
      invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
      // 这里是执行 @PostConstruct 的方法
      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;
  }

从这个方法可以看出,先执行 @PostConstruct 的方法,然后调用 invokeInitMethods() 执行afterPropertiesSetinit-method

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

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
      if (logger.isTraceEnabled()) {
        logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
        try {
          AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
            // 执行 afterPropertiesSet
            ((InitializingBean) bean).afterPropertiesSet();
            return null;
          }, getAccessControlContext());
        }
        catch (PrivilegedActionException pae) {
          throw pae.getException();
        }
      }
      else {
          // 执行 afterPropertiesSet
        ((InitializingBean) bean).afterPropertiesSet();
      }
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
      // 执行 init-method 
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
          !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
          !mbd.isExternallyManagedInitMethod(initMethodName)) {
        invokeCustomInitMethod(beanName, bean, mbd);
      }
    }
  }

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文