对于最新稳定版本,请使用Spring Framework 7.0.1spring-doc.cadn.net.cn

方法注入

在大多数应用场景中,容器中的大多数豆子都是单粒豆。当一颗单粒豆需要 与其他单粒豆合作,或者非单粒豆需要协作 对于另一个非单例豆,通常通过定义一个来处理依赖关系 豆子作为他者的财产。当豆子生命周期出现时,问题就会出现 不同。假设单例豆A需要使用非单例(原型)豆B, 也许在A上的每个方法调用时。容器仅生成单点豆A 一次,因此只有一次机会设置属性。容器不能 每次需要豆子B时,都为豆子A提供一个新的豆子实例。spring-doc.cadn.net.cn

解决方案是放弃某种控制权的反转。你可以通过实现应用上下文感知接口 以及制作getBean(“B”)呼叫集装箱请求 (a 通常是新的)豆子B实例,每次豆子A需要时都会进行。以下示例 展示了这种方法:spring-doc.cadn.net.cn

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 Framework。方法注入,Spring IoC 的一个较为先进的功能 容器,可以让你更干净利落地处理这个用例。spring-doc.cadn.net.cn

你可以在这篇博客文章中了解更多关于方法注射动机的信息。spring-doc.cadn.net.cn

查找方法注入

查找方法注入是容器覆盖 上方法的能力 容器管理的豆子,返回另一个命名豆子的查找结果。 容器。查找通常涉及一个原型豆,如描述的情景 在前一节。春季框架 通过从 CGLIB 库生成字节码实现该方法注入 动态生成一个覆盖该方法的子类。spring-doc.cadn.net.cn

  • 为了让这种动态子类工作,Spring Bean 容器的类 子类不能最后,且被覆盖的方法不能最后也。spring-doc.cadn.net.cn

  • 单元测试一个具有抽象方法要求你对该职业进行子类 你自己并提供一个存根实现抽象方法。spring-doc.cadn.net.cn

  • 组件扫描也需要混凝土方法,而扫描需要混凝土 课程要选。spring-doc.cadn.net.cn

  • 另一个关键限制是查找方法无法与工厂方法兼容,且 特别是不@Bean配置类中的方法,因为在这种情况下, 容器不负责创建实例,因此无法创建 一个运行时实时生成的子类。spring-doc.cadn.net.cn

指挥经理在上一个代码片段中的类,以及 Spring 容器动态覆盖了createCommand()方法。这指挥经理类没有任何 Spring 依赖,因为 重做后的示例显示:spring-doc.cadn.net.cn

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
}

在包含要注入方法的客户端类中(指挥经理在这方面 需要以下形式的签名:spring-doc.cadn.net.cn

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果该方法为抽象,动态生成子类实现了该方法。 否则,动态生成子类覆盖定义在 的具体方法 原始班级。请考虑以下例子:spring-doc.cadn.net.cn

<!-- 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>

豆子被识别为commandManager自称createCommand()方法 每当需要新的实例时myCommand(我的指挥)豆。你必须小心部署 这myCommand(我的指挥)如果真的需要的话,豆子就是原型。如果是 单示例,即同一实例myCommand(我的指挥)每次都归还豆豆。spring-doc.cadn.net.cn

或者,在基于注释的组件模型中,你可以声明查询 通过@Lookup注释,如下示例所示:spring-doc.cadn.net.cn

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
}

或者,更习惯地说,你也可以指望目标豆子会被 声明查找方法的返回类型:spring-doc.cadn.net.cn

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
}

注意,通常你应该用具体的声明声明这种带注释的查找方法 存根实现,以便它们与 Spring 组件兼容 扫描规则,抽象类默认被忽略。但这一限制并不 应用到明确注册或明确导入的Beans。spring-doc.cadn.net.cn

另一种访问不同范围目标豆的方法是ObjectFactory/ 提供商注射点。请参见“Scoped Beans as Dependencies”。spring-doc.cadn.net.cn

你也可能找到ServiceLocatorFactoryBean(在org.springframework.beans.factory.configpackage)被用于。spring-doc.cadn.net.cn

任意方法替换

一种不如查找方法注入有用的方法形式是能够 用另一种方法实现替换管理豆中的任意方法。你 可以放心跳过这部分,直到你真正需要这个功能。spring-doc.cadn.net.cn

基于XML的配置元数据,您可以使用替换方法元素变 用另一种方法替换现有方法实现,用于部署的 BEAN。考虑 以下类,其方法称为计算值我们想要覆盖的:spring-doc.cadn.net.cn

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.MethodReplacerInterface提供了新的方法定义,如下示例所示:spring-doc.cadn.net.cn

/**
 * 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 定义 类似于以下示例:spring-doc.cadn.net.cn

<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类型/><替换方法/>元素表示被覆盖方法的方法签名。签名 对于参数只有在方法被超载且有多个变体时才是必要的 存在于该类中。为方便起见,参数的类型字符串可以是 全限定类型名称的子串。例如,以下全部匹配java.lang.字符串:spring-doc.cadn.net.cn

java.lang.String
String
Str

因为参数数量通常足以区分每种可能的变量 选择,这个快捷方式可以节省大量输入,因为你只需输入 与参数类型匹配的最短字符串。spring-doc.cadn.net.cn