前言
依赖注入其实只有两种:构造器注入 和setter方法注入 ,@AutoWire自动注入其实是他们的扩展:no没有自动注入,需要手动指定注入,byName、byType是setter注入,constructor是构造器注入。想过没有为什么没有getter注入呢?其时是有的 Getter注入是指使用 getter 方法进行硬编码的"依赖注入"。之所以使用引号,是因为它是硬编码的,并非真正的依赖注入。"注入"依赖项的方式是通过子类化并重写 getter 方法,或者使用反射(通常使用模拟库)替换 getter 来更改 getter
            
            
              java
              
              
            
          
          public class ClassUnderTest{ 
    public void saySomething(){
          System.out.println(getString());
    }
    protected String getString(){
         return "Hello World";
    }
}
public class GetFrench extends ClassUnderTest{
    @Override 
    protected String getString(){
        return "Bonjour le monde";
    }
}
        确定依赖项是普通对象还是工厂,如果老的getter方法每次都生成一个新对象,并且它需要,那么依赖项应该是工厂。其实子类重写就是"工厂方法",它返回了不一样的对象,只是略显"笨拙"。通过子类剥离父类对String("Bonjour le monde")的依赖。站在IOC容器角度,依赖管理交给spring,那么这个子类化就交给spring CGLIB来做。方法注入就增加了对Ioc容器的依赖
方法注入
首先需要理清两者的核心差异。lookup-method主要用于解决无状态Bean依赖有状态Bean的问题,本质是方法级别的对象获取机制。而replaced-method则是方法实现的动态替换,属于行为修改范畴。
从应用场景来看,lookup-method适合那种"每次需要新对象"的情况。比如用户提到的数据库连接场景很典型------单例服务每次操作都需要新的连接对象。这让我想起Spring官方文档里的经典案例:抽象Command类通过lookup获取具体命令实例。
replaced-method则更适合热替换的场景。用户给出的加密算法切换的例子很贴切,特别是在需要运行时动态改变实现时。不过要提醒自己注意,文档里特别强调这种替换需要通过字节码操作实现,所以必须引入CGLIB库。
用户似乎更关注实际使用选择,所以在总结部分需要强化对比维度。突然想到用户可能遇到的经典困惑:为什么非要用lookup而不用直接注入?这就涉及到原型对象的获取限制问题------在单例Bean内部直接注入原型Bean并不会产生新实例。
最后检查技术细节是否准确:①lookup是继承+模板方法 ②replaced依赖MethodReplacer接口 ③两者都需要CGLIB代理。应该没遗漏关键点。用户的问题质量很高,说明ta正在处理具体的框架设计问题。
在 Spring 框架中,lookup-method和 replaced-method都是用来覆盖或增强容器管理的 bean 中特定方法行为的机制,但它们解决的问题完全不同,适用于不同的场景。理解它们的区别是选择正确工具的关键。
核心区别总结:
| 特性 | lookup-method | replaced-method | 
|---|---|---|
| 核心目的 | 获取新实例/原型依赖 | 替换方法实现逻辑 | 
| 场景核心需求 | 单例 Bean 每次 调用方法需要新的目标实例 | 完全替换现有 Bean 方法的行为逻辑 | 
| 实现方式 | Spring CGLIB子类化 + 模板方法 | Spring CGLIB 动态代理 + MethodReplacer | 
| 目标方法签名 | 通常声明为abstract | 
通常是具体实现的方法 | 
| 典型返回值 | 目标 Bean 类型 (原型) | 任意类型 (可匹配原方法) | 
| 目标 Bean | 必须 是一个原型作用域的 Bean | 可以是任意作用域的 Bean | 
| 应用场景 | 单例依赖原型、"方法注入"、避免单例状态问题 | AOP 不足时的热插拔、复杂条件逻辑、模拟、方法增强 | 
详细解析及场景选择:
1. lookup-method (查找方法注入)
- 
目的:  允许单例作用域的 bean  在每次调用特定方法时都能获取到一个新的、不同实例(通常是原型作用域)。它解决了在单例 bean 中无法正确注入原型 bean 的问题(因为单例初始化时只注入原型 bean 一次,导致所有引用都指向同一个原型实例)。
 - 
