1.依赖注入

(1) 依赖注入(DI)的概念:某个bean的依赖项,由容器来负责注入维护,而非我们自己手动去维护,以此来达到bean之间解耦的目的,如下

//情况一:不使用依赖注入
public class A {} public class B {
//B依赖了A对象,这种依赖关系是由我们自己手动来维护的,编码于代码之中,是强依赖
private A a = new A();
} //情况二:使用依赖注入
@Component
public class A {} @Component
public class B {
//B依赖了A对象,这个A对象是由容器来提供的,无需我们关心
@Autowired
private A a;
}

(2) 依赖注入的两种方式

  • 基于构造函数的依赖注入:容器通过调用带有参数的构造函数来完成依赖项的注入,其中构造函数中的每个参数都代表一个依赖项,其参数解析规则如下
//例一:
//ExampleA继承自ExampleB
public class ExampleA extends ExampleB {} public class ExampleB {} public class Combine {
//Combine依赖了ExampleA和ExampleB
public Combine(ExampleA a, ExampleB b) { }
} <!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean> <bean id="combine" class="cn.example.spring.boke.Combine">
<!-- Spring会按照类型进行精确匹配,因此1会被注入到构造函数的第二个参数b中,而2会被注入到构造函数的第一个参数a中,此时与这些构造函数标签声明的先后顺序无关 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>
</beans> //例二:
//将Combine构造函数变更一下
public class Combine {
//构造函数参数均为ExampleB
public Combine(ExampleB b1, ExampleB b2) { }
} <!-- xml配置文件 -->
<bean id="combine" class="cn.example.spring.boke.Combine">
<!-- 此时无法进行精确匹配,因为构造函数参数均为ExampleB,这时就会按照这些构造函数标签声明的先后顺序进行依赖项的注入,
结果为1会被注入到构造函数的第一个参数b1中,而2会被注入到构造函数的第二个参数b2中,如果将这两个构造函数标签的声明顺序颠倒一下,结果也会随之相反 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean> //例三
public class ExampleC {
public ExampleC(int number, String str) { }
} <!-- xml配置文件1 -->
<beans ...>
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 此时的Spring无法判断2000或50000究竟是一个int还是一个String,因此它首先会采取例二中的办法,按构造函数标签的声明顺序进行注入,结果为number被注入2000,str被注入字符串50000 -->
<constructor-arg value="2000"></constructor-arg>
<constructor-arg value="50000"></constructor-arg>
</bean>
</beans> <!-- xml配置文件2 -->
<beans ...>
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 可以使用type属性,指定希望注入的参数的类型,来解决歧义问题,结果number被注入50000,str被注入字符串2000,但是如果构造函数参数列表中有多个类型相同的参数,则又会按照构造函数标签的声明顺序进行注入 -->
<constructor-arg type="java.lang.String" value="2000"></constructor-arg>
<constructor-arg type="int" value="50000"></constructor-arg>
</bean>
</beans> <!-- xml配置文件3 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 使用index属性,按构造函数参数索引位置进行注入,index属性为0的构造函数标签的值会被注入到构造函数的第一个参数,以此类推 -->
<constructor-arg index="1" value="50000"></constructor-arg>
<constructor-arg index="0" value="2000"></constructor-arg>
</bean> <!-- xml配置文件4 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 使用name属性,按照构造函数参数名称进行注入,不过要注意,Spring要求使用这种方式时必须开启debug flag或结合@ConstructorProperties注解一起使用,详见官方文档(此处存疑,因为在我本地可直接使用下面这种方式,无需其他配置) -->
<constructor-arg name="str" value="50000"></constructor-arg>
<constructor-arg name="number" value="2000"></constructor-arg>
</bean>
  • 基于setter方法的依赖注入:在实例化bean之后,容器会调用setter方法来注入依赖项
public class ExampleA {}

public class ExampleC {
private ExampleA exampleA; //使用setter方法注入依赖项exampleA
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
} <!-- 方法一:使用自动装配(autowire),即容器自动去依赖项来进行注入 -->
<beans ...>
<!-- 被依赖项exampleA -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean> <!-- 注意,在基于xml的配置中,要使容器进行基于setter的依赖注入,则要指定autowire属性为 byName或byType,如果不指定,则autowire属性默认为no,即不进行依赖注入 -->
<bean id="exampleC" class="cn.example.spring.boke.ExampleC" autowire="byType"></bean>
</beans> <!-- 方法二:使用手动装配 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean> <bean id="exampleC" class="cn.example.spring.boke.ExampleC">
<!-- 使用property标签手动注入,name即属性名称,ref代表注入对象 -->
<property name="exampleA" ref="exampleA"></property>
</bean>
</beans>

(3) 在依赖注入过程中,Spring会结合PropertyEditor一起使用,来将属性从一种类型转换为另一种类型,我们也可以利用PropertyEditor进行数据类型转换工作

(4) Spring对依赖项的解析规则:

  • 创建ApplicationContext,并填充bean的配置元数据
  • bean与bean之间的依赖关系蕴含于成员变量,构造函数参数等之中,当一个bean被创建时,它的依赖项会被提供
  • 每个成员变量值或构造函数参数值要么是我们所提供的值,要么是对容器中另一个bean的引用
  • Spring会对成员变量值或构造参数值进行确切的类型转换,以得到实际的类型,比如 <constructor-arg value="2000" ... 中的值2000,Spring会根据实际情况将其转换为字符串2000或int值2000

