20191103 《Spring5高级编程》笔记-第3章
第3章 在Spring中引入IoC和DI
依赖注入是IOC的一种特殊形式,尽管这两个术语经常可以互换使用。
3.1 控制反转和依赖注入
IOC的核心是DI,旨在提供一种更简单的机制来设置组件依赖项,并在整个生命周期中管理这些依赖项。需要某些依赖项的组件通常被称为依赖对象,或者在IOC的情况下被称为目标对象。
IOC可以分解为两种子类型:依赖注入(DI)和依赖查找。
3.2 控制反转的类型
使用依赖查找时,组件必须获取对依赖项的引用,而是用依赖注入时,依赖项将通过IOC容器注入组件。
依赖查找有两种类型:依赖拉取(dependency pull,DL)和上下文依赖查找(contextualized dependency lookup,CDL)。
依赖注入有两种风格:构造函数和setter依赖注入。
3.2.6 setter注入与构造函数注入
使用接口来定义依赖项的能力是setter注入的一个经常被提及的好处,但实际上,应该尽量保持setter仅用于诸如接口。除非完全确定特定业务接口的所有实现都需要一个特定的依赖项,否则让每个实现类定义自己的依赖项并为业务方法保留业务接口。
虽然不应该在业务接口中放置用于依赖项的setter方法,但在业务接口中放置用来获取配置参数的setter和getter方法却是一个好主意。可以将配置参数视为依赖项的特例。
应该根据使用情况选择诸如类型。基于setter的注入预序在不创建新对象的情况下交换依赖项,并且还可以让类选择适当的默认值,而无需显式注入对象。但想要确保将依赖项传递给组件和设计不可变对象时,构造函数注入是一个不错的选择。
3.4 Spring中的依赖注入
3.4.1 bean和BeanFactory
Spring的依赖注入容器的核心是BeanFactory
接口。BeanFactory负责管理组件,包括依赖项以及它们的生命周期。在Spring中,术语bean用于引用由容器管理的任何组件,bean配置由实现BeanDefinition
接口的类的实例表示。bean配置不仅存储有关bean本身的信息,还存储有关它所依赖的bean的信息。
3.4.2 BeanFactory实现
在声明Spring XSD位置时,最好不要包含版本号。这个问题已经由Spring代为处理,因为版本化的XSD文件是通过spring.schema文件的指针配置的。该文件驻留在定义为项目依赖项的spring-beans
模块中。这样做也可以防止升级到新版本的Spring时对所有的bean文件进行修改。
3.4.3 ApplicationContext
在Spring中,ApplicationContext
接口是BeanFactory
的一个扩展。在开发基于Spring的应用程序时,建议通过ApplicationContext接口与Spring进行交互。
3.5 配置ApplicationContext
3.5.2 基本配置概述
<context:component-scan>
标记告诉Spring扫描代码,从而找到使用@Component
、@Controller
、@Service
、@Repository
注解的注入bean以及支持在指定包(及其所有子包)下使用@Autowired
、@Inject
和@Resource
注解的bean。在<context:component-scan>
标记中,可以使用逗号、分号和空格作为分隔符来定义多个包。此外,该标记支持组件扫描的包含和排除,从而实现更细粒度的控制。
3.5.3 声明Spring组件
@ComponentScan
注解与<context:component-scan>
标签等效。
配置类可以使用@ImportResource
从一个或多个XML文件中导入bean定义。
p名称空间
没有在XSD文件中定义,而只存在于Spring Core
中;因此,在schemaLocation属性中没有声明XSD。
c名称空间
没有在XSD文件中定义,而只存在于Spring Core
中;因此,在schemaLocation属性中没有声明XSD。
当有多个构造函数参数或者你的类有多个构造函数时,需要为每个<constructor-arg>
标记指定一个index
属性,以指定构造函数签名中参数的索引,从0开始。
对于注解式的构造函数注入,可以通过将注解@Value
直接应用于目标构造函数方法来避免混淆。
@Autowired
注解只能应用于一个构造函数方法,如果将该注解应用于多个构造函数方法,Spring会在启动时报错。
Spring中支持的第三种依赖注入被称为字段注入。依赖项直接注入字段中,不需要构造函数或setter。字段注入通过使用@Autowired
注解来注解类成员完成。
通过分别使用<property>
和<constructor-args>
标记,可以将注入参数用于setter注入和构造函数注入。
@Valuea
可以直接应用于属性声明语句。
@Value("tc..p1")
private String p1;
被注入的类型不一定时目标bean上所定义的确切类型:类型只需要兼容即可。兼容意味着如果目标bean上声明的类型是一个接口,那么注入类型必须实现此接口。如果声明的类型是一个类,那么注入类型必须是相同类型或子类型。
注入和ApplicationContext嵌套
Spring支持ApplicationContext的
层次结构,一个上下文可以成为另一个上下文的父级。
当父子上下文中存在名称相同的bean时,<ref>
标签通过bean属性获得子上下文中的bean,通过parent
属性获取父上下文中的bean。
注入集合
可以选择<list>
、<map>
、<set>
或<props>
来表示List
、Map
、Set
或Properties
实例。<props>
标记只允许将String
作为值传入。
利用Spring提供的util名称空间
来声明用来存储集合属性的bean,可以极大地简化配置。
对于集合类型注入,必须明确指示Spring通过指定@Resource注解支持的bean名称来执行注入。因为@Autowired
注解的语义定义方式是,始终将数组、集合和映射视为相应bean的集合,而目标bean类型从声明的集合值类型派生。例如,如果一个类具有List<ContentHolder>
类型的属性并且定义了@Autowired
注解,那么Spring会尝试将当前ApplicationContext
中所有ContentHolder
类型的bean注入该属性。
3.5.4 使用方法注入
除了构造函数注入和setter注入,Spring提供的另一个不常用的DI功能是方法注入。Spring方法注入功能有两种形式:查找方法注入(Lookup Method Injection)和方法替换(Method Replacement)。查找方法注入提供了一种机制,bean可以获得它的一个依赖项;而方法替换允许随意替换bean上任何方法的实现,而无需修改原始代码。
<bean id="abstractLookupDemoBean" class="study.hwj.chapter03.lookup.AbstractLookupDemoBean">
<lookup-method name="getMySinger" bean="singer"/>
</bean>
StopWatch
类是Spring提供的一个实用类。用来执行简单的性能测试。
@Scope注解:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Singer {
...
}
@Lookup注解:
@Lookup("singer")
public void sing() {}
查找方法注入的注意事项
当想要使用两个具有不同生命周期的bean时,可以使用查找方法注入。当bean共享相同的生命周期时,应该避免使用查找方法注入,尤其当这些bean都是单例时。
基于XML配置的查找方法没必要一定抽象化,抽象化可以防止忘记配置查找方法,也可以使用空白的实现。基于注解的配置强制方法的空白实现;否则,不会创建bean。
方法替换
通过使用方法替换,可以任意替换任何bean的任何方法的实现,而无需更改所修改bean的源代码。
Spring在内部使用CGLIB
将想要替换的方法的调用重定向为另一个实现了MethodReplacer
接口的bean。
在有重载方法的情况下,可以使用<arg-type>
标记指定要匹配的方法签名。<arg-type>
标记支持模式匹配,因此String
可以与java.lang.String
以及java.lang.StringBuffer
匹配。
<bean id="methodReplacer" class="study.hwj.chapter03.methodreplace.FormatMessageReplacer"/>
<bean id="replacementTarget" class="study.hwj.chapter03.methodreplace.ReplacementTarget">
<replaced-method name="formatMessage" replacer="methodReplacer">
<arg-type>String</arg-type>
</replaced-method>
</bean>
建议为每个方法或一组重载的方法使用一个MethodReplacer
。应该避免针对多个不相关的方法使用单个MethodReplacer
3.5.5 了解bean命名
每个bean在其所在的ApplicationContext
中至少包含一个唯一的名称。如果为<bean>
标记赋予一个id
属性,那么该属性的值将用作名称。如果没有指定id
属性,Spring会查找name
属性,如果定义了name属性,那么将使用name属性中定义的第一个名称(name属性可以定义多个名称)。如果既没有id,也没有name,那么Spring使用该bean的类名作为名称。如果声明了多个没有ID或名称的相同类型的bean,那么Spring将在ApplicationContext初始化期间,在注入时抛出异常。
作为一种惯例,应该使用id
属性作为bean的名称,然后使用名称别名将bean与其他名称相关联。
bean名称别名
Spring允许一个bean拥有多个名称。可以通过在<bean>
的name属性中指定以空格、逗号或分号分隔的名称列表来实现多个名称。name属性可以替代id属性,或者组合使用这两个属性。除了使用name属性,还可以使用<alias>
标记定义别名。
可以通过调用ApplicationContext.getAliases(String)
并传入任何bean的名称或id来获取bean的别名列表。别名列表以字符串数组返回(不包含传入的名称)。
Spring IOC对name和id属性值的处理方式不同。
<!-- 这个bean的id为【jon johnny,jonathan;jim】 -->
<bean id="jon johnny,jonathan;jim" class="java.lang.String"/>
使用注解配置的bean命名
当使用注解声明bean定义时,bean命名与XML稍有不同。
@Component
注解没有任何参数,在这种情况下遵循的惯例是将bean命名为类本身,但是将首字母小写,其他构造型注解也遵循该惯例。使用@Component("singer")
相当于使用@Component注解Singer类。如果项以不同的方式命名bean,@Component注解必须接受bean名称作为参数。由于@Component的参数成为bean的唯一标识符,以这种方式声明bean时不可以使用别名。
使用@Bean
注解方法声明时,没有为@Bean提供任何参数时,方法名就是bean的id。要声明别名,可以使用name属性,如果该属性的值是要给字符串数组,第一个数组值成为id,其他值成为别名。
在Spring4.2中引入了@AliasFor
注解。该注解用于为注解属性声明别名。还可以声明元注解属性的别名。
使用@AliasFor为注解的属性创建别名是有一定局限性的。@AliasFor不能用于任何构造型注解(@Component及其特例),还有@Qualifier
注解,因为出现的晚。
3.5.6 了解bean实例化模式
选择实例化模式
关于<bean>
的scope
属性:
单例(singleton)应该在下列情况下使用:
- 没有状态的共享对象;
- 具有只读状态的共享对象;
- 具有共享状态的共享对象;
- 具有可写状态的高通量(hign-throughput)对象;
非单例(prototype):
- 具有可写状态的对象;
- 具有私有状态的对象;
实现bean作用域
Spring从版本4开始支持以下bean作用域:
- 单例作用域:默认;
- 原型作用域;
- 请求作用域:用于Web应用程序,针对每个HTTP请求;
- 会话作用域:用于Web应用程序,针对每个HTTP会话;
- 全局会话作用域:用于基于
Portlet
的Web应用程序。带有全局会话作用域的bean可以在同一个Spring MVC
驱动的Web应用程序的所有Portlet之间共享; - 线程作用域:当一个新线程请求bean实例时,Spring将创建一个新的bean实例,而对于同一个线程,返回相同的实例。线程作用域默认情况下未注册;
- 自定义作用域:通过实现
org.springframework.beans.factory.config.Scope
接口创建自定义作用域,并在Spring配置中注册自定义作用域(对于XML,使用org.springframework.beans.factory.config.CustomScopeConfigurer
);
3.6 解析依赖项
可以使用<bean>
的depends-on
属性为Spring提供有关bean的依赖项的附加信息。
ApplicationContextAware
接口是一个特定于Spring的接口,它为 ApplicationContextAware 对象强制实现一个setter。Spring IOC容器会自动进行检测,并且注入bean段创建时所在的 ApplicationContextAware 。这一切都是在调用bean的构造函数之后完成的,因此在构造函数中使用 ApplicationContextAware 将导致 NullPointerException
异常。
当开发应用程序时,应该避免应用程序使用此功能;相反,应该通过setter和构造函数注入协定来定义依赖项。但是如果将Spring与遗留代码集成在一起,可能会发现代码中定义的依赖项要求向Spring框架提供额外的信息。
3.7 自动装配bean
Spring支持五种自动装配模式:
- byName模式:Spring尝试将每个属性连接到同名的bean。如果目标bean中具有名为foo的属性并且在ApplicationContext中定义了foo bean,那么foo bean将被分配给目标bean的foo属性;
- byType模式:Spring通过在ApplicationContext中自动使用相同类型的bean来尝试连接目标bean模式的每个属性;
- 构造函数模式:与byType模式功能上相同。Spring试图匹配构造函数中最大数量的参数;
- 默认模式:Spring将自动在构造函数模式和byType模式之间进行选择。如果bean有一个默认的(无参)构造函数,那么就是用byType模式;否则,使用构造函数模式;
- 无:默认。
使用注解定义bean时,默认的自动装配模式是byType。
当有多个相同类型的bean时,可以使用primary
和qualifier
指定。
在大多数情况下,不应该使用自动装配。
3.8 设置bean继承
如果使用共享的属性值声明大量具有相同值的bean,则应该避免使用复制粘贴来共享值,而是应该在配置中设置集成层次结构。
bean继承不必与Java继承层次结构相匹配。与其说bean继承是一项继承功能,还不如说更像是模板功能。如果要更改子bean的类型,该类型必须扩展父bean的类型。
20191103 《Spring5高级编程》笔记-第3章的更多相关文章
- C#高级编程笔记之第二章:核心C#
变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...
- C#高级编程笔记之第一章:.NET体系结构
1.1 C#与.NET的关系 C#不能孤立地使用,必须与.NET Framework一起使用一起考虑. (1)C#的体系结构和方法论反映了.NET基础方法论. (2)多数情况下,C#的特定语言功能取决 ...
- 20191105 《Spring5高级编程》笔记-【目录】
背景 开始时间:2019/09/18 21:30 Spring5高级编程 版次:2019-01-01(第5版) Spring5最新版本:5.1.9 CURRENT GA 官方文档 Spring Fra ...
- 读《C#高级编程》第1章问题
读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...
- Android高级编程笔记(四)深入探讨Activity(转)
在应用程序中至少包含一个用来处理应用程序的主UI功能的主界面屏幕.这个主界面一般由多个Fragment组成,并由一组次要Activity支持.要在屏幕之间切换,就必须要启动一个新的Activity.一 ...
- C#高级编程9 第18章 部署
C#高级编程9 第18章 部署 使用 XCopy 进行部署 本主题演示如何通过将应用程序文件从一台计算机复制到另一台计算机来部署应用程序. 1.将项目中生成的程序集复制到目标计算机,生成的程序集位于项 ...
- C#高级编程9 第17章 使用VS2013-C#特性
C#高级编程9 第17章 使用VS2013 编辑定位到 如果默认勾选了这项,请去掉勾选,因为勾选之后解决方案的目录会根据当前文件选中. 可以设置项目并行生成数 版本控制软件设置 所有文本编辑器行号显示 ...
- C#高级编程9 第16章 错误和异常
C#高级编程9 第16章 错误和异常 了解这章可以学会如何处理系统异常以及错误信息. System.Exception类是.NET运行库抛出的异常,可以继承它定义自己的异常类. try块代码包含的代码 ...
- C#高级编程笔记之第三章:对象和类型
类和结构的区别 类成员 匿名类型 结构 弱引用 部分类 Object类,其他类都从该类派生而来 扩展方法 3.2 类和结构 类与结构的区别是它们在内存中的存储方式.访问方式(类似存储在堆上的引用类型, ...
- UNIX环境高级编程笔记之文件I/O
一.总结 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是“哇”这种很吃惊的表情.其实大概三年前,那会大三,我就买了这本书 ...
随机推荐
- Linux 开机自动启动脚本
1)编写要执行脚本的sh文件mysetup.sh #!/bin/sh ### BEGIN INIT INFO # Provides: land.sh # Required-start: $local_ ...
- 【LeetCode】排序 sort(共20题)
链接:https://leetcode.com/tag/sort/ [56]Merge Intervals (2019年1月26日,谷歌tag复习) 合并区间 Input: [[1,3],[2,6], ...
- Mybatis SQL 使用JAVA 静态资源
常量:${@com.htsc.backtest.component.Global@PAGE_SIZE} 静态方法:${@com.htsc.backtest.component.Global@doMet ...
- Centos 学习之路:基础(1)
冯·诺伊曼计算机模型: 采用二进制数表示程序和数据: 能存储程序和数据,并能自动控制程序的执行: 具备运算器.控制器.存储器.输入设备和输出设备5个基本部分. CPU:是控制器及运算器 CPU的架构类 ...
- 在父组件中,传值给子组件-vue
1.通过 props <x-test :name="username"></x-test>1)props为字符串数组 props: ['name']2)pr ...
- C++ GUI Qt4学习笔记04
本章将实现应用程序的功能,通过编写底层函数来完成之前的Spreadsheet程序,关于如何载入和保存文件,如何在内存中存储数据,如何实现剪贴板的操作以及如何向QTableWidget中添加对电子指标软 ...
- Mixly-指令
串口: 向串口监视器输出数据 十进制 Serial.println(ir_item,HEX); 向串口监视器输出数据---十六进制 通信: 接收11脚的红外信号,把接收到的数据 ...
- linux C++ 通讯架构(一)nginx安装、目录、进程模型
nginx是C语言开发的,号称并发处理百万级别的TCP连接,稳定,热部署(运行时升级),高度模块化设计,可以用C++开发. 一.安装和目录 1.1 前提 epoll,linux内核版本为2.6或以上 ...
- Linux相关TCP参数优化: proc/sys/net/ipv4/ 提高web质量
tcp_wmem(3个INTEGER变量): min, default, max min:为TCP socket预留用于发送缓冲的内存最小值.每个tcp socket都可以在建议以后都可以使用它.默认 ...
- java面向对象复习之一
目的: 复习如何实现代码的逻辑思路: 复习类的封装: 复习类和对象的创建使用和封装: 练习: 实现功能:人到超市买东西 抽出三个类: 人 超市 东西: 功能点: 买: 它们之间的联系:东西包含于超市 ...