Spring IoC 容器的核心能力之一是靈活的屬性注入(Dependency Injection)。本文通過完整示例,系統講解 基本類型、數組、List、Set、Map、java.util.Date 等各類屬性的 XML 注入方式,並展示 父 Bean 抽取公共配置 以及 自定義 PropertyEditor 實現日期字符串轉換 的進階技巧。
package com.jadyer.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Bean11 {
private Integer intValue;
private String strValue;
private String[] arrayValue;
private List<String> listValue;
private Set<String> setValue;
private Map<String, String> mapValue;
private Date dateValue;
// 省略 getter/setter
}
public class Bean22 {
private Bean33 bean33;
private Bean44 bean44; // 對應 setter setBean44()
private Bean55 bean55;
// getter/setter
}
public class Bean33 { private Integer id; private String name; private String sex; }
public class Bean44 { private Integer id; private String name; private String sex; private Integer age; }
public class Bean55 { private String password; }
<?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"
default-lazy-init="true">
<!-- 基礎類型與集合注入 -->
<bean id="bean11" class="com.jadyer.model.Bean11">
<property name="intValue" value="123"/> <!-- 自動轉型 -->
<property name="strValue" value="Hello_Spring"/>
<property name="arrayValue">
<list><value>array11</value><value>array22</value></list>
</property>
<property name="listValue">
<list><value>list11</value><value>list22</value></list>
</property>
<property name="setValue">
<set><value>set11</value><value>set22</value></set>
</property>
<property name="mapValue">
<map>
<entry key="key11" value="value11"/>
<entry key="key22" value="value22"/>
</map>
</property>
<!-- 日期注入:格式需與 PropertyEditor 一致 -->
<property name="dateValue" value="2010年06月04日"/>
</bean>
<!-- 關聯對象注入 -->
<bean id="bean22" class="com.jadyer.model.Bean22">
<property name="bean33" ref="bean33"/>
<property name="bean44" ref="bean44"/> <!-- 對應 setBean44() -->
<property name="bean55" ref="bean55"/>
</bean>
<bean id="bean55" class="com.jadyer.model.Bean55">
<property name="password" value="123"/>
</bean>
</beans>
<beans ...>
<!-- 定義抽象父 Bean,abstract="true" 表示不實例化 -->
<bean id="abstractBean" abstract="true">
<property name="id" value="2"/>
<property name="name" value="張起靈"/>
<property name="sex" value="男"/>
</bean>
<!-- 子 Bean 繼承父 Bean 配置,僅覆蓋差異屬性 -->
<bean id="bean33" class="com.jadyer.model.Bean33" parent="abstractBean"/>
<bean id="bean44" class="com.jadyer.model.Bean44" parent="abstractBean">
<property name="age" value="26"/>
</bean>
</beans>
優勢:消除重複配置,修改公共屬性只需改一處;子 Bean 仍可覆蓋任意屬性。
<beans ...>
<!-- 1. 定義編輯器 Bean -->
<bean id="utilDatePropertyEditor" class="com.jadyer.util.UtilDatePropertyEditor">
<property name="pattern" value="yyyy年MM月dd日"/>
</bean>
<!-- 2. 向容器註冊:java.util.Date 類型統一使用該編輯器 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value-ref="utilDatePropertyEditor"/>
</map>
</property>
</bean>
</beans>
package com.jadyer.util;
import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UtilDatePropertyEditor extends PropertyEditorSupport {
private String pattern;
public void setPattern(String pattern) { this.pattern = pattern; }
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
Date date = new SimpleDateFormat(pattern).parse(text);
setValue(date); // 關鍵:將轉換後的 Date 對象設置進去
} catch (ParseException e) {
throw new IllegalArgumentException("日期解析失敗: " + text, e);
}
}
}
原理:Spring 容器啟動時,遇到 <property name="dateValue" value="..."/>,會查找針對 java.util.Date 的 PropertyEditor,調用 setAsText() 將字符串轉為 Date 對象後再注入。
主配置中的 default-lazy-init="true" 使所有 Bean 採用「按需創建」模式:
getBean() 時才創建,啟動更快,但錯誤發現延後。<bean lazy-init="true"> 精確控制。package com.jadyer.junit;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jadyer.model.Bean11;
import com.jadyer.model.Bean22;
public class PropertyInjectionTest extends TestCase {
private ApplicationContext factory;
@Override
protected void setUp() {
// 通配符加載多個配置文件,合並為一個上下文
factory = new ClassPathXmlApplicationContext("applicationContext-*.xml");
}
public void testInjection11() {
Bean11 bean = (Bean11) factory.getBean("bean11");
System.out.println("intValue: " + bean.getIntValue());
System.out.println("strValue: " + bean.getStrValue());
System.out.println("arrayValue: " + java.util.Arrays.toString(bean.getArrayValue()));
System.out.println("listValue: " + bean.getListValue());
System.out.println("setValue: " + bean.getSetValue());
System.out.println("mapValue: " + bean.getMapValue());
System.out.println("dateValue: " + bean.getDateValue()); // 輸出標準 Date 格式
}
public void testInjection22() {
Bean22 bean = (Bean22) factory.getBean("bean22");
System.out.println("bean33: " + bean.getBean33().getName());
System.out.println("bean44 age: " + bean.getBean44().getAge());
System.out.println("bean55 pwd: " + bean.getBean55().getPassword());
}
}
| 場景 | XML 寫法 | 關鍵點 |
|---|---|---|
| 基本類型 | <property name="x" value="123"/> | Spring 內置編輯器自動轉型 |
| 數組 / List | <list><value>...</value></list> | <list> 同時適用於數組與 List |
| Set | <set><value>...</value></set> | 自動去重 |
| Map | <map><entry key="k" value="v"/></map> | Key/Value 均可為引用 |
| 日期類型 | <property name="d" value="2010年06月04日"/> | 需自定義 PropertyEditor + CustomEditorConfigurer |
| 引用對象 | <property name="obj" ref="beanId"/> | ref 指向目標 Bean 的 id/name |
| 公共配置復用 | abstract="true" + parent="..." | 父 Bean 不實例化,子 Bean 繼承並可覆蓋 |
@Autowired 配合構造器最清晰;Setter 注入用於可選依賴。<util:list> 等命名空間(Spring 2.5+),語義更清晰,支持跨文件引用。LocalDate/LocalDateTime,Spring 3.4+ 內置支持,無需自定義編輯器。default-lazy-init="true",啟動階段暴露配置錯誤更安全。Spring 屬性注入機制強大且靈活,掌握基本類型、集合、日期、父 Bean 抽取、自定義編輯器這五大核心場景,足以應對 95% 以上的業務建模需求。隨著 Spring 版本演進,注解驅動(@Value、@ConfigurationProperties)逐漸取代 XML,但理解底層 XML 機制對排查疑難雜症、維護舊項目仍具不可替代的價值。