最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序

 更新時(shí)間:2021年09月15日 11:35:18   作者:且徐行2020  
這篇文章主要介紹了關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

@PostConstruct、init-method、afterPropertiesSet() 執(zhí)行順序

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

程序版本:Spring Boot 2.3.5.RELEASE

準(zhǔn)備好要驗(yàn)證的材料:

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();
    }
}

執(zhí)行啟動(dòng)類,可以看到在控制臺(tái)中輸出:

@PostConstruct

afterPropertiesSet()

initMethod()

說明執(zhí)行順序是:@PostConstruct、afterPropertiesSet()、init-method

接下來將跟著源碼來了解為什么是這個(gè)順序。

@PostConstruct 標(biāo)注的方法在何時(shí)被誰調(diào)用

首先,在 init() 中打個(gè)斷點(diǎn),然后以 debug 的方式啟動(dòng)項(xiàng)目,得到下面的調(diào)用棧:

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 的方法,進(jìn)入到第6行。

public void invoke(Object target) throws Throwable {
   ReflectionUtils.makeAccessible(this.method);
   this.method.invoke(target, (Object[]) null);
}

很明顯,這里是在通過反射調(diào)用某個(gè)對(duì)象的一個(gè)方法,并且這個(gè)“某個(gè)對(duì)象”就是我們定義的 Foo的實(shí)例對(duì)象了。

那么這里的 method 又是在什么時(shí)候進(jìn)行賦值的呢?

invoke(...) 全路徑是:

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke

并且 LifecycleElement 有且只有一個(gè)顯示聲明并且?guī)?shù)的構(gòu)造器,這個(gè)要傳入構(gòu)造器的參數(shù)正是 invoke(...) 使用的那個(gè) Method 對(duì)象。

接下來就是查一下,是誰在 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行是在通過反射判斷方法上是否存在某個(gè)注解,如果是的話就加到一個(gè)集合中,在最后用于構(gòu)建 LifecycleMetadata 實(shí)例。

在這里因?yàn)槲覀円檎业氖潜毁x值的內(nèi)容,所以在使用IDE進(jìn)行查找時(shí)只要關(guān)注 write 相關(guān)的內(nèi)容就行了。

在這里插入圖片描述

到這里,已經(jīng)知道了是在 CommonAnnotationBeanPostProcessor 的構(gòu)造器中進(jìn)行 set 的,也就是當(dāng)創(chuàng)建 CommonAnnotationBeanPostProcessor 實(shí)例的時(shí)候就會(huì)進(jìn)行賦值,并且 CommonAnnotationBeanPostProcessor 是 InitDestroyAnnotationBeanPostProcessor 的子類。

在這里插入圖片描述

并且,CommonAnnotationBeanPostProcessor 構(gòu)造器中調(diào)用的 setInitAnnotationType 其實(shí)是它父類的方法,實(shí)際是對(duì) InitDestroyAnnotationBeanPostProcessor 的實(shí)例的 initAnnotationType 字段進(jìn)行賦值。

到這里已經(jīng)可以明確 buildLifecycleMetadata(...) 中判斷的正是 @PostConstruct。

再回到buildLifecycleMetadata(...) ,查看其使用的 doWithLocalMethods(...) 的實(shí)現(xiàn)。

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);
        }
    }
}

很簡單,通過反射獲得類的所有方法,然后調(diào)用一個(gè)函數(shù)接口的方法,這個(gè)函數(shù)接口的實(shí)現(xiàn)就是判斷方法是不是被 @PostConstruct 標(biāo)注,如果被標(biāo)注的話放到一個(gè)名字叫 currInitMethods 的集合中。

看到這里或許你已經(jīng)意識(shí)到了, @PostConstruct 可以標(biāo)注多個(gè)方法,并且因?yàn)榉瓷浍@取方法時(shí)是根據(jù)聲明順序的、 currInitMethods 是 ArrayList,兩者之間的順序是一樣的。

好了,被 @PostConstruct 標(biāo)注的方法已經(jīng)找到放到集合中了,將被用來構(gòu)建 LifecycleMetadata 實(shí)例了。

buildLifecycleMetadata(...) 方法返回一個(gè) LifecycleMetadata 實(shí)例,這個(gè)返回值中包含傳入Class實(shí)例中得到的所有被 @PostConstruct 標(biāo)注的 Method 實(shí)例,接下來要看看是誰在調(diào)用 buildLifecycleMetadata(...) 方法,看看它是怎么用的?

追溯到 findLifecycleMetadata(...) 而 findLifecycleMetadata(...) 又有好幾處被調(diào)用。

在這里插入圖片描述

查看最早得到的方法調(diào)用棧,查到postProcessBeforeInitializatio(...) ,它又是再被誰調(diào)用?

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)

跟著方法調(diào)用棧,我們來到

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;
}

在這個(gè)方法,遍歷 BeanPostProcessors 集合并執(zhí)行每個(gè) BeanPostProcessor 的 postProcessBeforeInitialization(...) 方法。

**那么 getBeanPostProcessors() 中的內(nèi)容又是在什么時(shí)候放進(jìn)去的呢?都有哪些內(nèi)容?**可以通過 AnnotationConfigUtils和 CommonAnnotationBeanPostProcessor 查找,這里就不再贅述了。

查找 applyBeanPostProcessorsBeforeInitialization(...) 的調(diào)用者,來到

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;
}

顧名思義,在這個(gè)方法中對(duì) Bean 進(jìn)行初始化。是在被注入前一定要經(jīng)過的過程,到這里被 @PostConstruct 標(biāo)注的方法執(zhí)行已經(jīng)完成了。至于這個(gè)方法誰調(diào)用可以自己查看調(diào)用棧。

