Bean後置處理器 - SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference

来源:https://www.cnblogs.com/elvinle/archive/2020/07/27/13384992.html
-Advertisement-
Play Games

示例 @Component public class IndexA { @Autowired IndexB bbb; public IndexA() { System.out.println("IndexA constructor..."); } public void printf(){ Syst ...


示例

@Component
public class IndexA {

    @Autowired
    IndexB bbb;

    public IndexA() {
        System.out.println("IndexA constructor...");
    }

    public void printf(){
        System.out.println("indexA printf : ");
        System.out.println("indexB --> " + (bbb == null ? null : bbb.getClass().getName()));
    }
}

@Component
public class IndexB {

    @Autowired
    IndexA aaa;

    public IndexB() {
        System.out.println("IndexB constructor...");
    }

    public void printf(){
        System.out.println("indexB printf : ");
        System.out.println("indexA --> " + (aaa == null ? null : aaa.getClass().getName()));
    }
}

@Configuration
@ComponentScan({
         "com.study.ioc.cyc"
})
public class StartConfig {
}

測試代碼:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(StartConfig.class);
    IndexA a = ac.getBean(IndexA.class);
    System.out.println("spring's indexA --> " + a.getClass().getName());
    a.printf();
    System.out.println("======================");
    IndexB b = ac.getBean(IndexB.class);
    System.out.println("spring's indexB --> " + b.getClass().getName());
    b.printf();
}

運行結果:

 

 

 對於這種存在迴圈依賴的情況, 其大致過程是這樣的:

1. 實例化 IndexA

2. 對 IndexA 進行屬性註入, 此時發現 屬性 IndexB

3. 實例化 IndexB

4. 對 IndexB 進行屬性註入, 此時又發現了屬性 IndexA

5. 對 IndexA 執行 getSingleton("indexA") (此時, 會調用後置處理器 SmartInstantiationAwareBeanPostProcessor), 拿到 IndexA

6. 回到 IndexB 屬性註入的地方, 然後對 IndexB 進行初始化

7. IndexB 變成了一個 bean, 回到 IndexA 屬性註入的地方, 然後對 IndexA 進行初始化

 

getSingleton的關鍵代碼為:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

此時, allowEarlyReference = true, 是寫死的

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //從map中獲取bean如果不為空直接返回,不再進行初始化工作
    //講道理一個程式員提供的對象這裡一般都是為空的
    //1.先從一級緩存獲取
    Object singletonObject = this.singletonObjects.get(beanName);
    //2.如果沒獲取到, 且 bean 還在創建中時
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //3.再從二級緩存中獲取
            singletonObject = this.earlySingletonObjects.get(beanName);
            //4. 還沒獲取到, 且允許迴圈依賴時
            if (singletonObject == null && allowEarlyReference) {
                //5. 最後從三級緩存中獲取 對象的工廠, 通過 getObject 來獲取對象
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

看到這裡, 需要回到

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

看一句關鍵代碼:

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    //這裡創建了一個匿名的 ObjectFactory 實現類, 他是一個工廠, 可以用來獲取對象
    //addSingletonFactory中, 將這個工廠放到 singletonFactories 中去了. singletonFactories 是spring的三級緩存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

singletonFactory.getObject() 調用的, 其實是 這裡的 getEarlyBeanReference() 方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

前面有看到過, SmartInstantiationAwareBeanPostProcessor 可以用來選擇構造函數, 那麼這裡, 他還可以用來暴露 bean 的早期引用. 

此例中, 就是暴露 IndexA 的早期引用. 此時 IndexA 還沒有進行初始化, 是一個半成品.

如果有對 IndexA 進行 AOP , 那麼也會在這裡進行一次代理. 將代理過的對象暴露給 IndexB.

 

還是通過調試的方式, 來確定下, 這邊有那些後置處理器能滿足條件:

1. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

2. AutowiredAnnotationBeanPostProcessor

 

ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

由其父類實現:

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#getEarlyBeanReference

@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    return bean;
}

ImportAwareBeanPostProcessor 在 創建 bean 的這整個過程中, 出現的頻率蠻高的, 但是大部分都是來湊熱鬧的.

 

AutowiredAnnotationBeanPostProcessor

也是由其父類實現:

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#getEarlyBeanReference

@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    return bean;
}

