此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
方法注入
在大多数应用场景中,容器中的大多数 Bean 都是单例。当一个单例 Bean 需要与另一个单例 Bean 或非单例 Bean需要与另一个非单例 Bean 协作时,您通常会处理依赖关系通过将一个 Bean 定义为另一个 Bean 的属性。当 Bean 生命周期不同时,就会出现问题。假设单例 Bean A 需要使用非单例(原型)Bean B,也许在 A 上的每个方法调用时。容器仅创建单例 Bean A一次,因此只有一次设置属性的机会。容器不能每次需要 Bean B 的新实例时,都会向 Bean A 提供一个新实例。
解决方案是放弃一些控制反转。您可以通过实现ApplicationContextAware
接口 和制作一个getBean("B")
调用容器每次 Bean A 需要时,都会请求 (a通常是新的) bean B 实例。以下示例显示了这种方法:
-
Java
-
Kotlin
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* A class that uses a stateful Command-style class to perform
* some processing.
*/
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;
}
}
package fiona.apple
// Spring-API imports
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
// A class that uses a stateful Command-style class to perform
// some processing.
class CommandManager : ApplicationContextAware {
private lateinit var applicationContext: ApplicationContext
fun process(commandState: Map<*, *>): Any {
// grab a new instance of the appropriate Command
val command = createCommand()
// set the state on the (hopefully brand new) Command instance
command.state = commandState
return command.execute()
}
// notice the Spring API dependency!
protected fun createCommand() =
applicationContext.getBean("command", Command::class.java)
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.applicationContext = applicationContext
}
}
前面的情况是不可取的,因为业务代码知道并耦合到Spring 框架。方法注入,Spring IoC 的一个高级功能容器,让你干净地处理这个用例。
查找方法注入
查找方法注入是容器覆盖容器管理的 bean 上的方法的能力,并在 容器。 查找通常涉及原型 bean,如上一节中描述的场景中。Spring 框架通过使用 CGLIB 库中的字节码生成来实现此方法注入,以动态生成覆盖该方法的子类。
|
在CommandManager
类,则Spring 容器动态覆盖createCommand()
方法。 这CommandManager
class 没有任何 Spring 依赖项,因为重新设计的示例显示:
-
Java
-
Kotlin
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();
}
package fiona.apple
// no more Spring imports!
abstract class CommandManager {
fun process(commandState: Any): Any {
// grab a new instance of the appropriate Command interface
val command = createCommand()
// set the state on the (hopefully brand new) Command instance
command.state = commandState
return command.execute()
}
// okay... but where is the implementation of this method?
protected abstract fun createCommand(): Command
}
在包含要注入的方法的客户端类中(CommandManager
在此case) 中,要注入的方法需要以下形式的签名:
<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>
<!-- commandManager uses myCommand prototype bean -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
标识为commandManager
调用自己的createCommand()
方法 每当它需要一个新的实例时myCommand
豆。 部署时必须小心 这myCommand
bean 作为原型,如果这确实是需要的。如果它是单示例,则myCommand
每次返回 bean。
或者,在基于注释的组件模型中,您可以通过@Lookup
注释,如以下示例所示:
-
Java
-
Kotlin
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup("myCommand")
protected abstract fun createCommand(): Command
}
或者,更惯用地说,您可以依赖目标 bean 根据declared 返回类型解析 lookup 方法:
-
Java
-
Kotlin
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup
protected abstract fun createCommand(): Command
}
访问不同作用域的目标 Bean 的另一种方法是 您可能还会发现 |
任意方法替换
与查找方法注入相比,方法注入的一种不太有用的形式是能够 将托管 Bean 中的任意方法替换为另一个方法实现。你 可以安全地跳过本节的其余部分,直到您真正需要此功能。
对于基于 XML 的配置元数据,您可以使用replaced-method
元素设置为
将现有方法实现替换为另一个已部署 bean 的方法实现。考虑
以下类,它有一个名为computeValue
我们想要覆盖的:
-
Java
-
Kotlin
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
class MyValueCalculator {
fun computeValue(input: String): String {
// some real code...
}
// some other methods...
}
实现org.springframework.beans.factory.support.MethodReplacer
interface 提供了新的方法定义,如以下示例所示:
-
Java
-
Kotlin
/**
* 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 ...;
}
}
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
class ReplacementComputeValue : MethodReplacer {
override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
// get the input value, work with it, and return a computed result
val input = args[0] as String;
...
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"/>
您可以使用一个或多个<arg-type/>
元素中的<replaced-method/>
元素来指示被覆盖的方法的方法签名。签名仅当方法重载且多个变体存在于类中时,参数的签名才需要为方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下全部匹配java.lang.String
:
java.lang.String
String
Str
因为参数的数量通常足以区分每个可能的选择,所以这个快捷方式可以节省大量键入,让你只键入与参数类型匹配的最短字符串。