1.4.6. Method Injection 方法注入
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
在大多数应用程序场景中,容器中的大多数bean
都是单例的。当一个单例bean
需要与另一个单例bean
协作时,或者一个非单例bean
需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean的生命周期不同时,问题就出现了。假设单例bean A需要使用非单例(prototype)bean B,可能在对A的每次方法调用上都是如此,容器只创建一次单例bean A,因此只获得一次设置属性的机会。容器不能在每次需要bean B的时候都向bean A提供一个新的实例。
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware
interface, and by making a getBean("B")
call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach:
一个解决办法是放弃一些控制反转。您可以通过实现ApplicationContextAware
接口,以及在每次bean A需要bean B实例时,通过对容器进行getBean(“B”)调用来让bean A知道容器。下面的例子展示了这种方法:
// 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;
}
}
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly.
前面的方法并不可取,因为业务代码知道Spring框架并与之耦合。方法注入是Spring IoC容器的一种高级特性,它允许您干净地处理这个用例。
You can read more about the motivation for Method Injection in this blog entry.
您可以在这篇博客文章中阅读更多关于方法注入动机的内容。
Lookup Method Injection 查找方法注入
Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
查找方法注入是指容器覆盖容器管理bean上的方法并返回容器中另一个已命名bean的查找结果的能力。查找通常涉及prototype bean,如上一节所述的场景。Spring框架通过使用来自CGLIB库的字节码生成动态生成覆盖该方法的子类来实现这种方法注入。
For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be
final
, and the method to be overridden cannot befinal
, either.
要使这个动态子类工作,Spring bean容器子类的类不能是final,要覆盖的方法也不能是final。
Unit-testing a class that has an
abstract
method requires you to subclass the class yourself and to supply a stub implementation of theabstract
method.
单元测试具有抽象方法的类需要您自己创建类的子类,并提供抽象方法的存根实现。
Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
具体的方法对于组件扫描也是必要的,这需要具体的类来拾取。
A further key limitation is that lookup methods do not work with factory methods and in particular not with
@Bean
methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.
另一个关键的限制是,查找方法不能与工厂方法一起工作,特别是不与配置类中的@Bean方法一起工作,因为在这种情况下,容器不负责创建实例,因此不能动态地创建运行时生成的子类。
In the case of the CommandManager
class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand()
method. The CommandManager
class does not have any Spring dependencies, as the reworked example shows:
对于前面代码片段中的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();
}
In the client class that contains the method to be injected (the CommandManager
in this case), the method to be injected requires a signature of the following form:
在包含要注入的方法的客户端类中(本例为CommandManager),要注入的方法需要以下形式的签名:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
If the method is abstract
, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. Consider the following example:
如果方法是抽象的,则动态生成的子类实现该方法。否则,动态生成的子类将覆盖在原始类中定义的具体方法。考虑下面的例子:
<!-- 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>
The bean identified as commandManager
calls its own createCommand()
method whenever it needs a new instance of the myCommand
bean. You must be careful to deploy the myCommand
bean as a prototype if that is actually what is needed. If it is a singleton, the same instance of the myCommand
bean is returned each time.
标识为commandManager
的bean在需要myCommand
bean的新实例时调用它自己的createCommand()
方法。如果真的需要将myCommand
bean部署为prototype ,则必须小心。如果是单例,则每次都返回相同的myCommand
bean实例。
Alternatively, within the annotation-based component model, you can declare a lookup method through the @Lookup
annotation, as the following example shows:
或者,在基于注释的组件模型中,您可以通过@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();
}
Or, more idiomatically, you can rely on the target bean getting resolved against the declared return type of the lookup method:
或者,更习惯的做法是,您可以依赖于根据查找方法声明的返回类型
解析目标bean:
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup(注意这里在spring扫描规则时会失效,最好声明具体实现)
protected abstract MyCommand createCommand();
}
Note that you should typically declare such annotated lookup methods with a concrete stub implementation, in order for them to be compatible with Spring’s component scanning rules where abstract classes get ignored by default. This limitation does not apply to explicitly registered or explicitly imported bean classes.
请注意,您通常应该使用具体的存根实现来声明这种带注释的查找方法,以便它们与Spring的组件扫描规则兼容,其中抽象类在默认情况下会被忽略。此限制不适用于显式注册或显式导入的bean类。
Another way of accessing differently scoped target beans is an ObjectFactory / Provider injection point. See Scoped Beans as Dependencies.You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful. |
|
---|---|
访问作用域不同的目标bean的另一种方法是ObjectFactory/ Provider 注入点。请将作用域bean视为依赖项。您还可以在org.springframework.beans.factory package 中找到ServiceLocatorFactoryBean )是有用的 |
Arbitrary Method Replacement 任意的方法替换
A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. You can safely skip the rest of this section until you actually need this functionality.
方法注入的一种不如查找方法注入
有用的形式是用另一个方法实现替换托管bean中的任意方法的能力。您可以安全地跳过本节的其余部分,直到您真正需要此功能。
With XML-based configuration metadata, you can use the replaced-method
element to replace an existing method implementation with another, for a deployed bean. Consider the following class, which has a method called computeValue
that we want to override:
对于基于xml的配置元数据,您可以使用replaced-method
元素将一个已部署bean的现有方法实现替换为另一个方法实现。考虑下面的类,它有一个名为computeValue
的方法,我们想要覆盖它:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
A class that implements the org.springframework.beans.factory.support.MethodReplacer
interface provides the new method definition, as the following example shows:
实现org.springframework.bean .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 ...;
}
}
The bean definition to deploy the original class and specify the method override would resemble the following example:
部署原始类并指定方法覆盖的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"/>
You can use one or more <arg-type/>
elements within the <replaced-method/>
element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String
:
可以使用< replacement -method/>
元素中的一个或多个< argt-ype />
元素来指示被重写的方法的方法签名。只有当方法被重载并且类中存在多个变体时,参数的签名才有必要。为了方便,参数的类型字符串可以是完全限定类型名的子字符串。例如,以下所有匹配
java.lang.String
String
Str
Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by letting you type only the shortest string that matches an argument type.
由于参数的数量通常足以区分每种可能的选择,因此通过只输入与参数类型匹配的最短字符串,此快捷方式可以节省大量输入。
下一节:Bean Scopes Bean作用域