Spring 依赖注入(DI)详解 [Spring][依赖注入的 6 种实现方式][setter注入][构造器注入][注解注入][自动装配注入][静态工厂注入][实例工厂注入]

您的“关注”和“点赞”,是信任,是认可,是支持,是动力......

如意见相佐,可留言。
本人必将竭尽全力试图做到准确和全面,终其一生进行修改补充更新。

1 依赖注入概述

依赖注入,英文叫做 Dependency Injection,简称 DI

DI 和 IoC (《Spring IoC 容器详解》)含义相同,它们是从两个角度描述的同一个概念、做同一件事情。

当某个 Java 实例需要另一个 Java 实例时,使用 Spring 之前都是由调用者创建(使用 new 关键字获得被调用者实例)被调用者的实例,而使用 Spring 框架后,被调用者的实例不再由调用者创建,而是由 Spring IoC 容器创建,这称为控制反转,也即 IoC。

Spring IoC 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring IoC 容器获得被调用者实例,这称为依赖注入,也即 DI。

Bean 的依赖注入方式,或叫 Bean 的装配方式,有多种形式,比如接下来要介绍的基于 XML 的依赖注入(有两种实现方式:Setter Injection 设置注入和 Constructor Injection 构造器注入)、基于 Annotation 的依赖注入、基于自动装配、基于静态工厂的方式的依赖注入和基于实例工厂的方式的依赖注入等。

2 依赖注入的多种方式(Bean 的装配方式)

2.1 基于 XML 的依赖注入

2.1.1 Setter Injection(设置方法注入)

基于设置方法注入,也可以叫做setter方法注入,这是最简单的注入方式。

指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。

案例实操,走你!

目的:在UserService接口的实现类UserServiceImpl中用setter方法初始化UserDao的对象。

具体步骤如下所示,

第一步:在com.manongajie.service包下创建UserService接口。如下图所示:

在这里插入图片描述

第二步:在com.manongajie.serviceimpl包下创建接口UserService的实现类UserServiceImpl。在类中声明userDao变量,但是没有初始化它,这里就必须要 setter 方法(是 IoC 容器的注入入口。相当于 IoC 容器调用 setter 方法初始化 userDao 变量)了,如下图所示:

在这里插入图片描述

第三步:在 applicationContext.xml配置文件中添加配置信息。<property>标签中的 name属性值为setXxx()方法的参数(也叫依赖项);ref属性值为 Bean 的nameid值。添加内容,如下图所示:

在这里插入图片描述

第四步:编写测试类并使用JUnit 测试运行。在com.manongajie.test包下的SpringTest类中创建test2()方法。如下图所示:

在这里插入图片描述

从上图中可以看出,使用 Spring IoC 容器获取 userService的实例后,调用了该实例的 addUser()方法,在该方法中又调用了 UserDao 接口的实现类 UserDaoImpl 中的 save()方法(userDao 对象就是 IoC 容器自动调用 setter 方法注入的。本例中 setter 方法为 setUserDao() 方法)。

2.1.2 Constructor Injection(构造器注入)

指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法(本文本小节以带参数的构造器进行介绍)实现,每个参数代表一个依赖,Spring 容器会根据 bean 中指定的构造方法参数来决定调用哪个构造函数。

案例实操,走你!

目的:在UserDaoImpl类中创建成员变量userService,使用UserDaoImpl类的构造器注入userService 实例,也就是说 spring 容器在创建 UserDaoImpl类的实例时,也要将 userService实例作为构造器的参数值传到UserDaoImpl类中(配置文件中操作,使用<constructor-arg>标签,ref属性表示 Bean 的 id)。

具体步骤如下所示,

第一步:直接在之前的项目中操作,在UserDaoImpl类中创建一个成员变量userService,构造器UserService(UserService userService),如下图所示:

在这里插入图片描述

第二步:配置配置文件,如下图所示:

在这里插入图片描述

第三步:编写测试类和JUnit 测试运行。如下图所示:

在这里插入图片描述

从上图中可以看出,通过 Spring 容器获取UserDao的实例后,调用了该实例的save()方法,在该方法中又调用了UserService接口的实现类UserServiceImpl中的addUser()方法(userService 对象就是 spring 容器通过构造器注入的。)

2.2 基于 Annotation(注解)的依赖注入

