Spring IoC 容器管理 Bean 生命週期,實例化策略主要有三種。本文以傳統 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">
<!-- 1. 無參構造器 (類別必須有 public 無參構造器) -->
<bean id="userDao" class="com.example.dao.UserDaoImpl" />
<!-- 2. 帶參構造器注入 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="jdbcUrl" value="jdbc:mysql://localhost:3306/db" />
<constructor-arg name="username" value="root" />
<constructor-arg name="password" value="123456" />
</bean>
<!-- 3. 索引/類型匹配 (無名參數) -->
<bean id="service" class="com.example.service.UserServiceImpl">
<constructor-arg index="0" ref="userDao" />
<constructor-arg index="1" value="defaultRole" />
</bean>
</beans>
public class UserDaoImpl implements UserDao {
// 無參構造器 (隱式或顯式)
public UserDaoImpl() {}
}
public class UserServiceImpl implements UserService {
private final UserDao userDao;
private final String defaultRole;
// 帶參構造器
public UserServiceImpl(UserDao userDao, String defaultRole) {
this.userDao = userDao;
this.defaultRole = defaultRole;
}
}
原理:容器調用**靜態方法**獲取實例,適用於**單例模式、連接池、複雜初始化邏輯**。
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean"
factory-method="getObject" /> <!-- FactoryBean 介面方法 -->
<!-- 自定義靜態工廠 -->
<bean id="singletonService"
class="com.example.factory.ServiceFactory"
factory-method="getInstance" />
public class ServiceFactory {
private static volatile ServiceFactory INSTANCE;
private ServiceFactory() {}
public static ServiceFactory getInstance() { // 必須 static
if (INSTANCE == null) {
synchronized (ServiceFactory.class) {
if (INSTANCE == null) INSTANCE = new ServiceFactory();
}
}
return INSTANCE;
}
}
原理:先建立**工廠 Bean 實例**,再調用其**非靜態方法**產生目標 Bean。適用於**工廠本身需要依賴注入**、**多種產品系列**。
<!-- 1. 定義工廠 Bean -->
<bean id="carFactory" class="com.example.factory.CarFactory">
<property name="paintShop" ref="paintShop" /> <!-- 工廠本身注入依賴 -->
</bean>
<!-- 2. 透過工廠方法建立產品 -->
<bean id="sedan" factory-bean="carFactory" factory-method="createSedan" />
<bean id="suv" factory-bean="carFactory" factory-method="createSuv" />
<bean id="truck" factory-bean="carFactory" factory-method="createTruck" />
public class CarFactory {
private PaintShop paintShop; // 注入依賴
public void setPaintShop(PaintShop paintShop) {
this.paintShop = paintShop;
}
// 非靜態方法
public Car createSedan() {
Car car = new Sedan();
paintShop.paint(car, Color.BLACK);
return car;
}
public Car createSuv() { /* ... */ }
public Car createTruck() { /* ... */ }
}
| 維度 | 構造器 | 靜態工廠 | 實例工廠 |
|---|---|---|---|
| 配置屬性 | class + constructor-arg | class + factory-method | factory-bean + factory-method |
| 調用方式 | new Constructor() | StaticFactory.method() | factoryInstance.method() |
| 工廠本身是否為 Bean | 否 | 否 | **是** (factory-bean) |
| 工廠可注入依賴 | 否 | 否 | **是** |
| 適用場景 | 90% 一般情況 | 單例、舊 API、複雜建構邏輯 | 抽象工廠、工廠需狀態/依賴 |
| 測試友好度 | 高 (構造器注入) | 中 | 中 |
實作 org.springframework.beans.factory.FactoryBean<T>,容器調用 getObject() 獲取真正 Bean:
<bean id="dataSource" class="com.example.factory.CustomDataSourceFactoryBean">
<property name="url" value="jdbc:mysql://..." />
</bean>
public class CustomDataSourceFactoryBean implements FactoryBean<DataSource> {
private String url;
@Override
public DataSource getObject() throws Exception {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url);
return ds;
}
@Override
public Class<?> getObjectType() { return DataSource.class; }
@Override
public boolean isSingleton() { return true; }
public void setUrl(String url) { this.url = url; }
}
@Configuration
public class AppConfig {
// 1. 構造器注入 (推薦)
@Bean
public UserService userService(UserDao userDao) {
return new UserServiceImpl(userDao, "USER");
}
// 2. 靜態工廠
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return new SqlSessionFactoryBean().getObject();
}
// 3. 實例工廠
@Bean
public CarFactory carFactory(PaintShop paintShop) {
return new CarFactory(paintShop);
}
@Bean
public Car sedan(CarFactory carFactory) {
return carFactory.createSedan();
}
}