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

Spring 實例化 Bean 的三種核心方式:構造器、靜態工廠、實例工廠全解析

     編輯:Java 程式設計入門

前言:Spring IoC 容器的 Bean 實例化機制

Spring 容器管理 Bean 生命週期的起點是實例化。雖然現代 Spring(Boot 3.x / Framework 6.x)推薦構造器注入與組件掃描(@Component、@Autowired),但理解 XML 配置時代的三種經典實例化方式,對於維護舊專案、排查實例化異常、掌握 Spring 底層原理至關重要。

本文深度解析:構造器實例化、靜態工廠方法、實例工廠方法,附帶原理圖解、參數注入、優缺點對比與遷移建議。

一、方式一:構造器實例化(最常用、最直觀)

XML 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 無參構造器 --> 
    <bean id="personDao" class="springdao.PersonDao" />

    <!-- 有參構造器:constructor-arg 注入 --> 
    <bean id="personDaoWithArgs" class="springdao.PersonDao">
        <constructor-arg name="name" value="ZhangSan" />
        <constructor-arg name="dep" value="IT" />
    </bean>
</beans>

Bean 類

package springdao;

public class PersonDao {
    private String name;
    private String dep;

    public PersonDao() {} // 無參構造器(必須顯式或隱式存在)

    public PersonDao(String name, String dep) {
        this.name = name;
        this.dep = dep;
    }
    // getter/setter ...
    public void test() { System.out.println("hello, spring"); }
}

獲取 Bean

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonDao dao = ctx.getBean("personDao", PersonDao.class);
dao.test();

原理:反射調用構造器

  • 容器解析 <bean class="..."/>,獲取 Class 對象。
  • 根據 <constructor-arg> 匹配構造器參數(類型、名稱、索引)。
  • 通過 Constructor.newInstance(args) 實例化(內部用反射,繞過訪問修飾符)。
  • 後續進行屬性填充(Setter 注入)、初始化回調(InitializingBean、@PostConstruct)。

二、方式二:靜態工廠方法實例化

適用場景

  • 實例化邏輯複雜(單例判斷、參數校驗、多實現選擇)。
  • 隱藏具體實現類(介面導向編程)。
  • 第三方庫類無法修改構造器/添加註解。

XML 配置

<bean id="productCreator" 
      class="serviceImpl.ProductCreator" 
      factory-method="createPersonDao" />
      <!-- 注意:class 指向工廠類,factory-method 指定靜態方法 -->

工廠類

package serviceImpl;

import springdao.PersonDao;

public class ProductCreator {
    // 必須是 static 方法
    public static PersonDao createPersonDao() {
        // 複雜邏輯:配置讀取、緩存檢查、代理增強等
        return new PersonDao("FactoryUser", "FactoryDep");
    }

    // 非靜態方法(用於方式三)
    public PersonDao productACreate() {
        return new PersonDao("InstanceUser", "InstanceDep");
    }
}

關鍵點

  • class 屬性指向工廠類,而非 Bean 類。
  • factory-method 指向static 方法,返回 Bean 實例。
  • 容器不實例化工廠類,直接調用靜態方法。
  • 可通過 <constructor-arg> 向工廠方法傳參。

三、方式三:實例工廠方法實例化

區別於方式二:工廠本身也是 Spring 管理的 Bean

  • 工廠類實例由容器創建(支援依賴注入、生命週期回調)。
  • 調用工廠實例的非靜態方法創建目標 Bean。
  • 適合工廠需要注入其他服務(如 DataSource、Config)的場景。

XML 配置(兩步驟)

<!-- 1. 定義工廠 Bean -->
<bean id="productCreator" class="serviceImpl.ProductCreator" />

<!-- 2. 定義目標 Bean,引用工廠 Bean -->
<bean id="productACreate" 
      factory-bean="productCreator" 
      factory-method="productACreate" />

獲取 Bean

PersonDao dao = ctx.getBean("productACreate", PersonDao.class); // 獲取的是工廠方法返回的實例
dao.test();