從這裡可以看出, 正常情況下(不需要代理時), 該是什麼, 這裡就返回什麼, 不會對 bean 進行任何操作

 

 

上面說, 在這個後置處理器的處理裡面, 可能會產生代理, 為了印證這個問題, 需要對實例代碼進行一些小的修改:

@Component
@Aspect
public class Aopa {

    @Pointcut("execution(* com.study.ioc.cyc.IndexA.*(..))")
    public void pointCutMethodA() {
    }


    @Before("pointCutMethodA()")
    public void beforeA() {
        System.out.println("before invoke indexA.*() method -- Aopa");
    }
}


@EnableAspectJAutoProxy
@Configuration
@ComponentScan({
         "com.study.ioc.cyc"
        , "com.study.ioc.aop"
})
public class StartConfig {
}

此時, 再到方法裡面進行調試, 會發現多出來一個後置處理器, 變成了三個:

1. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

2. AnnotationAwareAspectJAutoProxyCreator

3. AutowiredAnnotationBeanPostProcessor

 

AnnotationAwareAspectJAutoProxyCreator

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

進 wrapIfNecessary 中看:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

這裡看到, 有一個創建代理的方法. 確實是通過這裡, 產生了代理方法.

 

此例中, 如果aop裡面, 把 IndexB 也加進去, 那麼 IndexB 是不是也會在這裡進行代理呢?

答案是否定的. IndexB在此例中, 不會進這個方法的. 對於 IndexB 來說, 其實是一個正常的創建過程.

IndexB 的代理, 會在別的地方進行.(initializeBean中 - 初始化之後的後置處理器中完成代理) 

這個在後面就能看到

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在一些小的應用中,難免會用到資料庫,Sqlite資料庫以其小巧輕便,無需安裝,移植性好著稱,本文主要以一個簡單的小例子,簡述Python在Sqlite資料庫方面的應用,僅供學習分享使用,如有不足之處,還請指正。 ...
  • Optional Optional 類是一個可以為null的容器對象。可以很好的解決空指針異常。 1 創建Optional對象 創建一個空的Optional對象 Optional<String> empty = Optional.empty(); 創建一個非空的Optional對象 Optional ...
  • Apache Kafka 架構和相關概念 Apache Kafka 是一款開源的分散式消息引擎系統 消息引擎的同類 ActiveMQ RabbitMQ WebSphere MQ Rocket MQ JMS僅僅是一組 API 協議 消息引擎的作用 削峰填谷 緩衝上下游瞬時突發流量,使其更平滑.特別是對 ...
  • 百度雲盤:Python Cookbook(第3版)PDF高清完整版免費下載 提取碼:i2y5 豆瓣評分: 內容簡介 《Python Cookbook(第3版)中文版》介紹了Python應用在各個領域中的一些使用技巧和方法,其主題涵蓋了數據結構和演算法,字元串和文本,數字、日期和時間,迭代器和生成器,文 ...
  • JDK動態代理和 CGLIB 代理 JDK動態代理:其代理對象必須是某個介面的實現,它是通過在運行期期間創建一個介面的實現類來完成對目標對象的代理。 代碼示例 介面 public interface IUserDao { void save(); } 實現類 public class UserDao ...
  • spring在初始化之後, 還調用了一次 Bean 的後置處理器. 代碼片段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterIniti ...
  • 代碼片段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization @Override public Object ...
  • 當spring完成屬性註入之後, 就要開始 bean 的初始化了 代碼片段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean // Initialize the bea ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...