接上篇: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,则会引发致命错误。

使用 byTypeconstructor 自动装配模式,可以装配数组和类型化的集合。在这种情况下,容器中所有与预期类型匹配的自动装配候选者都会被提供以满足依赖性。如果预期的键类型是 String,则可以自动装配强类型 Map 实例。自动装配 Map 实例的值由与预期类型匹配的所有 bean 实例组成, Map 实例的键包含相应的 bean 名称。

自动装配的局限和缺点

当在整个项目中使用一致的自动装配时,自动装配效果最佳。如果一般不使用自动装配,那么开发人员使用它来装配一个或两个 bean 定义可能会让人感到困惑。

考虑自动装配的局限和缺点:

  • propertyconstructor-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 属性优先使用显示定义的值 truefalse。对于此类 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)的更多相关文章

  1. Spring核心技术(八)——Spring自动装载的注解

    本文针对自动装载的一些注解进行描述. 基于注解的容器配置 @Required注解 @Required注解需要应用到Bean的属性的setter方法上面,如下面的例子: public class Sim ...

  2. Spring核心技术(六)——Spring中Bean的生命周期

    前文已经描述了Bean的作用域,本文将描述Bean的一些生命周期作用,配置还有Bean的继承. 定制Bean 生命周期回调 开发者通过实现Spring的InitializeingBean和Dispos ...

  3. Spring核心技术(五)——Spring中Bean的作用域

    前文概述了Spring的容器,Bean,以及依赖的一些信息,本文将描述一下Bean的作用域 Bean的作用域 当开发者定义Bean的时候,同时也会定义了该如何创建Bean实例.这些具体创建的过程是很重 ...

  4. Spring核心技术(四)——Spring的依赖及其注入(续二)

    前面两篇文章描述了IoC容器中依赖的概念,包括依赖注入以及注入细节配置.本文将继续描述玩全部的依赖信息. 使用 depends-on 如果一个Bean是另一个Bean的依赖的话,通常来说这个Bean也 ...

  5. Spring 核心技术(3)

    接上篇:Spring 核心技术(2) version 5.1.8.RELEASE 1.4 依赖 典型的企业应用程序不会只包含单个对象(或 Spring 术语中的 bean).即使是最简单的应用程序也是 ...

  6. Spring 核心技术(2)

    接上篇:Spring 核心技术(1) version 5.1.8.RELEASE 1.3 Bean概述 Spring IoC 容器管理一个或多个bean,他们都是根据提供的配置元数据(例如 XML 中 ...

  7. Spring 核心技术(4)

    接上篇:Spring 核心技术(3) version 5.1.8.RELEASE 1.4.2 依赖关系及配置详情 如上一节所述,你可以将 bean 属性和构造函数参数定义为对其他托管 bean(协作者 ...

  8. Spring 核心技术(6)

    接上篇:Spring 核心技术(5) version 5.1.8.RELEASE 1.5 Bean 作用域 创建 bean 定义时,你创建了一种用于创建 bean 定义中定义的类实例的方法.bean定 ...

  9. Spring 核心技术(7)

    接上篇:Spring 核心技术(6) version 5.1.8.RELEASE 1.6 定制 Bean 的特性 Spring Framework 提供了许多可用于自定义 bean 特性的接口.本节将 ...

随机推荐

  1. H5离线缓存基础系列

    1.什么是离线缓存 离线缓存:离线缓存可以将站点的一些文件缓存到本地,它是浏览器自己的一种机制,将需要的文件缓存下来,以便后期即使没有连接网络,被缓存的页面也可以展示. 2.离线缓存的优势 在没有网络 ...

  2. zabbix自定义监控项数据类型错误

    问题描述 监控cpu使用率,脚本获取的值是浮点型  zabbix创建监控项时没有选数据类型,导致监控数据有问题. 查看 zabbix-server 日志: ::203016.768 error rea ...

  3. nginx实现最简单的直播

    系统环境 [root@yunwei-test live]# cat /etc/redhat-release CentOS Linux release (Core) [root@yunwei-test ...

  4. Cisco packet tracer6.0下的网络工程实训

    期末的专业实训,在cisco6.0下配置一个简单的局域网.主要用到了下面几个技术:dhcp中继.vlan的划分.链路聚合.静态nat.ospf协议.访问控制列表.先看一下总的拓扑图,在分步实现功能. ...

  5. Spring Boot 打包成的可执行 jar ,为什么不能被其他项目依赖?

    前两天被人问到这样一个问题: "松哥,为什么我的 Spring Boot 项目打包成的 jar ,被其他项目依赖之后,总是报找不到类的错误?" 大伙有这样的疑问,就是因为还没搞清楚 ...

  6. 查询IP地址的免费API

    1.百度 1.http://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=192.168.0.0&co=&resource_ ...

  7. 关于Nginx499、502和504的分析

    我相信有些人在面试运维类岗位的时候会碰到对方问关于这方面的问题,我这里通过几个实验来复现这个情况,并做出相关分析,我希望大家看完后针对这种问题能有一个清晰思路. 服务器 IP Nginx 192.16 ...

  8. C# 中的委托和事件本质讲解

    C# 中的委托和事件 文中代码在VS2005下通过,由于VS2003(.Net Framework 1.1)不支持隐式的委托变量,所以如果在一个接受委托类型的位置直接赋予方法名,在VS2003下会报错 ...

  9. 网络下载器 迅雷大众版 v7.9.42.5050 精简绿色版

    下载地址:点我 基本介绍 迅雷大众版是一款采用了先进的超线程技术基于网格原理,能够将存在于第三方服务器和计算机上的数据文件进行有效整合,通过这种先进的超线程技术,用户能够以更快的速度从这些第三方节点平 ...

  10. ~~核心编程(二):面向对象——类&属性~~

    进击のpython 类&属性 虽然我们上一part写了一个面向对象的程序:人狗大战 但是如果在面向对象来看 你这些的就不够规范 你既然选择用面向对象的思想来写 那你就要符合人家的定义规范和操作 ...