在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面試題:爲什麼HashMap不建議使用對象作爲Key?
Java面試題:你知道Spring的IOC嗎?那麼,它爲什麼這麼重要呢?