在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嗎?那麼,它為什麼這麼重要呢?