Spring 依赖注入(DI)详解:三种注入方式
**作者正在学习 Spring IOC/DI , 本文基于 Spring XML 配置方式,系统整理依赖注入的概念、三种注入方式及复杂类型的注入,适合正在学习 Spring IOC/DI 的同学参考。
一、什么是依赖注入(DI)?
在讲注入方式之前,先搞清楚两个概念的关系:
- IOC(控制反转):把对象的创建权交给 Spring,降低程序间的耦合
- DI(依赖注入,Dependency Injection):在当前类需要用到其他类的对象时,由 Spring 负责注入,我们只需要在配置文件中说明依赖关系
简单说:IOC 负责创建对象,DI 负责维护对象之间的依赖关系。 两者是同一件事的两个角度,不是两个独立的功能。
能注入的数据类型
DI 支持注入三类数据:
| 类型 | 说明 |
|---|---|
| 基本类型和 String | int、double、String 等 |
| 其他 bean 类型 | 在配置文件或注解中配置过的 bean 对象 |
| 复杂类型/集合类型 | 数组、List、Set、Map、Properties |
注入的方式
共有三种:
- 构造函数注入
- set 方法注入(默认推荐)
- 注解注入(本文暂不涉及)
二、构造函数注入
2.1 基本用法
通过 <constructor-arg> 标签,在 bean 创建时向构造函数传参。
涉及的标签属性:
| 属性 | 说明 |
|---|---|
name | 指定构造函数中对应参数的名称(最常用) |
value | 提供基本类型和 String 类型的值 |
ref | 引用容器中已存在的其他 bean 对象 |
实体类:
public class User {
private String username;
private Integer age;
private Date birthday;
public User(String username, Integer age, Date birthday) {
this.username = username;
this.age = age;
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
XML 配置:
<bean id="user" class="com.xq.pojo.User">
<constructor-arg name="username" value="eric"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" value="2022-10-10"></constructor-arg>
</bean>
2.2 踩坑:Date 类型注入报错
直接用 value 注入日期会报错:
Cannot convert value of type 'java.lang.String' to required type 'java.util.Date'
原因:XML 中所有值本质上都是字符串,Spring 可以自动把 "18" 转成 Integer,但无法把 "2022-10-10" 转成 Date。
解决方案:把 Date 单独配成一个 bean,再用 ref 引用:
<bean id="user" class="com.xq.pojo.User">
<constructor-arg name="username" value="eric"></constructor-arg>
<constructor-arg name="age" value="12"></constructor-arg>
<constructor-arg name="birthday" ref="date"></constructor-arg>
</bean>
<bean id="date" class="java.util.Date"></bean>
2.3 优缺点总结
| 说明 | |
|---|---|
| 优势 | 获取 bean 时注入数据是必须的,对象一旦创建就保证数据完整 |
| 弊端 | 即使某些字段用不到,创建对象时也必须传入所有参数,强制依赖构造函数签名 |
三、set 方法注入
3.1 基本用法
通过 <property> 标签,调用对应字段的 setter 方法完成注入。这是 Spring 最常用的注入方式。
涉及的标签属性:
| 属性 | 说明 |
|---|---|
name | 对应 setter 方法名去掉 set 后首字母小写(如 setUsername → username) |
value | 提供基本类型和 String 类型的值 |
ref | 引用容器中已存在的其他 bean 对象 |
实体类(需提供 setter 方法):
public class User {
private String username;
private Integer age;
private Date birthday;
public void setUsername(String username) { this.username = username; }
public void setAge(Integer age) { this.age = age; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
// getter 省略...
}
XML 配置:
<bean id="user" class="com.xq.pojo.User">
<property name="username" value="eric"></property>
<property name="age" value="38"></property>
<property name="birthday" ref="date"></property>
</bean>
<bean id="date" class="java.util.Date"></bean>
3.2 与构造函数注入的对比
| 构造函数注入 | set 方法注入 | |
|---|---|---|
| 注入时机 | 对象创建时 | 对象创建后 |
| 是否强制 | 是(无默认空参构造时必须传参) | 否(字段可选注入) |
| 灵活性 | 低 | 高 |
| 推荐场景 | 必填字段、不可变对象 | 一般场景(Spring 默认推荐) |
四、复杂类型注入
当字段类型是集合或数组时,需要使用对应的子标签来注入。
实体类:
public class Animal {
private String[] strs;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
private Properties pros;
// setter 方法省略...
@Override
public String toString() {
return "Animal{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", set=" + set +
", map=" + map +
", pros=" + pros +
'}';
}
}
XML 配置(完整示例):
<bean id="animal" class="com.xq.pojo.Animal">
<!-- 注入数组类型 -->
<property name="strs">
<array>
<value>str1</value>
<value>str2</value>
<value>str3</value>
</array>
</property>
<!-- 注入 List -->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- 注入 Set -->
<property name="set">
<set>
<value>s1</value>
<value>s2</value>
<value>s3</value>
</set>
</property>
<!-- 注入 Map -->
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
<entry key="k3" value="v3"></entry>
</map>
</property>
<!-- 注入 Properties -->
<property name="pros">
<props>
<prop key="p1">v1</prop>
<prop key="p2">v2</prop>
<prop key="p3">v3</prop>
</props>
</property>
</bean>
各集合类型对应的子标签速查
| 字段类型 | 使用的子标签 | 子元素 |
|---|---|---|
数组 [] | <array> | <value> |
List | <list> | <value> |
Set | <set> | <value> |
Map | <map> | <entry key="" value=""> |
Properties | <props> | <prop key="">值</prop> |
注意:
List和数组在结构上是可以互换子标签的(<list>可以注入给数组字段,<array>也可以注入给 List 字段),Spring 会自动做类型转换。Map和Properties类似,都是键值对,但Properties的 key 和 value 只能是 String。
五、小结
| 注入方式 | 标签 | 适用场景 |
|---|---|---|
| 构造函数注入 | <constructor-arg> | 必填字段,创建即完整 |
| set 方法注入 | <property> | 一般场景,灵活可选 |
| 复杂类型注入 | <array>/<list>/<set>/<map>/<props> | 集合、数组字段 |
三种方式可以混用,根据实际需求选择。实际项目中 set 方法注入最为常见,构造函数注入在需要保证对象不变性时使用。
如果本文对你有帮助,欢迎点赞收藏。有问题欢迎在评论区交流!
更多推荐



所有评论(0)