關於@PostConstruct、afterPropertiesSet和init-method的執行順序
@PostConstruct、init-method、afterPropertiesSet() 執行順序
想要知道 @PostConstruct、init-method、afterPropertiesSet() 的執行順序,隻要搞明白它們各自在什麼時候被誰調用就行瞭。
程序版本:Spring Boot 2.3.5.RELEASE
準備好要驗證的材料:
public class Foo implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet()"); } @PostConstruct public void init() { System.out.println("@PostConstruct"); } private void initMethod() { System.out.println("initMethod()"); } }
@Configuration public class FooConfiguration { @Bean(initMethod = "initMethod") public Foo foo() { return new Foo(); } }
執行啟動類,可以看到在控制臺中輸出:
@PostConstruct
afterPropertiesSet()
initMethod()
說明執行順序是:@PostConstruct、afterPropertiesSet()、init-method
接下來將跟著源碼來瞭解為什麼是這個順序。
@PostConstruct 標註的方法在何時被誰調用
首先,在 init() 中打個斷點,然後以 debug 的方式啟動項目,得到下面的調用棧:
init:23, Foo (com.xurk.init.foo) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invoke:389, InitDestroyAnnotationBeanPostProcessor$LifecycleElement (org.springframework.beans.factory.annotation) invokeInitMethods:333, InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata (org.springframework.beans.factory.annotation) postProcessBeforeInitialization:157, InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) applyBeanPostProcessorsBeforeInitialization:415, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) initializeBean:1786, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) doCreateBean:594, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:516, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) lambda$doGetBean$0:324, AbstractBeanFactory (org.springframework.beans.factory.support) getObject:-1, 2103763750 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$169) getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:322, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:897, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:879, AbstractApplicationContext (org.springframework.context.support) refresh:551, AbstractApplicationContext (org.springframework.context.support) refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context) refresh:758, SpringApplication (org.springframework.boot) refresh:750, SpringApplication (org.springframework.boot) refreshContext:405, SpringApplication (org.springframework.boot) run:315, SpringApplication (org.springframework.boot) run:1237, SpringApplication (org.springframework.boot) run:1226, SpringApplication (org.springframework.boot) main:14, InitApplication (com.xurk.init)
從上往下看,跳過使用 sun.reflect 的方法,進入到第6行。
public void invoke(Object target) throws Throwable { ReflectionUtils.makeAccessible(this.method); this.method.invoke(target, (Object[]) null); }
很明顯,這裡是在通過反射調用某個對象的一個方法,並且這個“某個對象”就是我們定義的 Foo的實例對象瞭。
那麼這裡的 method 又是在什麼時候進行賦值的呢?
invoke(…) 全路徑是:
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke
並且 LifecycleElement 有且隻有一個顯示聲明並且帶參數的構造器,這個要傳入構造器的參數正是 invoke(…) 使用的那個 Method 對象。
接下來就是查一下,是誰在 new LifecycleElement 。
於是定位到:
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) { if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { return this.emptyLifecycleMetadata; } List<LifecycleElement> initMethods = new ArrayList<>(); List<LifecycleElement> destroyMethods = new ArrayList<>(); Class<?> targetClass = clazz; do { final List<LifecycleElement> currInitMethods = new ArrayList<>(); final List<LifecycleElement> currDestroyMethods = new ArrayList<>(); ReflectionUtils.doWithLocalMethods(targetClass, method -> { if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { LifecycleElement element = new LifecycleElement(method); currInitMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new LifecycleElement(method)); if (logger.isTraceEnabled()) { logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); initMethods.addAll(0, currInitMethods); destroyMethods.addAll(currDestroyMethods); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : new LifecycleMetadata(clazz, initMethods, destroyMethods)); }
第15-26行是在通過反射判斷方法上是否存在某個註解,如果是的話就加到一個集合中,在最後用於構建 LifecycleMetadata 實例。
在這裡因為我們要查找的是被賦值的內容,所以在使用IDE進行查找時隻要關註 write 相關的內容就行瞭。
到這裡,已經知道瞭是在 CommonAnnotationBeanPostProcessor 的構造器中進行 set 的,也就是當創建 CommonAnnotationBeanPostProcessor 實例的時候就會進行賦值,並且 CommonAnnotationBeanPostProcessor 是 InitDestroyAnnotationBeanPostProcessor 的子類。
並且,CommonAnnotationBeanPostProcessor 構造器中調用的 setInitAnnotationType 其實是它父類的方法,實際是對 InitDestroyAnnotationBeanPostProcessor 的實例的 initAnnotationType 字段進行賦值。
到這裡已經可以明確 buildLifecycleMetadata(…) 中判斷的正是 @PostConstruct。
再回到buildLifecycleMetadata(…) ,查看其使用的 doWithLocalMethods(…) 的實現。
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) { Method[] methods = getDeclaredMethods(clazz, false); for (Method method : methods) { try { mc.doWith(method); } catch (IllegalAccessException ex) { throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex); } } }
很簡單,通過反射獲得類的所有方法,然後調用一個函數接口的方法,這個函數接口的實現就是判斷方法是不是被 @PostConstruct 標註,如果被標註的話放到一個名字叫 currInitMethods 的集合中。
看到這裡或許你已經意識到瞭, @PostConstruct 可以標註多個方法,並且因為反射獲取方法時是根據聲明順序的、 currInitMethods 是 ArrayList,兩者之間的順序是一樣的。
好瞭,被 @PostConstruct 標註的方法已經找到放到集合中瞭,將被用來構建 LifecycleMetadata 實例瞭。
buildLifecycleMetadata(…) 方法返回一個 LifecycleMetadata 實例,這個返回值中包含傳入Class實例中得到的所有被 @PostConstruct 標註的 Method 實例,接下來要看看是誰在調用 buildLifecycleMetadata(…) 方法,看看它是怎麼用的?
追溯到 findLifecycleMetadata(…) 而 findLifecycleMetadata(…) 又有好幾處被調用。
查看最早得到的方法調用棧,查到postProcessBeforeInitializatio(…) ,它又是再被誰調用?
init:23, Foo (com.xurk.init.foo) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invoke:389, InitDestroyAnnotationBeanPostProcessor$LifecycleElement (org.springframework.beans.factory.annotation) invokeInitMethods:333, InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata (org.springframework.beans.factory.annotation) postProcessBeforeInitialization:157, InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) applyBeanPostProcessorsBeforeInitialization:415, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) initializeBean:1786, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
跟著方法調用棧,我們來到
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
@Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
在這個方法,遍歷 BeanPostProcessors 集合並執行每個 BeanPostProcessor 的 postProcessBeforeInitialization(…) 方法。
**那麼 getBeanPostProcessors() 中的內容又是在什麼時候放進去的呢?都有哪些內容?**可以通過 AnnotationConfigUtils和 CommonAnnotationBeanPostProcessor 查找,這裡就不再贅述瞭。
查找 applyBeanPostProcessorsBeforeInitialization(…) 的調用者,來到
org.springframework.beans.factory.support.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()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { 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; }
顧名思義,在這個方法中對 Bean 進行初始化。是在被註入前一定要經過的過程,到這裡被 @PostConstruct 標註的方法執行已經完成瞭。至於這個方法誰調用可以自己查看調用棧。
init-method、afterPropertiesSet() 的調用
細心的朋友可能已經發現瞭在 initializeBean(…) 中有調用到一個叫做 invokeInitMethods(…) 的方法。
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>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
BeanDefinition是Bean定義的一個抽象。類似於在Java中存在Class類用於描述一個類,裡面有你定義的 Bean 的各種信息。
在第4行,判斷是不是 InitializingBean,如果是的話會進行類型強轉,然後調用 afterPropertiesSet()。
在第26行,獲得到自定義初始化方法的名字,然後在第30行調用 invokeCustomInitMethod 執行完成。
順序的確定
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()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { 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; }
initializeBean(…) 方法中,先執行 applyBeanPostProcessorsBeforeInitialization(…) 在執行 invokeInitMethods(…) 。
而 applyBeanPostProcessorsBeforeInitialization(…) 會執行被 @PostConstruct 標註的方法,invokeInitMethods(…) 會執行 afterPropertiesSet() 和自定義的初始化方法,並且 afterPropertiesSet() 在自定義的初始化方法之前執行。
所以它們之間的執行順序是:
@PostConstruct > afterPropertiesSet() > initMethod()
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Spring BeanPostProcessor源碼示例解析
- 使用spring容器在初始化Bean時前和後的操作
- spring初始化方法的執行順序及其原理分析
- 解決tk mapper 通用mapper的bug問題
- Spring Aware源碼設計示例解析