init-method、afterPropertiesSet() 的調(diào)用

細(xì)心的朋友可能已經(jīng)發(fā)現(xiàn)了在 initializeBean(...) 中有調(diào)用到一個(gè)叫做 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定義的一個(gè)抽象。類似于在Java中存在Class類用于描述一個(gè)類,里面有你定義的 Bean 的各種信息。

在第4行,判斷是不是 InitializingBean,如果是的話會(huì)進(jìn)行類型強(qiáng)轉(zhuǎn),然后調(diào)用 afterPropertiesSet()。

在第26行,獲得到自定義初始化方法的名字,然后在第30行調(diào)用 invokeCustomInitMethod 執(zhí)行完成。

順序的確定

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(...) 方法中,先執(zhí)行 applyBeanPostProcessorsBeforeInitialization(...) 在執(zhí)行 invokeInitMethods(...) 。

而 applyBeanPostProcessorsBeforeInitialization(...) 會(huì)執(zhí)行被 @PostConstruct 標(biāo)注的方法,invokeInitMethods(...) 會(huì)執(zhí)行 afterPropertiesSet() 和自定義的初始化方法,并且 afterPropertiesSet() 在自定義的初始化方法之前執(zhí)行。

所以它們之間的執(zhí)行順序是:

@PostConstruct > afterPropertiesSet() > initMethod()

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java數(shù)據(jù)結(jié)構(gòu)之簡單的連接點(diǎn)(link)實(shí)現(xiàn)方法示例

    Java數(shù)據(jù)結(jié)構(gòu)之簡單的連接點(diǎn)(link)實(shí)現(xiàn)方法示例

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之簡單的連接點(diǎn)(link)實(shí)現(xiàn)方法,涉及java指針指向節(jié)點(diǎn)的相關(guān)使用技巧,需要的朋友可以參考下
    2017-10-10
  • 詳解Elasticsearch如何把一個(gè)索引變?yōu)橹蛔x

    詳解Elasticsearch如何把一個(gè)索引變?yōu)橹蛔x

    這篇文章主要為大家介紹了詳解Elasticsearch如何把一個(gè)索引變?yōu)橹蛔x示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • SpringBoot集成ShedLock實(shí)現(xiàn)分布式定時(shí)任務(wù)的示例代碼

    SpringBoot集成ShedLock實(shí)現(xiàn)分布式定時(shí)任務(wù)的示例代碼

    ShedLock 是一個(gè) Java 庫,通常用于分布式系統(tǒng)中,確保定時(shí)任務(wù)(Scheduled Tasks)在集群環(huán)境下只被某一個(gè)實(shí)例執(zhí)行一次,本文給大家介紹了SpringBoot集成ShedLock實(shí)現(xiàn)分布式定時(shí)任務(wù)的示例代碼,需要的朋友可以參考下
    2024-12-12
  • SpringBoot熱部署和整合Mybatis的過程

    SpringBoot熱部署和整合Mybatis的過程

    熱部署,就是在應(yīng)用正在運(yùn)行的時(shí)候升級(jí)軟件,卻不需要重新啟動(dòng)應(yīng)用,本文給大家詳細(xì)介紹SpringBoot熱部署和整合Mybatis的過程,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • Java中Date與String相互轉(zhuǎn)換的方法

    Java中Date與String相互轉(zhuǎn)換的方法

    這篇文章主要為大家詳細(xì)介紹了Java中Date與String相互轉(zhuǎn)換方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Java中正則表達(dá)式的使用和詳解(下)

    Java中正則表達(dá)式的使用和詳解(下)

    這篇文章主要介紹了Java正則表達(dá)式的使用和詳解(下)的相關(guān)資料,包括常用正則表達(dá)式和正則表達(dá)式語法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧
    2017-04-04
  • java線程池核心API源碼詳細(xì)分析

    java線程池核心API源碼詳細(xì)分析

    大家好,本篇文章主要講的是java線程池核心API源碼詳細(xì)分析,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • SpringBoot Maven Clean報(bào)錯(cuò)解決方案

    SpringBoot Maven Clean報(bào)錯(cuò)解決方案

    這篇文章主要介紹了SpringBoot Maven Clean報(bào)錯(cuò)解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 深入淺析HashMap key和value能否為null

    深入淺析HashMap key和value能否為null

    HashMap的key和value可為null,線程不安全,HashTable的key和value均不可為null,線程安全,ConcurrentHashMap在多線程場景下使用,key和value也不能為null,還對(duì)它們進(jìn)行了測試和底層代碼分析,本文介紹HashMap key和value能否為null,感興趣的朋友跟隨小編一起看看吧
    2025-04-04
  • Java枚舉類與注解,新手一篇搞定它

    Java枚舉類與注解,新手一篇搞定它

    枚舉類型是Java 5新增的特性,它是一種新的類型,允許用常量來表示特定的數(shù)據(jù)片斷,而且全部都以類型安全的形式來表示。由于Java 不支持多繼承,所以枚舉對(duì)象不能再繼承其他類(可以實(shí)現(xiàn)接口)
    2021-06-06

最新評(píng)論

平安县| 江口县| 古浪县| 铜鼓县| 乌鲁木齐县| 三台县| 丹棱县| 乐陵市| 凉城县| 慈溪市| 左权县| 临沂市| 安新县| 美姑县| 江阴市| 姚安县| 富民县| 建阳市| 新乡县| 宽城| 长沙市| 巴青县| 德安县| 邯郸县| 闽侯县| 玛沁县| 德惠市| 福泉市| 永修县| 苍溪县| 改则县| 城固县| 辉县市| 荆州市| 延津县| 博客| 澳门| 贵定县| 新巴尔虎右旗| 土默特右旗| 平安县|