Spring 核心技术(5)
接上篇:Spring 核心技术(4)
version 5.1.8.RELEASE
1.4.5 自动装配协作者
Spring 容器可以自动连接协作 bean 之间的关系。你可以让 Spring 通过检查 ApplicationContext
中的内容自动为 bean 解析协作者(其他bean)。自动装配具有以下优点:
- 自动装配可以显着减少指定属性或构造函数参数的需要。(在本章其他地方讨论的其他机制,如bean模板 ,在这方面也很有价值。)
- 自动装配可以随着对象的演变更新配置。例如,如果需要向类添加依赖项,自动装配可以自动满足该依赖项,而无需修改配置。因此,自动装配在开发期间尤其有用,在代码库变得更稳定后也不会取消切换到显式装配的选项。
使用基于 XML 的配置元数据(请参阅依赖注入)时,可以使用 <bean/>
元素的 autowire
属性为 bean 定义指定装配模式。自动装配功能有四种模式。你指定每个 bean 的自动装配,因此可以选择要自动装配哪些。下表描述了四种自动装配模式:
模式 | 说明 |
---|---|
no | (默认)无自动装配。Bean 引用必须由 ref 元素定义。在进行较大部署更新时不建议更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。 |
byName | 按属性名称自动装配。Spring 查找与需要自动装配的属性同名的 bean。例如,如果 bean 定义按名称设置为根据名称进行注入并且它包含一个 master 属性(即,它有一个 setMaster(..) 方法),则 Spring 会查找名为 master 的 bean 定义并使用它来设置属性。 |
byType | 如果容器中只存在一个该属性类型的 bean,则允许属性自动装配。如果存在多个,则抛出致命异常,这表示可能不能对该 bean 使用 byType 自动装配。如果没有匹配的 bean,则不会发生任何事情(该属性未设置)。 |
constructor | 类似于 `byType 但适用于构造函数参数。如果容器中没有构造函数参数类型的一个 bean,则会引发致命错误。 |
使用 byType
或 constructor
自动装配模式,可以装配数组和类型化的集合。在这种情况下,容器中所有与预期类型匹配的自动装配候选者都会被提供以满足依赖性。如果预期的键类型是 String
,则可以自动装配强类型 Map
实例。自动装配 Map
实例的值由与预期类型匹配的所有 bean 实例组成, Map
实例的键包含相应的 bean 名称。
自动装配的局限和缺点
当在整个项目中使用一致的自动装配时,自动装配效果最佳。如果一般不使用自动装配,那么开发人员使用它来装配一个或两个 bean 定义可能会让人感到困惑。
考虑自动装配的局限和缺点:
property
和constructor-arg
中设置的显式依赖项始终覆盖自动装配。不能自动装配简单属性,例如基本类型,Strings
,和Classes
(以及此类简单属性的数组)。这是设计中的限制。- 自动装配不如显式装配精确。虽然如前面的表中所述,但 Spring 会谨慎地避免在带有歧义时进行猜测,这可能出现预料之外的结果。Spring 管理的对象之间的关系不再明确记录。
- 从 Spring 容器中获取文档的工具可能无法获取自动装配信息。
- 容器中的多个 bean 定义可以匹配 setter 方法或构造函数参数指定的类型以进行自动装配。对于数组,集合或
Map
实例,这不一定是个问题。但是,对于期望单个值的依赖关系,这种模糊性不是可以随意解决的。如果没有可用的唯一 bean 定义,则抛出异常。
在后一种情况下,您有几种选择:
- 放弃自动装配,使用持显式装配。
- 如下一节所述,通过将其
autowire-candidate
属性设置为false
,可以避免对bean定义进行自动装配。 - 通过将其
<bean/>
元素的primary
属性设置为true
,将单个 bean 定义指定为主要候选者。 - 基于注解的配置项实现更细粒度的控件,如基于注解的容器配置中所述。
从自动装配中排除 Bean
基于每个 bean,你可以从自动装配中排除 bean。在 Spring 的 XML 格式中,将 <bean/>
元素的 autowire-candidate
属性设置为 false
。容器使指定的 bean 定义对自动装配不可用(包括注解配置,例如 @Autowired
)。
autowire-candidate
属性旨在仅影响基于类型的自动装配。它不会影响根据名称的显式引用,即使指定的 bean 未标记为自动注解的候选人,也会解析它。因此,如果名称匹配,则按名称自动装配会注入 bean。
你还可以根据 bean 名称的表达式匹配来限制自动装配候选项。顶级的 <beans/>
元素在 default-autowire-candidates
属性中接受一个或多个表达式 。例如,要将自动装配候选状态限制为名称以 Repository
结尾的任何 bean,那么就提供值 *Repository
。需要提供多个表达式时,请在列表中定义并使用逗号分隔。bean 定义的 autowire-candidate
属性优先使用显示定义的值 true
或 false
。对于此类 bean,表达式匹配规则并不适用。
这些技术对于永远不希望通过自动装配注入其他 bean 的 bean 非常有用。这并不意味着排除的 bean 本身不能使用自动装配进行配置。相反,bean 本身不是其他 bean 自动装配的候选者。
1.4.6 方法注入
在大多数应用程序场景中,容器中的大多数 bean 都是单例。当单例 bean 需要与另一个单例 bean 协作或非单例 bean 需要与另一个非单例 bean 协作时,通常通过将一个 bean 定义为另一个 bean 的属性来处理依赖关系。当 bean 的生命周期不同时会出现问题。假设单例 bean A 需要使用非单例(原型)bean B,可能是在 A 上的每个方法调用上。容器只创建一次单例 bean A,因此只有一次机会来设置属性。容器不能在 bean A 每次需要时提供一个新的 bean B 的实例。
解决方案是放弃一部分控制反转。你可以通过实现 ApplicationContextAware
接口使 bean A 对容器可见,同时通过让容器调用 getBean("B")
在每次需要时请求 bean B 的实例。以下示例展示了此方法:
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
前面提到的方法并不理想,因为业务代码知道并耦合到 Spring Framework。方法注入是Spring IoC容器的一个高级功能,可以更加轻松地处理这种场景。
你可以在此博客中阅读有关方法注入成因的更多信息。
查找方法注入
查找方法注入是容器重写容器管理的 bean 中的方法并为容器中另外一个命名的 bean 返回查找结果的能力。查找通常涉及原型 bean,如上一节中描述的场景。Spring Framework 通过使用 CGLIB 库中的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。
- 要使这个动态子类工作,Spring bean 容器子类不能设置为
final
,要重写的方法也不能设置为final
。- 对具有
abstract
方法的类进行单元测试需要你自己对类进行子类化并提供该abstract
方法的 stub 实现。- 组件扫描也需要具体的方法,这需要具体的类来获取。
- 另一个关键限制是查找方法不适用于工厂方法,特别是
@Bean
配置类中的方法,因为在这种情况下,容器不负责创建实例,因此无法凭空创建出运行时生成的子类。
对于前面代码片段中的 CommandManager
类,Spring 容器动态地覆盖 createCommand()
方法的实现。该 CommandManager
类没有任何 Spring 的依赖,因为重新写的示例如下所示:
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
在包含要注入的方法的客户端类中(本例中的 CommandManager
),要注入的方法需要以下形式的签名:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
如果方法是 abstract
,则动态生成的子类实现该方法。否则,动态生成的子类将覆盖原始类中定义的具体方法。请考虑以下示例:
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
只要需要 myCommand
bean 的新实例,定义为 commandManager
的 bean 就会调用自己的 createCommand()
方法。如果实际需要,必须注意将 myCommand
bean 部署为最初形态。如果它是单例,则每次返回相同的 myCommand
bean 实例。
或者,在基于注释的组件模型中,可以通过 @Lookup
注解声明查找方法,如以下示例所示:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
或者,更常用的,可以通过针对查找方法已声明的返回类型解析的目标 Bean:
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract MyCommand createCommand();
}
请注意,通常应该使用具体的 stub 实现来声明这种带注释的查找方法,以使它们与 Spring 的组件扫描规则兼容,默认情况下抽象类会被忽略。此限制不适用于显式注册或显式导入的 bean 类。
访问不同作用域的目标 bean 的另一种方法是
ObjectFactory
/Provider
注入点。请参阅使用 Scoped Beans 作为依赖关系。你可能还会发现
ServiceLocatorFactoryBean
(在org.springframework.beans.factory.config
包中)有用。
任意方法替换
与查找方法注入相比,一种不太有用的方法注入形式是能够使用另一个方法实现替换托管 bean 中的任意方法。你可以安全地跳过本节的其余部分,直到确实需要此功能。
使用基于 XML 的配置元数据时,可以使用 replaced-method
元素将已存在的方法实现替换为已部署的 bean。考虑以下类,它有一个我们想要覆盖的 computeValue
方法:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
实现 org.springframework.beans.factory.support.MethodReplacer
接口的类提供新的方法定义,如以下示例所示:
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
部署原始类并指定方法覆盖的 bean 定义类似于以下示例:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
你可以使用一个或多个 <replaced-method/>
元素的 <arg-type/>
元素来表明被覆盖的方法的方法签名。仅当方法重载且类中存在多个变体时,才需要参数的签名。为方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下所有均匹配 java.lang.String
:
java.lang.String
String
Str
因为参数的数量通常足以区分每个可能的选择,所以通过让您只键入与参数类型匹配的最短字符串,此快捷方式可以节省大量的输入。
Spring 核心技术(5)的更多相关文章
- Spring核心技术(八)——Spring自动装载的注解
本文针对自动装载的一些注解进行描述. 基于注解的容器配置 @Required注解 @Required注解需要应用到Bean的属性的setter方法上面,如下面的例子: public class Sim ...
- Spring核心技术(六)——Spring中Bean的生命周期
前文已经描述了Bean的作用域,本文将描述Bean的一些生命周期作用,配置还有Bean的继承. 定制Bean 生命周期回调 开发者通过实现Spring的InitializeingBean和Dispos ...
- Spring核心技术(五)——Spring中Bean的作用域
前文概述了Spring的容器,Bean,以及依赖的一些信息,本文将描述一下Bean的作用域 Bean的作用域 当开发者定义Bean的时候,同时也会定义了该如何创建Bean实例.这些具体创建的过程是很重 ...
- Spring核心技术(四)——Spring的依赖及其注入(续二)
前面两篇文章描述了IoC容器中依赖的概念,包括依赖注入以及注入细节配置.本文将继续描述玩全部的依赖信息. 使用 depends-on 如果一个Bean是另一个Bean的依赖的话,通常来说这个Bean也 ...
- Spring 核心技术(3)
接上篇:Spring 核心技术(2) version 5.1.8.RELEASE 1.4 依赖 典型的企业应用程序不会只包含单个对象(或 Spring 术语中的 bean).即使是最简单的应用程序也是 ...
- Spring 核心技术(2)
接上篇:Spring 核心技术(1) version 5.1.8.RELEASE 1.3 Bean概述 Spring IoC 容器管理一个或多个bean,他们都是根据提供的配置元数据(例如 XML 中 ...
- Spring 核心技术(4)
接上篇:Spring 核心技术(3) version 5.1.8.RELEASE 1.4.2 依赖关系及配置详情 如上一节所述,你可以将 bean 属性和构造函数参数定义为对其他托管 bean(协作者 ...
- Spring 核心技术(6)
接上篇:Spring 核心技术(5) version 5.1.8.RELEASE 1.5 Bean 作用域 创建 bean 定义时,你创建了一种用于创建 bean 定义中定义的类实例的方法.bean定 ...
- Spring 核心技术(7)
接上篇:Spring 核心技术(6) version 5.1.8.RELEASE 1.6 定制 Bean 的特性 Spring Framework 提供了许多可用于自定义 bean 特性的接口.本节将 ...
随机推荐
- .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...
- 图片加载时间缓慢问题API
一.背景 最近段时间,开发写值工具项目中,出现图片加载问题API,响应时间缓慢:为了优化图片加载问题,我进行图片压缩方法,然后API的图片加载还是慢,最终在自己无意中乱写找到了根本的原因. ...
- 记一次基于Cloudflare服务的爬虫
前言 前几天有个朋友托我帮忙爬一个网站,第一次打开那个网站时,我去,它竟然就要验证码,不过当时是想加几个header应该就能解决,不过事实证明我还是错了.接下来将记录下爬虫中遇到的一些坑以及解决办法. ...
- ElasticStack学习(三):ElasticSearch基本概念
1.文档 1)ElasticSearch是面向文档的,文档是所有可搜索数据的最小单位.例如: a)日志文件中的日志项: b)一张唱片的详细信息: c)一篇文章中的具体内容: 2)在ElasticSea ...
- Go - Map 集合
目录 概述 声明 Map 生成 JSON 编辑和删除 推荐阅读 概述 Map 集合是无序的 key-value 数据结构. Map 集合中的 key / value 可以是任意类型,但所有的 key ...
- 按行读取String类型
BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content.getByt ...
- C语言学习书籍推荐《C语言入门经典(第4版)》
霍顿 (Ivor Horton) (作者), 杨浩 (译者) <C语言入门经典(第4版)>的目标是使你在C语言程序设计方面由一位初学者成为一位称职的程序员.读者基本不需要具备任何编程知识, ...
- Autocad2017破解版下载|Autodesk Autocad 2017中文破解版下载 64位(附注册机/序列号)
Autocad2017是Autodesk公司开发的自动计算机辅助设计软件,可用于二维绘图.详细绘制.设计文档和基本三维设计,它具有良好的用户界面,允许用户通过交互菜单或命令行方式来进行各种操作,包括图 ...
- servlet的几个函数
request.getContextPath 上下文,例如 /bignews1 (自带 “ / ”) request.getScheme() 协议,例如HTTP request.getServer ...
- Excel中vlookup函数使用
https://baijiahao.baidu.com/s?id=1594684818733205984&wfr=spider&for=pc