2.2.1 基于 Annotation(注解)的依赖注入概述

如果应用中 Bean 的数量较多,基于 XML 配置文件实现 Bean 的装配会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。

基于 Annotation(注解)的依赖注入就可以解决这个问题。

2.2.2 常用注解概述

  • @Component
    可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

  • @Repository
    用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

  • @Service
    通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

  • @Controller
    通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

  • @Autowired(在本文 《2.3 基于自动装配的依赖注入》 小节详细介绍)
    用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。

  • @Resource
    其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。

    @Resource 中有两个重要属性:name 和 type。

    Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。
    如果指定 name 属性,则按实例名称进行装配;
    如果指定 type 属性,则按 Bean 类型进行装配。
    如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。

  • @Qualifier
    与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

2.3.3 案例实操,走起

第一步:导入关键 JAR 包。然后创建 DAO 层接口,在com.manongajie.dao包下创建PersonDao接口,并添加add()方法。如下图所示:

在这里插入图片描述
第二步:创建 DAO 层接口的实现类。在com.manongajie.daoimpl包下创建PersonDao接口的实现类PersonDaoImpl,并添加 DAO 层 add()方法。对类使用@Repository 注解将 PersonDaoImpl 类标识为 Spring 中的 Bean,其写法相当于配置文件中 <bean id="personDao" class="com.manongajie.daoimpl.PersonDaoImpl"/> 的书写。如下图所示:

在这里插入图片描述
第三步:创建 Service 层接口。图下图所示:

在这里插入图片描述
第四步:创建 Service 层接口的实现类。如下图所示:

在这里插入图片描述

第五步:创建 Action 控制层。如下图所示:

在这里插入图片描述
第六步:创建 Spring 配置文件。需要添加更多的约束文件,如何把约束文件交给 Eclipse 管理,请参见博文《开启 Spring 之旅:第一个 Spring 程序 !》

在这里插入图片描述

第七步:创建测试类和 JUnit 运行测试。如下图所示:

在这里插入图片描述

从上图中可以看出,DAO 层、Service 层和 Action 层的 add()方法都成功输出了结果。
So,使用 Annotation 装配 Bean 的方式已经成功实现了。

2.3 基于自动装配的依赖注入

本文篇幅太长了,另写一文。

请参见博文《Spring 基于自动装配的依赖注入详解》

2.4 静态工厂注入

需要提供一个静态工厂方法 createBean(),创建 Bean 的实例(是指createBean()方法返回哪个类的实例,就创建哪个类的实例)。
需要在配置文件中,使用 <bean> 标签的 factory-method 属性,用于告诉 Spring 容器调用工厂类中的 createBean() 方法获取 Bean 的实例。

案例演示,如下所示:

第一步:创建实体类 Person。在com.manongajie.static_factory包下创建。

在这里插入图片描述
第二步:创建静态工厂类MyBeanFactory,并在类中创建createBean()静态方法,用于创建 Bean 的实例。

在这里插入图片描述
第三步:创建配置文件。

在这里插入图片描述
第四步:创建测试类和JUnit 测试运行。

在这里插入图片描述
从以上运行结果可以看出,使用静态工厂的方式也成功对 Bean 进行了实例化。

2.5 实例工厂注入

使用这种方式,工厂类不再使用静态方法创建 Bean 的实例,而是直接在成员方法中创建 Bean 的实例。

也就是说需要一个成员方法createBean()来创建 Bean 的实例。

在配置文件中,需要实例化的 Bean 也不是通过 class 属性直接指向其实例化的类,而是通过 factory-bean 属性配置一个实例工厂,然后使用 factory-method 属性确定使用工厂中的哪个方法。

案例演示,如下所示:

第一步:创建实体类 Person,并在类中添加say()方法。

在这里插入图片描述
第二步:创建实例工厂类,并添加成员方法createBean()

在这里插入图片描述
第三步:创建配置文件。

在这里插入图片描述
第四步:创建测试类和JUnit 测试运行。

在这里插入图片描述
从以上运行结果可以看出,使用实例工厂的方式也是可以对 Bean 进行实例化的哦。。

【文章其他地址】

微信公众号:码农阿杰

CSDN 博客

【参考资料】

spring

Artifact Repository Browser

Apache Commons

posted @ 2020-05-20 19:54  码农阿杰  阅读(1064)  评论(0编辑  收藏  举报