方法注入
在大多数应用场景中,容器中的大部分 bean 都是 单例的。当单例 bean 需要与另一个单例 bean 协作或非单例 bean 需要与另一个非单例 bean 协作时,您通常通过将一个 bean 定义为另一个 bean 的属性来处理依赖关系。当 bean 生命周期不同时就会出现问题。假设单例 bean A 需要使用非单例(原型)bean B,可能在 A 上的每次方法调用上。容器只创建单例 bean A 一次,因此只有一次设置属性的机会。容器无法在每次需要时为 bean A 提供 bean B 的新实例。
一个解决方案是放弃一些控制反转。您可以通过实现接口来使 bean A 了解容器ApplicationContextAware
,并通过在getBean("B")
每次 bean A 需要时调用容器来请求(通常是新的)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 库的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。
对于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
在本例中为 the)的客户端类中,要注入的方法需要以下形式的签名:
<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>
标识为的 bean在需要bean的新实例时commandManager
调用它自己的createCommand()
方法myCommand
。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) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
请注意,您通常应该使用具体的存根实现声明此类带注释的查找方法,以便它们与 Spring 的组件扫描规则兼容,其中默认情况下会忽略抽象类。此限制不适用于显式注册或显式导入的 bean 类。
以上信息摘抄于 Spring 官方文档,如果其他内容,可参照 Spring Framework 官方文档。
网友评论