依赖
自动装配协作者
Spring容器可以自动装配在协作bean之间的关系。
Spring可以根据ApplicationContext的内容自动分解出bean的协作者。
自动装配的优点:
自动装配可以显著减少指定属性或构造器参数的需要。
自动装配会在对象发生改变时自动更新配置,这样,就不需要反复改动已经写好的配置文件。
当使用基于xml的配置元数据时,通过<bean>元素的autowire属性指定自动装配模式。在使用时,为每一个bean指定一种自动装配模式。
自动装配有四种模式
no:(默认)不进行自动装配,bean引用必须通过ref元素进行定义。不建议在大型系统中使用自动装配,因为显式指定协作者可以使系统更可控清晰。
byName:通过属性名称进行自动装配。即bean的名称与属性名称一一对应。
byType:如果正好只有一个与属性同类型的bean存在于容器中,允许属性被自动装配。如果同类型bean存在多个会抛出严重异常,如果没有一个bean被匹配,那么这个属性不会被设置,也不会抛出异常。
constructor:与byType类似,但是应用于构造器参数。如果没有一个bean与构造器参数的类型匹配,那么一个重大错误会发生。
可以通过byType与constructor自动转配模式装配数组或集合。所有匹配期待类型的bean都会作为依赖注入。
可以将依赖检测与自动装配行为结合。
自动装配的缺点与局限
如果自动装配只用于部分bean,会使开发者陷入困惑。
自动装配的缺点如下:
property与constructor-arg的显式依赖设置将会重载自动装配。并且原始类型不能被自动装配。
自动装配没有显式装配明确。
装配信息不能用于生成文档。
多个bean定义匹配基于byType、constructor的自动装配指定的类型。对与数组、集合、Maps,这并不是问题,但是如果期待的依赖为单一值,那么将会抛出异常。
当最后一个场景发生时,用于应对的选项有:
放弃自动装配,选择显式装配。
通过将bean定义的autowire-candidate属性设置为false避免自动装配。
通过设置当前bean的primary属性,将一个单独bean定义指定为主要候选者。
通过基于注解的配置进行更细粒度的控制。
将一个bean排除出自动装配
可以通过将autowire-candidate属性设置为false,使得这个bean不能作为依赖被用于自动装配。
autowire-candidate属性只会影响基于类型的自动装配,并不会影响基于名称的显式引用。
所以,如果名称匹配的话,基于名称的自动装配会成功。
可以通过bean名称的匹配模式限制byName的自动装配。
<beans>的default-autowire-candidate属性可以接受多个模式。
eg:aaa*用以匹配名称以aaa为开头的bean。
可以通过逗号间隔提供多个匹配模式。
如果bean定义的autowire-candidate属性被显式设置为true或false,此bean会被优先匹配。但是,模式匹配规则不会被应用。
一个排除出自动装配的bean自身能够使用自动装配,但是不能被自动装配进其他beans中。
方法注入
bean有不同的生命周期,当不同生命周期的bean彼此依赖,并进行依赖注入时会产生问题。
eg:单例bean A被原型bean B注入,由于A只会被初始化一次,所以B只会被注入一次,B不会在每次被需要时都注入A一次。
一个解决方案是放弃部分控制反转,通过ApplicationContextAware接口令A获得ApplicationContext的引用,然后在每次需要B的bean时,使用getBean方法获取B的bean。
不过,由于与spring API的耦合,所以并不推荐。
查阅方法注入
查阅方法注入是容器重载容器管理的bean上方法的能力,用于返回使用其他bean名称查找的结果。
Spring框架通过CGLIB库的字节码生成技术动态生成重载方法的子类实现方法注入。
被方法注入的bean不能为final类,而且被重载的方法也不能为final。
单元测试被方法注入的抽象方法时,需要手动提供一个抽象方法的实现。
对于组件扫描来说,具体的方法是必要的。
查阅方法的更关键的局限在于不能使用工厂方法,特别是在配置类中使用的@Bean方法。
package fiona.apple;
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();
}
protected abstract Command createCommand();
}
<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>
note:使用方法注入,使得createCommand()方法返回Command bean。
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
note:使用基于注解的方式进行方法注入。
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract MyCommand createCommand();
}
note:基于注解的方法进行方法注入,并且使用被重载的方法的返回值类型进行bean匹配。
ps:通常需要声明一个具有具体实现的被注解的查阅方法的stub,为了与Spring的组件扫描兼容,因为Spring组建扫描会忽略抽象类。
另一种访问不同范围的bean的方法是,使用ObjectFactory/Provide 注入点。
任意方法替换
一种比查阅方法注入更少使用的方法注入是替换被管理bean的任意方法的能力。
使用基于xml的配置元数据,可以通过replaced-method元素来替换一个已经存在的方法实现。
实现了org.springframework.beans.factory.support.MethodReplacer接口的类提供了新的方法实现。
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
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 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"/>
ps:使用一个或多个包含<arg-type>元素的<replaced-method>元素指定被重载方法的方法签名。
ps:通常,只有当方法被重载并且存在多个变体于当前类中时,方法签名中的参数是必要的。
ps:方法签名中参数的类型string可以是完整类型名称的部分,eg:String映射java.lang.String。
ps:由于方法参数的数量往往就足以区分方法,所以可以使用类型的简写省略打入的字符数量。