(5) Spring在容器创建完成后便会对bean的配置元数据进行检验(此时不会进行依赖项的注入,直到bean被成功创建后才会进行注入)。单例bean在默认情况下会被提前实例化(即在容器被创建后创建),而其他作用域的bean只有在需要的时候才会被创建(懒加载)。在实际创建bean时,Spring会尽可能晚设置该bean的属性值和依赖项,因此Spring容器可能在创建的时候正常但在之后的使用中会产生异常

(6) 在不存在循环依赖的情况下,bean A如果有一个依赖项为bean B,则Spring容器会在调用bean A的setBeanB方法来注入bean B之前会完全配置好bean B,即Bean B的依赖项已被完全注入,其生命周期回调也已被执行,如下

/**
* 提供三个类,ExampleA,ExampleB和ExampleC,其中ExampleA依赖了ExampleB,而ExampleC又依赖了ExampleA,即B->A->C
* 此时,容器会先配置好ExampleB,再注入给ExampleA,而ExampleA配置完毕后,最后会被注入给ExampleC,可观察控制台打印语句的输出顺序
*/
public class ExampleB {
public ExampleB() {
System.out.println("ExampleB构造器...");
}
} public class ExampleA {
public ExampleA() {
System.out.println("ExampleA构造器...");
} private ExampleB exampleB; public void setExampleB(ExampleB exampleB) {
System.out.println("ExampleA设置了" + exampleB);
this.exampleB = exampleB;
}
} public class ExampleC { public ExampleC() {
System.out.println("ExampleC构造器...");
} private ExampleA exampleA; public void setExampleA(ExampleA exampleA) {
System.out.println("ExampleC设置了" + exampleA);
this.exampleA = exampleA;
}
} <!-- xml配置文件 -->
<beans ...>
<bean id="exampleC" class="cn.example.spring.boke.ExampleC">
<property name="exampleA" ref="exampleA"></property>
</bean> <bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="exampleB" ref="exampleB"></property>
</bean> <bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
</beans>

Spring IOC官方文档学习笔记(三)之依赖项的更多相关文章

  1. Spring Framework 官方文档学习(三)之Resource

    起因 标准JDK中使用 java.net.URL 来处理资源,但有很多不足,例如不能限定classpath,不能限定 ServletContext 路径. 所以,Spring提供了 Resource ...

  2. Vue.js官方文档学习笔记(三)创建Vue实例

    创建Vue实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的: var vm=new Vue({ //选项 }) Vue的设计受到了mvvm的启发 当创建一个 Vue 实 ...

  3. Spring 4 官方文档学习(十一)Web MVC 框架

    介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...

  4. Spring Boot 官方文档学习(一)入门及使用

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  5. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

    题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

  6. Spring boot官方文档学习(一)

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  7. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

    本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

  8. Spring 4 官方文档学习(十二)View技术

    关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

  9. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

    接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

  10. Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC

    内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...

随机推荐

  1. Sqoop 之 安装

    Sqoop 之 安装 前言 安装 Sqoop 的前提是已经具备 Java 和 Hadoop 的环境. 一.下载并解压 1) 下载地址:http://mirrors.hust.edu.cn/apache ...

  2. 6.MongoDB系列之特殊索引和集合类型

    1. 地理空间索引及全文搜索 与Elasitcsearch一样,MongoDB同样支持地理空间索引及全文搜索,由于选型常用ES而非MongoDB此处略过 2. TTL索引 首先先了解下固定集合,其类似 ...

  3. Linux基础_4_文件夹管理

    文件路径 . #表示当前目录,同./ .. #表示上级目录,同../ ../../ #表示上上级目录 / #表示根目录 注:文件名长度不超过255个字符 注:.开头为隐藏文件 切换目录 cd #默认切 ...

  4. NLP之TextLSTM(预测单词下一个字母)

    LSTM 目录 LSTM 1.理论 1.1 LSTM与RNN 1.1.1 RNN的缺点 1.1.2 LSTM 1.2 LSTM基本结构 2.实验 2.1 实验步骤 2.2 算法模型 1.理论 1.1 ...

  5. 这才是使用ps命令的正确姿势

    这才是使用ps命令的正确姿势 前言 在linux系统当中我们通常会使用命令去查看一些系统的进程信息,我们最常使用的就是 ps (process status).ps 命令主要是用于查看当前正在运行的程 ...

  6. LcdTools如何实现PX01设置不同的画面不同的背光亮度

    背光驱动分两种原理:恒压模式和恒流模式.恒压背光顾名思义提供恒定电压即可,这种屏正常来讲自带背光驱动电路,只需提供背光工作电压.背光使能和背光调光占空比控制.恒流背光指屏的背光只有纯灯串,需外部提供相 ...

  7. 四、Django中使用celery

    项目跟目录创建celery包,目录结构如下: mycelery/ ├── config.py ├── __init__.py ├── main.py └── sms/ ├── __init__.py ...

  8. 【lwip】08-ARP协议一图笔记及源码实现

    目录 前言 8.1 IP地址与MAC地址 8.2 ARP协议简介 8.3 ARP协议报文 8.4 ARP缓存表 8.4.1 ARP缓存表简介 8.4.2 LWIP中的缓存表 8.4.3 ARP缓存表数 ...

  9. 在博客中实现播放音乐功能(QQ,网易,酷狗,虾米,百度)

    1.在页头head标签里添加: <link rel="stylesheet" href="https://static.likepoems.com/cdn/apla ...

  10. 洛谷 P3201 梦幻布丁 题解

    (这篇题解可能没什么营养,主要是记录一下我用map乱搞启发式合并的神奇做法) 首先我们知道,我们肯定要用一堆集合维护每一种数当前的位置,并支持合并和数连续出现的段数两种操作 我发现这个东西并不好搞,但 ...