首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring 源碼深度解析:Prototype Bean 實例化、byName/byType 自動裝配、FactoryBean getObject 實現原理

     編輯:Java 綜合教學

Spring 源碼深度解析:Prototype Bean、自動裝配、FactoryBean

Spring 容器啟動與 Bean 生命周期管理是面試高頻、排查疑難雜癥的核心。本文基於 Spring 5.x 源碼,深度剖析 Prototype Bean 實例化流程、byName/byType 自動裝配實現、FactoryBean getObject 調用鏈路,附關鍵源碼註解與流程圖。

一、Prototype Bean 實例化流程 (AbstractBeanFactory.doGetBean)

// 單例 Bean 路徑
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args));
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// Prototype Bean 路徑
else if (mbd.isPrototype()) {
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);      // 1. 記錄 ThreadLocal 正在創建
        prototypeInstance = createBean(beanName, mbd, args); // 2. 直接創建
    } finally {
        afterPrototypeCreation(beanName);       // 3. 移除 ThreadLocal 記錄
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

關鍵差異點

環節SingletonPrototype
獲取方式getSingleton (先查緩存 singletonObjects)直接 createBean
創建前無特殊處理beforePrototypeCreation: 記錄到 ThreadLocal prototypesCurrentlyInCreation
創建後加入 singletonObjects、注冊銷毀回調afterPrototypeCreation: 從 ThreadLocal 移除
循環依賴支持 (三級緩存)不支持 (每次新建,無早期暴露)

ThreadLocal 作用

防止多線程同時創建同一 Prototype Bean 導致狀態混亂,非線程安全保護 (Prototype 本身不保證線程安全)。

二、byName 自動裝配源碼 (AbstractAutowireCapableBeanFactory.autowireByName)

protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    // 1. 找出所有非簡單類型的未滿足屬性 (非基本類型、String、Date 等)
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    
    for (String propertyName : propertyNames) {
        // 2. 容器中是否存在同名 Bean
        if (containsBean(propertyName)) {
            // 3. 獲取/創建該 Bean
            Object bean = getBean(propertyName);
            // 4. 加入屬性值持有器
            pvs.add(propertyName, bean);
            // 5. 註冊依賴關係 (銷毀順序用)
            registerDependentBean(propertyName, beanName);
        } else {
            // 找不到不報錯,靜默跳過 (byName 特性)
        }
    }
}

關鍵細節

  • 非簡單類型判斷:BeanUtils.isSimpleProperty 過濾基本類型、包裝類、String、Date、Class 等
  • 不報錯:找不到同名 Bean 靜默跳過,區別於 byType 必須唯一
  • 最終注入:pvs 統一由 AbstractPropertyAccessor.setPropertyValues 遍歷注入

三、byType 自動裝配源碼 (autowireByType → resolveDependency)

protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
        // 跳過 Object 類型屬性 (無意義)
        if (!Object.class.equals(pd.getPropertyType())) {
            MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
            DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
            // 核心解析入口
            Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
            if (autowiredArgument != null) pvs.add(propertyName, autowiredArgument);
        }
    }
}

resolveDependency 核心邏輯 (DefaultListableBeanFactory.doResolveDependency)

  1. 候選者收集:findAutowireCandidates → BeanFactoryUtils.beanNamesForTypeIncludingAncestors 獲取所有匹配類型的 Bean 名稱
  2. 可注入性檢查:isAutowireCandidate (排除 autowire-candidate=false)
  3. 實例化候選者:getBean(candidateName)
  4. 歧義解決:
    • 0 個 → required=true 拋 NoSuchBeanDefinitionException
    • 1 個 → 直接使用
    • 多個 → determinePrimaryCandidate 找 @Primary,無則拋 NoUniqueBeanDefinitionException

四、FactoryBean getObject 調用鏈路 (getObjectForBeanInstance)

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    // 1. & 前綴檢查:名稱以 & 開頭且非 FactoryBean → 報錯
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(...);
    }
    
    // 2. 非 FactoryBean 或要求返回工廠本身 → 直接返回
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }
    
    // 3. FactoryBean 處理分支
    FactoryBean factory = (FactoryBean) beanInstance;
    if (mbd == null) {
        // 從緩存獲取 (單例 FactoryBean)
        Object object = getCachedObjectForFactoryBean(beanName);
        if (object == null) {
            object = doGetObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    } else {
        // 原型/非單例 FactoryBean:每次調用 getObject
        return doGetObjectFromFactoryBean(factory, beanName, !synthetic);
    }
}

doGetObjectFromFactoryBean 關鍵邏輯

private Object doGetObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
    // 1. 權限檢查 (SecurityManager 環境)
    Object object = AccessController.doPrivileged(() -> factory.getObject());
    
    // 2. 空值檢查:單例 FactoryBean 正在創建中返回 null → 報錯
    if (object == null && isSingletonCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(...);
    }
    
    // 3. 後處理器增強 (如 AOP 代理)
    if (object != null && shouldPostProcess) {
        object = postProcessObjectFromFactoryBean(object, beanName);
    }
    return object;
}

單例 vs 原型 FactoryBean 緩存差異

FactoryBean 類型isSingleton()getObject() 執行頻率緩存位置
Singleton FactoryBeantrue首次調用後緩存,後續直接返回緓存factoryBeanObjectCache (Map)
Prototype FactoryBeanfalse每次調用都執行 getObject() 創建新對象無緩存

五、源碼閱讀關鍵斷點建議 (IDE Debug)

  1. AbstractBeanFactory.doGetBean:統一入口,觀察 scope 分流
  2. AbstractAutowireCapableBeanFactory.createBean:實例化、屬性填充、初始化全流程
  3. DefaultListableBeanFactory.doResolveDependency:自動裝配核心歧義解決
  4. AbstractBeanFactory.getObjectForBeanInstance:FactoryBean 與普通 Bean 分流
  5. FactoryBean.getObject:自定義 FactoryBean 斷點驗證邏輯

Spring 源碼雖博大,但核心流程清晰:BeanDefinition → 實例化 → 屬性填充 (自動裝配) → 初始化 → 後處理 → 註冊/返回。掌握 Prototype 特殊性、byName/byType 歧義解決、FactoryBean 緩存機制,能解決 90% 容器級疑難雜癥。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved