**作者正在学习 Spring IOC/DI , 本文基于 Spring XML 配置方式,系统整理依赖注入的概念、三种注入方式及复杂类型的注入,适合正在学习 Spring IOC/DI 的同学参考。


一、什么是依赖注入(DI)?

在讲注入方式之前,先搞清楚两个概念的关系:

  • IOC(控制反转):把对象的创建权交给 Spring,降低程序间的耦合
  • DI(依赖注入,Dependency Injection):在当前类需要用到其他类的对象时,由 Spring 负责注入,我们只需要在配置文件中说明依赖关系

简单说:IOC 负责创建对象,DI 负责维护对象之间的依赖关系。 两者是同一件事的两个角度,不是两个独立的功能。

能注入的数据类型

DI 支持注入三类数据:

类型说明
基本类型和 StringintdoubleString
其他 bean 类型在配置文件或注解中配置过的 bean 对象
复杂类型/集合类型数组、List、Set、Map、Properties

注入的方式

共有三种:

  1. 构造函数注入
  2. set 方法注入(默认推荐)
  3. 注解注入(本文暂不涉及)

二、构造函数注入

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 后首字母小写(如 setUsernameusername
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 会自动做类型转换。MapProperties 类似,都是键值对,但 Properties 的 key 和 value 只能是 String。


五、小结

注入方式标签适用场景
构造函数注入<constructor-arg>必填字段,创建即完整
set 方法注入<property>一般场景,灵活可选
复杂类型注入<array>/<list>/<set>/<map>/<props>集合、数组字段

三种方式可以混用,根据实际需求选择。实际项目中 set 方法注入最为常见,构造函数注入在需要保证对象不变性时使用。


如果本文对你有帮助,欢迎点赞收藏。有问题欢迎在评论区交流!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