Bean後置處理器 - InstantiationAwareBeanPostProcessor#postProcessProperties

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

代碼片段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean boolean hasInstAwareBpps = hasInstantiationAwareBeanPo ...


代碼片段:

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

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
//深度引用檢查, 引用再引用
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
    if (pvs == null) {
        pvs = mbd.getPropertyValues();
    }
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            //這裡也是調用的實例化後的後置處理器, 只是調用的方法不一樣
       //這裡會進行 @Autowired 和 @Resource 的註入工作
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } }

同樣的, 通過調試的方式, 來確定這裡使用了那些後置處理器:

1.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

2.CommonAnnotationBeanPostProcessor

3.AutowiredAnnotationBeanPostProcessor

 

ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

@Override
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
    // Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
    // postProcessProperties method attempts to autowire other configuration beans.
    if (bean instanceof EnhancedConfiguration) {
        ((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
    }
    return pvs;
}

這裡是對 EnhancedConfiguration 提供支持. 其實他繼承了 BeanFactoryAware 介面, 並且什麼都沒乾

public interface EnhancedConfiguration extends BeanFactoryAware {}

EnhancedConfiguration 是 ConfigurationClassEnhancer 的一個內部介面, 是給 spring 自己內部使用的, 開發人員用不了這個, 也沒必要

 

CommonAnnotationBeanPostProcessor

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    //找出類中標註了@Resource註解的屬性和方法
    //else if (field.isAnnotationPresent(Resource.class))
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
    }
    return pvs;
}

這裡主要是對 @Resource 進行註入

findResourceMetadata在MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition的時候執行過.

所以這裡, 大概率是從緩存中拿取結果, 然後進行註入操作. 事實上, 也確實如此.

 

inject 最終會調用

org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject 方法

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
        throws Throwable {

    if (this.isField) {
        Field field = (Field) this.member;
        ReflectionUtils.makeAccessible(field);
        field.set(target, getResourceToInject(target, requestingBeanName));
    }
    else {
        if (checkPropertySkipping(pvs)) {
            return;
        }
        try {
            Method method = (Method) this.member;
            ReflectionUtils.makeAccessible(method);
            method.invoke(target, getResourceToInject(target, requestingBeanName));
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

這裡分兩種情況進行處理, 一種是 通過欄位註入, 另一種是通過方法註入.

欄位註入的方式是比較多見的. 

getResourceToInject() 方法是一個被重寫了的方法:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject

他是在一個內部類中被重寫的方法.

@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
            getResource(this, requestingBeanName));
}

預設情況下, lazy = false, 所以會走 getResource() 方法

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
        throws NoSuchBeanDefinitionException {

    if (StringUtils.hasLength(element.mappedName)) {
        return this.jndiFactory.getBean(element.mappedName, element.lookupType);
    }
    if (this.alwaysUseJndiLookup) {
        return this.jndiFactory.getBean(element.name, element.lookupType);
    }
    if (this.resourceFactory == null) {
        throw new NoSuchBeanDefinitionException(element.lookupType,
                "No resource factory configured - specify the 'resourceFactory' property");
    }
    return autowireResource(this.resourceFactory, element, requestingBeanName);
}

根據設置的不同屬性, 進不同的方法, 這裡主要看 autowireResource , 因為一般情況是不需要去設置這些屬性的

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
        throws NoSuchBeanDefinitionException {

    Object resource;
    Set<String> autowiredBeanNames;
    String name = element.name;

    if (factory instanceof AutowireCapableBeanFactory) {
        AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
        DependencyDescriptor descriptor = element.getDependencyDescriptor();
        //優先使用 根據 name 註入, 這個 name 就是 beanName
        //當 beanName 找不到時, 才就會去根據 type 來註入
        //依據是 !factory.containsBean(name) 
        if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
            autowiredBeanNames = new LinkedHashSet<>();
            //根據type(類型)來進行依賴註入
            resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
            if (resource == null) {
                //如果返回時 resource , 則表明, spring容器中找不到, 也創建不了 這個依賴的bean
                throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
            }
        }
        else {
            //根據name(名稱)來進行依賴註入
            resource = beanFactory.resolveBeanByName(name, descriptor);
            autowiredBeanNames = Collections.singleton(name);
        }
    }
    else {
        resource = factory.getBean(name, element.lookupType);
        autowiredBeanNames = Collections.singleton(name);
    }

    if (factory instanceof ConfigurableBeanFactory) {
        ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
        for (String autowiredBeanName : autowiredBeanNames) {
            if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
                beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
            }
        }
    }

    return resource;
}

都說, @Resource 在不設置 name 和 type 的情況下, 是優先使用 name 去 獲取/創建 依賴的 bean , 如果name找不到, 才會使用 type.

這段代碼, 就是支撐的依據.

 

AutowiredAnnotationBeanPostProcessor

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

這裡主要是對 @Autowired 和 @Value 進行註入的. findAutowiringMetadata

findAutowiringMetadata在MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition的時候執行過.

所以此處, 是從緩存中拿取結果, 然後進行註入操作.

最終會調用本類中的方法:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            //根據 type 進行依賴註入
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        synchronized (this) {
            if (!this.cached) {
                if (value != null || this.required) {
                    this.cachedFieldValue = desc;
                    registerDependentBeans(beanName, autowiredBeanNames);
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                            this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                    desc, autowiredBeanName, field.getType());
                        }
                    }
                }
                else {
                    this.cachedFieldValue = null;
                }
                this.cached = true;
            }
        }
    }
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

 

這裡有一個隱藏的後置處理器: SmartInstantiationAwareBeanPostProcessor

留到下一篇看


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

-Advertisement-
Play Games
更多相關文章
  • 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 ...
  • 示例 @Component public class IndexA { @Autowired IndexB bbb; public IndexA() { System.out.println("IndexA constructor..."); } public void printf(){ Syst ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...