从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • 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新特性
    • Java
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • 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,读者可以通过切换分支来查看本文的示例代码。

在GitHub上编辑此页 (opens new window)
上次更新: 2023/7/3 22:46:17
IoC的概念和作用
基于注解的IoC

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

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式