1.4.6. Method injection
1.4.6. 方法注入
??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 only creates the singleton bean A 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需要使用非单例(原型)bean B,可能在A上的每个方法调用上。容器只创建一次单例bean A,因此只有一次机会来设置属性。 每次需要时,容器都不能向bean A提供bean B的新实例。
??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 is an example of this approach: ??解决方案是放弃一些控制反转。 您可以通过实现ApplicationContextAware****接口来让bean A知道容器,并且每当bean A需要时,通过对容器的getBean(“B”)调用请求(通常是新的)bean B实例。 以下是这种方法的一个例子:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
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, allows this use case to be handled in a clean fashion. ??前面的内容是不可取的,因为业务代码知道并且耦合到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, to 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 generate dynamically a subclass that overrides the method. ??查找方法注入是容器覆盖容器管理的bean上方法的能力,以返回容器中另一个命名bean的查找结果。 查找通常包含一个原型bean,如前一节所述。 Spring Framework通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类,从而实现了此方法注入。
- For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
- 对于这个动态子类的工作,Spring bean容器将继承的类不能是最终的,并且被覆盖的方法也不是最终的。
- Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
- 对具有抽象方法的类进行单元测试需要您自己对该类进行子类化并提供抽象方法的存根实现。
- Concrete methods are also necessary for component scanning which requires concrete classes to pick up.
- 具体的方法也是必需的组件扫描,它需要具体的类来拾取。
- A further key limitation is that lookup methods won’t work with factory methods and in particular not with @Bean methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.
- 另一个关键的限制是查找方法不适用于工厂方法,特别是不在配置类中使用@Bean方法,因为在这种情况下容器不负责创建实例,因此无法创建运行时生成的 飞行中的子类。
??Looking at the CommandManager class in the previous code snippet, you see that the Spring container will dynamically override the implementation of the createCommand() method. Your CommandManager class will not have any Spring dependencies, as can be seen in the reworked example: ??查看前面的代码片段中的CommandManager类,可以看到Spring容器将动态覆盖createCommand()方法的实现。 您的CommandManager类不会有任何Spring依赖关系,如在重做示例中所示:
package fiona.apple;
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected abstract Command createCommand();
}
In the client class containing 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] 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. For example: ??如果方法是抽象的,则动态生成的子类将实现该方法。 否则,动态生成的子类会覆盖原始类中定义的具体方法。 例如:
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
</bean>
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
??The bean identified as commandManager calls its own method createCommand() 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 as a singleton, the same instance of the myCommand bean is returned each time. ??标识为commandManager的bean在需要myCommand bean的新实例时调用其自己的方法createCommand()。 您必须小心地将myCommand bean作为原型部署,如果这实际上是需要的话。 如果它是单例,则每次返回myCommand bean的相同实例。
??Alternatively, within the annotation-based component model, you may declare a lookup method through the @Lookupannotation: ??另外,在基于注解的组件模型中,你可以通过@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 may 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
protected abstract MyCommand createCommand();
}
Note that you will 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 in case of explicitly registered or explicitly imported bean classes. 请注意,您通常会使用具体的存根实现来声明这样的带注释的查找方法,以便它们与Spring的组件扫描规则兼容,其中抽象类在默认情况下被忽略。这种限制不适用于显式注册或显式导入的bean类。
Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. Check out Scoped beans as dependencies. 访问不同范围的目标Bean的另一种方式是ObjectFactory / Provider注入点。检查Scoped bean作为依赖关系。 The interested reader may also find the ServiceLocatorFactoryBean (in theorg.springframework.beans.factory.config package) to be of use.
感兴趣的读者也可以找到ServiceLocatorFactoryBean(在org.springframework.beans.factory.config包中)以供使用。
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. Users may safely skip the rest of this section until the functionality is actually needed. 与查找方法注入相比,不太有用的方法注入形式是能够用另一个方法实现来替换托管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, with a method computeValue, which we want to override: 使用基于XML的配置元数据,您可以使用replaced-method元素将现有的方法实现替换为已部署的bean。考虑下面的类,我们想要覆盖一个方法computeValue:
public class MyValueCalculator {
public String computeValue(String input) {
}
}
A class implementing the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition. 实现org.springframework.beans.factory.support.MethodReplacer接口的类提供了新的方法定义。
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
String input = (String) args[0];
...
return ...;
}
}
The bean definition to deploy the original class and specify the method override would look like this: 部署原始类并指定方法覆盖的bean定义如下所示:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<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 contained <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: 您可以在 <replaced-method />元素中使用一个或多个包含的<arg-type /> 元素来指示被覆盖的方法的方法签名。 只有当方法过载并且类中存在多个变体时,参数的签名才是必需的。 为了方便,参数的类型字符串可能是完全限定类型名称的子字符串。 例如,以下全部匹配java.lang.String:
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 allowing you to type only the shortest string that will match an argument type. 由于参数的数量通常足以区分每个可能的选项,因此只需键入与参数类型匹配的最短字符串,此快捷键就可以节省大量输入。
|