Java面試題:@PostConstruct、init-method和afterPropertiesSet執行順序?

在Spring框架中,@PostConstruct註解、init-method屬性、以及afterPropertiesSet()方法通常用於初始化Bean的邏輯。它們都提供了在Bean創建和初始化完成後執行的方法,但執行順序有所不同。

想要知道@PostConstruct、init-method、afterPropertiesSet()的執行順序,只要搞明白它們各自在什麼時候被誰調用就行了。

代碼如下:

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生命週期的初始化回調");
    }
}
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();
    }
}

執行啓動類,可以看到在控制檯中輸出:

執行了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方法中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @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方法裏面,如下:

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<Object>() {
            @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嗎?那麼,它爲什麼這麼重要呢?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章