从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

    • Spring 介绍
    • 程序中的耦合
    • IoC 的概念和作用
    • Spring 中的依赖注入
      • 什么是依赖注入
      • 构造函数注入
      • set 方法注入
      • 集合数据注入
      • 源码
    • 基于注解的 IoC
    • 使用基于 XML 的 IoC 完成单表的 CRUD
    • 使用基于注解的 IoC 完成单表的 CRUD
    • IoC 的纯注解配置
    • Spring 整合 Junit
    • 事务问题
    • 代理模式
    • AOP 的概念和入门
    • 基于注解的 AOP
    • Spring 的 JdbcTemplate
    • JdBCDaoSupport
    • 基于 XML 的 AOP 实现事务控制
    • 基于注解的 AOP 实现事务控制
    • Spring 的事务控制
    • 基于 XML 的声明式事务控制
    • 基于注解的声明式事务控制
    • 纯注解实现事务控制
    • Spring 编程式事务控制
    • Spring5 新特性
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • Spring
2023-05-08
目录

Spring 中的依赖注入

# 30.Spring 中的依赖注入

之前我们仅仅是消除了表现层对于 service 层的依赖;而 service 层对于 dao 层的依赖,我们还没有消除,例如在 service 层中还有一个 new dao 对象的代码,本文我们就来讲讲如何用注入的方式,降低耦合性

# 什么是依赖注入

依赖注入,全称 Dependency Injection,简称 DI

IoC 的作用:降低程序间的耦合,依赖关系的管理,对象的创建,就交由 Spring;在当前类需要用到其他类的对象时,由 Spring 为我们提供,我们只需要在配置文件中说明依赖关系,就可以在程序中使用了。

例如我们的 service 层依赖 dao 层,这就是一种依赖关系。

依赖关系的维护,就称之为依赖注入。也就是说,某个对象 A 的创建,依赖另一个对象 B,而我们需要将对象 B“注入”到对象 A 中,这样对象 A 才能创建成功。

能注入的数据有三类:

  • 基本类型和 String
  • 其他 bean 类型(在配置文件中或者注解配置过的 bean)
  • 复杂类型/集合类型 ‍ 注入的方式有三种:
  1. 使用构造函数提供
  2. 使用 set 方法提供
  3. 使用注解提供(后续会讲)

# 构造函数注入

接下来我们来演示如何注入

  1. 在 service 层中,新建几个成员变量,其中包含了基本类型和 String 类型,和其他 bean 类型(Date 也可以用 Bean 来管理)
  2. 新建构造函数,需要传入成员变量的值进行初始化
public class AccountServiceImpl implements IAccountService {


    private String name;
    private Integer age;
    private Date birthday;

    public AccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了" + name + "," + age + "," + birthday);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

注意:如果是经常变化的数据,并不适用于注入的方式。比如一个注册用户的功能,其用户名、密码等信息的每个人都不同的,用配置文件的方式肯定不合适;后续我们对于这种情况怎么处理。本文我们主要讲解依赖之间的关系,Spring 是怎么管理的。

然后我们配置注入,使用的标签是 constructor-arg,标签出现的位置在 bean 标签的内部。标签中的属性:

  • type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
  • index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从 0 开始的
  • name: 用于指定要注入的数据给构造函数中指定名称的参数赋值
  • value: 用于提供基本类型和 String 类型的数据
  • ref: 用于指定其他的 bean 类型数据,它指的是在 Spring 的 IoC 核心容器中出现过的 bean 对象

其中,前 3 个属性是用来定位给哪个属性赋值,后面 2 个属性是决定赋什么值 ‍ 例如,我们想给 String 属性赋值:

<bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
   <constructor-arg type="java.lang.String" value="Peterjxl"/>
</bean>
1
2
3

但如果参数列表中有两个 String 类型,就判断不出到底是给哪个 String 赋值,因此 type 属性通常不能独立完成注入的功能。

如果我们可以用 index 属性,由于通过索引位置能确定一个变量,能独立完成注入,但这种方式要记住索引的位置,如果方法参数有变化还得改,也比较麻烦。

因此,我们一般是用 name 属性,来定位变量。 ‍ 至此,我们可以给 name 和 age 赋值了:

<bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="Peterjxl"/>
        <constructor-arg name="age" value="18"/>
</bean>
1
2
3
4

虽然 value 的内容是字符串,但是 Spring 很智能,能帮我们将字符串 18 转为 Integer 类型; ‍ 但对于日期类型这种比较复杂的,Spring 就无能为例了,如果我们这样配置:

<bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="Peterjxl"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg name="birthday" value="2022-5-21"/>
</bean>
1
2
3
4
5

‍ 运行是会报错的:

Error creating bean with name 'accountService' defined in class path resource [bean.xml]:Unsatisfied dependency expressed through constructor parameter 2: Could not convert argument value of type [java.lang.String] to required type [java.util.Date]
1

大意:构造方法中,索引位置为 2 的参数赋值失败,不能转换 String 类型为 Date 类型 ‍ 解决方法一:我们将 Date 也用容器来管理,并用 ref 来引用:

<bean id="now" class="java.util.Date"/>

<bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="Peterjxl"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg name="birthday" ref="now"/>
</bean>
1
2
3
4
5
6
7

此时,是可以正常运行的:

service中的saveAccount方法执行了Peterjxl,18,Mon May 01 21:36:40 CST 2023
1

使用构造函数注入的优缺点:

