Spring 容器管理 Bean 生命週期的起點是實例化。雖然現代 Spring(Boot 3.x / Framework 6.x)推薦構造器注入與組件掃描(@Component、@Autowired),但理解 XML 配置時代的三種經典實例化方式,對於維護舊專案、排查實例化異常、掌握 Spring 底層原理至關重要。
本文深度解析:構造器實例化、靜態工廠方法、實例工廠方法,附帶原理圖解、參數注入、優缺點對比與遷移建議。
<?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>
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"); }
}
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonDao dao = ctx.getBean("personDao", PersonDao.class);
dao.test();
<bean class="..."/>,獲取 Class 對象。<constructor-arg> 匹配構造器參數(類型、名稱、索引)。Constructor.newInstance(args) 實例化(內部用反射,繞過訪問修飾符)。InitializingBean、@PostConstruct)。<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> 向工廠方法傳參。<!-- 1. 定義工廠 Bean -->
<bean id="productCreator" class="serviceImpl.ProductCreator" />
<!-- 2. 定義目標 Bean,引用工廠 Bean -->
<bean id="productACreate"
factory-bean="productCreator"
factory-method="productACreate" />
PersonDao dao = ctx.getBean("productACreate", PersonDao.class); // 獲取的是工廠方法返回的實例
dao.test();
productCreator(調用其構造器、注入依賴)。productACreate,找到 factory-bean 引用的工廠實例。productACreate() 方法獲取目標實例。| 維度 | 構造器實例化 | 靜態工廠方法 | 實例工廠方法 |
|---|---|---|---|
| XML 核心屬性 | class (Bean 類) | class (工廠類) + factory-method (static) | factory-bean + factory-method (instance) |
| 工廠類是否被容器管理 | 無工廠類 | 否 (僅類加載) | 是 (Bean 實例) |
| 工廠方法類型 | 構造器 | static | 非 static |
| 工廠可注入依賴 | N/A | 否 | 是 |
| 適用複雜邏輯 | 否 (構造器應簡單) | 是 | 是 |
| 單例控制 | 容器控制 (scope) | 工廠方法內部控制 | 工廠方法內部控制 |
| 序列化/遠程調用 | 良好 | 需工廠類可序列化 | 需工廠 Bean 可序列化 |
| 現代 Spring 推薦度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
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 方式 | 註解等價寫法 |
|---|---|
| 構造器實例化 | @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 實例 |
@ConditionalOnProperty、@ConditionalOnMissingBean)。| 問題 | 原因 | 解決 |
|---|---|---|
| 靜態工廠方法找不到 | 方法非 static / 名稱拼寫錯誤 / 參數不匹配 | 檢查修飾符、大小寫、重載解析 |
| 實例工廠 Bean 未初始化 | factory-bean 引用的 id 不存在 / 循環依賴 | 確認工廠 Bean 定義、使用 depends-on 或 @Lazy 打破循環 |
| 構造器注入參數歧義 | 多構造器且參數類型相同 | 指定 constructor-arg name/index/type 或統一用單構造器 |
| FactoryBean 返回 null | getObject() 返回 null 且 isSingleton()=true | 拋出異常或返回空對象,避免 NPE |
| 單例 Bean 線程不安全 | 工廠方法內部有狀態 | 工廠方法無狀態、或用 ThreadLocal、或改 prototype scope |
Spring Bean 實例化的三種 XML 方式——構造器、靜態工廠、實例工廠——分別對應「簡單創建」、「邏輯封裝無依賴」、「邏輯封裝有依賴」三類場景。理解其底層反射調用鏈、工廠 Bean 生命週期、FactoryBean 擴展點,能幫你快速定位實例化異常、設計靈活的第三方集成。新專務請擁抱 @Configuration + @Bean / @Component 註解模式,享受類型安全與現代化工具鏈紅利;舊專案維護時,本文對比表可作為快速查閱手冊。