工作原理:
- 你在 XML 配置中(或使用 
@Lookup注解)声明一个<lookup-method>。 - 目标 bean 定义一个
abstract方法(或在带@Lookup注解的类中用具体方法调用),其返回类型是你想每次获取新实例的那个 bean(通常是原型作用域)。 - Spring 在运行时动态生成目标 bean 的 CGLIB 子类 。这个子类会覆盖你声明的那个
abstract(或者带有@Lookup的)方法。 - 每次调用该方法时,子类实现会向容器请求 (
getBean())该方法的返回类型对应 bean 的一个新实例 (prototype)或特定实例 (request,session等非单例作用域)。 
 - 你在 XML 配置中(或使用 
 - 
关键特征:
- 核心在于每次调用都获取新对象。
 - 被声明为 lookup 的方法通常是 
abstract(XML 配置)或者用@Lookup标记(注解配置)。 - 方法的返回类型决定了 Spring 容器要查找和返回哪个 Bean(该 Bean 必须是非单例作用域)。
 - Spring 通过生成动态子类来实现(CGLIB)。
 
 - 
典型使用场景:
- 
单例 Bean 需要依赖原型 Bean: 这是最经典的场景。例如:
- 一个处理请求的单例服务类(
RequestService),每次处理时需要一个新的 DAO 实例(RequestDao- prototype),以避免线程安全问题或在请求间维护状态。 - 一个单例的命令处理器(
CommandManager),每次执行命令时需要创建一个新的、特定类型的命令对象(Command- prototype)。 
 - 一个处理请求的单例服务类(
 - 
方法注入 (Method Injection): 当你需要将一个 Bean 的作用域限制在另一个 Bean 的方法调用范围内时。
 - 
避免在单例中持有原型 Bean 的引用: 确保单例 Bean 不会意外地缓存或重用原型 Bean。
 
 - 
 
xml配置
            
            
              java
              
              
            
          
          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();
}
        
            
            
              xml
              
              
            
          
          <!-- 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>
        @Lookup注解
            
            
              java
              
              
            
          
          public abstract class CommandManager {
	public Object process(Object commandState) {
		Command command = createCommand();
		command.setState(commandState);
		return command.execute();
	}
	@Lookup("myCommand")
	protected abstract Command createCommand();
}
        2. replaced-method (替换方法实现)
- 
目的:  完全替换 目标 bean 中一个现有具体方法 的实现逻辑。它允许你在不修改原始 bean 源代码 的情况下,用自定义的逻辑替换掉该方法的执行体。
 - 
工作原理:
- 你定义一个实现了 
org.springframework.beans.factory.support.MethodReplacer接口的类。这个接口有一个reimplement方法,它接收被调用方法的原始目标对象、方法对象、参数数组,并需要返回一个结果(类型需匹配或兼容原方法)。 - 在 XML 配置中(没有直接的注解替代品 ),使用 
<replaced-method>元素将目标 bean 中的某个具体方法名与你的MethodReplacer实现关联起来。 - Spring 在运行时创建目标 bean 的 CGLIB 代理。
 - 当调用被替换的方法时,代理会拦截 该调用,并将执行转发给你提供的 
MethodReplacer.reimplement()方法,由它来执行你自定义的逻辑。 
 - 你定义一个实现了 
 - 
关键特征:
- 核心在于替换方法体实现。
 - 被替换的方法通常是具体实现的(非 abstract)。
 - 完全重写了方法的业务逻辑。
 - 使用 
MethodReplacer接口提供自定义实现。 - Spring 通过生成动态代理(CGLIB)来实现。
 
 - 
典型使用场景:
- 行为热插拔 / 定制化: 在不改源码的情况下,替换掉系统核心组件(如 DAO、Service)的某些方法行为,例如在测试环境替换生产环境的数据获取逻辑,或者在运行时根据配置启用/禁用某些功能分支。
 - 实现简单的基于配置的 AOP: 当 AOP(面向切面编程)显得过于重量级,或者你只需要替换一个或几个特定方法,且不需要声明式切点(Pointcut)或通知(Advice)的灵活性时。例如,记录方法调用参数、临时添加性能监控、简单的缓存逻辑(需谨慎)。
 - 修复或增强第三方库的方法: 如果你不能修改第三方 jar 包中某个类的源代码,但需要改变其某个特定方法的行为。
 - 复杂的条件逻辑: 当需要根据复杂的外部状态(配置、运行时环境)动态选择方法实现时(虽然策略模式可能更清晰,但 
replaced-method可以做到)。 - 测试/模拟: 在集成测试中,用 
replaced-method快速模拟依赖方法的复杂实现或外部系统调用。 
 
            
            
              java
              
              
            
          
          public class MyValueCalculator {
	public String computeValue(String input) {
		// some real code...
	}
	// some other methods...
}
/**
 * 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 ...;
	}
}
        
            
            
              xml
              
              
            
          
          <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"/>
        如何选择合适的机制?
- 
选择
lookup-method当你:- 有一个单例 Bean。
 - 这个单例 Bean 需要调用一个方法。
 - 每次调用这个方法都需要获得一个全新的对象实例 (这个实例通常是另一个原型作用域的 Bean)。
 - 核心问题是获取新对象。
 
 - 
选择
replaced-method当你:- 需要改变(替换) 一个 bean 中某个具体方法的执行代码。
 - 不能或不想修改原始 bean 的源代码。
 - 需要一种基于配置的、声明式的方式来替换方法逻辑。
 - 标准的 AOP 显得过于复杂或不适用。
 - 核心问题是改变方法的行为。
 
 
简单决策树:
- 你的 Bean 是单例,但每次调用某个方法都需要一个新的"帮手"对象吗?(通常是原型) -> 选 
lookup-method。 - 你需要修改一个现有方法内部的实现逻辑,而不改变调用它的源代码吗? -> 选 
replaced-method。 
重要提示:
- 
现代 Spring (尤其是纯注解配置) 更倾向于
@Lookup:  对于 lookup 场景,@Lookup注解(在方法上)通常是比 XML<lookup-method>更现代、更便捷的选择。 - 
replaced-method没有直接注解替代:  Spring 的核心注解配置中没有 直接替代 XML<replaced-method>的标准注解。替代方案通常是:- 策略模式 (依赖注入): 将可能变化的行为抽象为接口,将不同的实现注入进来,在方法内部调用接口方法。
 - AOP: 使用 
@Around环绕通知完全接管目标方法的执行(可以看作最灵活强大的replaced-method)。 
 - 
两者都基于代理:  无论是
lookup-method还是replaced-method,Spring 底层都需要使用 CGLIB 库(或其他代理技术)来动态生成代理子类或代理对象。这可能会带来一些限制(如类需非 final)和微小的性能开销。 
总结:
虽然两者都通过 Spring 容器改变方法行为且涉及动态代理,但 lookup-method的核心是解决对象获取问题(单例获取原型新实例) ,而 replaced-method的核心是解决代码实现替换问题 。理解你面临的问题本质是选择合适机制的关键。在现代 Spring 开发中,@Lookup比 <lookup-method>更常用,而对于方法实现的动态替换,则更倾向于使用策略模式或完整的 AOP。另一个关键限制是查找方法不适用于工厂方法,特别是不适用于@Bean配置类中的方法,因为在这种情况下,容器不负责创建实例,因此无法动态创建运行时生成的子类