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 特性的接口.本节将 ...
随机推荐
- 精通并发与 Netty (一)如何使用
精通并发与 Netty Netty 是一个异步的,事件驱动的网络通信框架,用于高性能的基于协议的客户端和服务端的开发. 异步指的是会立即返回,并不知道到底发送过去没有,成功没有,一般都会使用监听器来监 ...
- HDFS Java API 的基本使用
一. 简介 二.API的使用 2.1 FileSystem 2.2 创建目录 2.3 创建指定权限的目录 2.4 创建文件,并写入内容 ...
- php实现redis锁机制
<?php class Redis_lock { public static function getRedis() { $redis = new redis(); $redis->con ...
- CI控制器
当控制器要继承自定义的控制器的时候,有特定的定义: application/core/MY_Controller <?php class MY_Controller extends CI_Con ...
- Linux 安装Nginx与使用
最近继续整理Linux相关文档.这次整理的是Nginx,这里将自己整理的详细文档做个笔记. 1. 安装环境依赖包 1. gcc 语言编译器套件. 2. pcre 兼容正则表达式的库 rewrite ...
- 使用SQL行转列函数pivot遇到的问题
背景:对投票的结果按照单位进行汇总统计,数据库中表记录的各个账号对各个选项的投票记录.马上想到一个解决方案,先根据单位和选项进行Group By,然后再行转列得出单位对各个选项的投票情况. with ...
- linux c库函数大全
Linux C函数库参考手册 [转自ChinaUnix]第1章字符测试函数isalnum(测试字符是否为英文字母或数字)isalpha(测试字符是否为英文字母)isascii(测试字符是否为ASCI ...
- kuangbin专题 专题一 简单搜索 Find a way HDU - 2612
题目链接:https://vjudge.net/problem/HDU-2612 题意:‘@’表示KTV,‘#’表示无法走的地方,‘Y’,'M’表示两个人,他们要在KTV见面,问他们都到达KTV要花费 ...
- SQL Server 表结构操作
一.创建表 --直接定义主外键 create table wallet( ID ) primary key, ,) not null, Name ) default '余额', Member_ID ) ...
- vmware的卸载
vmware出了点问题,在控制面板里或者是360都没法删除干净.在网上搜了点资料,找到一些删除的方法,参考链接如下: http://zhidao.baidu.com/question/30902992 ...