  • 优势:在获取 bean 对象时,注入数据是必须的操作,否则对象无法创建成功
  • 弊端:改变了 bean 对象的实例化方式,使我们在创建对象时,即使用不到这些数据,也必须提供

所以我们一般比较少用构造函数注入的方式。

# set 方法注入

接下来我们演示使用 set 方法注入的方式,我们创建一个新的类 AccountServiceImpl2:

public class AccountServiceImpl2 implements IAccountService {
    private String name;
    private Integer age;
    private Date birthday;

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了" + name + "," + age + "," + birthday);
    }
}
1
2
3
4
5
6
7
8
9
10

请读者自行生成 getter 和 setter ‍ 接下来我们配置 bean.xml,使用 property 来完成注入。property 标签出现的位置:bean 标签的内部

标签的属性:

  • name: 用于指定注入时所调用的 set 方法名称(去掉方法名前面的 set,然后首字母小写)
  • value: 用于提供基本类型和 String 类型的数据
  • ref: 用于指定其他的 bean 类型数据,它指的是在 Spring 的 IOC 核心容器中出现过的 bean 对象 ‍
<!-- set方法注入 -->
<bean id="accountService2" class="com.peterjxl.service.impl.AccountServiceImpl2">
    <property name="name" value="Peterjxl"/>
    <property name="age" value="18"/>
    <property name="birthday" ref="now"/>
</bean>
1
2
3
4
5
6

‍ 然后我们在 Client 中演示:

IAccountService as2 = (IAccountService)ac.getBean("accountService2");
as2.saveAccount();
1
2

‍ 运行结果:

service中的saveAccount方法执行了Peterjxl,18,Mon May 01 21:49:32 CST 2023
1

‍ 使用 set 方法注入的优缺点:

优势:创建对象时没有明确的限制,可以直接使用默认构造函数(注意,如果有其他的有参的构造方法,那么得定义一个无参的构造方法)

弊端:如果有某个成员必须有值,但没有配置对应的 property 标签,则获取对象时 set 方法没有执行,导致对象创建出来是不能用的

总而言之,我们经常用的 set 方式注入

# 集合数据注入

最后我们来说下集合类型的数据如何注入。我们创建一个新的类 AccountServiceImpl3,并加入一些集合类型的成员变量:

public class AccountServiceImpl3 implements IAccountService {

    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProps;


    @Override
    public void saveAccount() {
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

请注意生成 setter 方法,getter 方法我们暂时不用。 ‍ 接下来我们配置 bean.xml:我们在 property 标签里使用对应的集合标签,来完成注入:

<!-- 复杂类型/集合类型的注入-->
<bean id="accountService3" class="com.peterjxl.service.impl.AccountServiceImpl3">
    <property name="myStrs">
        <array>
            <value>aaa</value>
            <value>bbb</value>
            <value>ccc</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>aaa</value>
            <value>bbb</value>
            <value>ccc</value>
        </list>
    </property>
    <property name="mySet">
        <set>
            <value>aaa</value>
            <value>bbb</value>
            <value>ccc</value>
        </set>
    </property>
    <property name="myMap">
        <map>
            <entry key="aaa" value="111"/>
            <entry key="bbb">
                <value>222</value>
            </entry>
        </map>
    </property>

    <property name="myProps">
        <props>
            <prop key="testC">111</prop>
            <prop key="testD">222</prop>
        </props>
    </property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

运行结果:

[aaa, bbb, ccc]
[aaa, bbb, ccc]
[aaa, bbb, ccc]
{aaa=111, bbb=222}
{testD=222, testC=111}
1
2
3
4
5

其实,对应的标签可以互换。

用于给 List 结构集合类型属性注入的标签是:<list> <array> <set>

用于给 Map 集合类型属性注入的标签是: <map> <props>

结构相同,标签可以互换。例如我们给数组注入时,使用的是 array 标签,其实用 list 标签也可以:

<property name="myStrs">
    <list>
        <value>aaa</value>
        <value>bbb</value>
        <value>ccc</value>
    </list>
</property>
1
2
3
4
5
6
7

# 源码

本项目已将源码上传到 GitHub (opens new window) 和 Gitee (opens new window) 上。并且创建了分支 demo3,读者可以通过切换分支来查看本文的示例代码。

上次更新: 2025/6/3 09:31:54
IoC 的概念和作用
基于注解的 IoC

← IoC 的概念和作用 基于注解的 IoC→

最近更新
01
新闻合订本 2025-10
10-31
02
2025 年 10 月记
10-30
03
用 AI 批量优化思源笔记排版
10-15
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式