原理流程

  1. 容器先實例化 productCreator(調用其構造器、注入依賴)。
  2. 解析 productACreate,找到 factory-bean 引用的工廠實例。
  3. 通過反射調用工廠實例的 productACreate() 方法獲取目標實例。

四、三種方式深度對比

維度構造器實例化靜態工廠方法實例工廠方法
XML 核心屬性class (Bean 類)class (工廠類) + factory-method (static)factory-bean + factory-method (instance)
工廠類是否被容器管理無工廠類否 (僅類加載)是 (Bean 實例)
工廠方法類型構造器static非 static
工廠可注入依賴N/A否是
適用複雜邏輯否 (構造器應簡單)是是
單例控制容器控制 (scope)工廠方法內部控制工廠方法內部控制
序列化/遠程調用良好需工廠類可序列化需工廠 Bean 可序列化
現代 Spring 推薦度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

五、進階:FactoryBean 介面(第四種「隱藏」方式)

Spring 內部大量使用 org.springframework.beans.factory.FactoryBean<T> 介面,實現極其靈活的 Bean 創建邏輯(如 SqlSessionFactoryBean、LocalContainerEntityManagerFactoryBean)。

public class MyFactoryBean implements FactoryBean<PersonDao> {
    @Override
    public PersonDao getObject() throws Exception {
        return new PersonDao("FactoryBeanUser", "FactoryBeanDep");
    }
    @Override
    public Class<?> getObjectType() { return PersonDao.class; }
    @Override
    public boolean isSingleton() { return true; }
}

// XML
<bean id="myFactoryBean" class="com.example.MyFactoryBean" />
// 獲取: ctx.getBean("myFactoryBean") 返回 PersonDao 實例
// 獲取工廠本身: ctx.getBean("&myFactoryBean") 返回 MyFactoryBean 實例

這是 Spring 擴展點的核心,框架集成(MyBatis、Hibernate、Redis)均基於此。

六、現代化遷移:從 XML 到註解配置

XML 方式註解等價寫法
構造器實例化@Component + @Autowired 構造器 (單構造器可省略 @Autowired)
靜態工廠方法@Bean 方法在 @Configuration 類中:
@Bean public PersonDao personDao() { return ProductCreator.createPersonDao(); }
實例工廠方法@Bean 方法調用另一個 @Bean:
@Bean public PersonDao personDao(ProductCreator creator) { return creator.productACreate(); }
FactoryBean實現 FactoryBean 介面 + @Component,或 @Bean 返回 FactoryBean 實例

遷移優勢

  • 類型安全、IDE 重構支援、編譯期檢查。
  • 條件裝配(@ConditionalOnProperty、@ConditionalOnMissingBean)。
  • 配置類即代碼,支援邏輯復用、測試替換。

七、常見坑與避坑指南

問題原因解決
靜態工廠方法找不到方法非 static / 名稱拼寫錯誤 / 參數不匹配檢查修飾符、大小寫、重載解析
實例工廠 Bean 未初始化factory-bean 引用的 id 不存在 / 循環依賴確認工廠 Bean 定義、使用 depends-on 或 @Lazy 打破循環
構造器注入參數歧義多構造器且參數類型相同指定 constructor-arg name/index/type 或統一用單構造器
FactoryBean 返回 nullgetObject() 返回 null 且 isSingleton()=true拋出異常或返回空對象,避免 NPE
單例 Bean 線程不安全工廠方法內部有狀態工廠方法無狀態、或用 ThreadLocal、或改 prototype scope

結語

Spring Bean 實例化的三種 XML 方式——構造器、靜態工廠、實例工廠——分別對應「簡單創建」、「邏輯封裝無依賴」、「邏輯封裝有依賴」三類場景。理解其底層反射調用鏈、工廠 Bean 生命週期、FactoryBean 擴展點,能幫你快速定位實例化異常、設計靈活的第三方集成。新專務請擁抱 @Configuration + @Bean / @Component 註解模式,享受類型安全與現代化工具鏈紅利;舊專案維護時,本文對比表可作為快速查閱手冊。

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