官方文档说明:

在Spring5核心的1.4.6章节

在大多数应用程序方案中,容器中的大多数bean都是 singletons 。当单例bean需要与另一个单例bean协作或非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean生命周期不同时会出现问题。假设单例bean A需要使用非单例(原型)bean B,可能是在A上的每个方法调用上。容器只创建一次单例bean A,因此只有一次机会来设置属性。每次需要时,容器都不能为bean A提供bean B的新实例。

解决方案是放弃一些控制反转。您可以通过实现 ApplicationContextAware 接口 make bean A aware of the container ,并通过 making a getBean("B") call to the container 在每次Bean A需要时请求(通常是新的)bean B实例。以下示例显示了此方法:

// 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;
}
}

前面的内容是不可取的,因为业务代码知道并耦合到Spring Framework。方法注入是Spring IoC容器的一个高级功能,可以让您干净地处理这个用例。

查找方法注入

Lookup方法注入是容器覆盖容器管理bean上的方法并返回容器中另一个命名bean的查找结果的能力。查找通常涉及原型bean,如 the preceding section 中描述的场景。 Spring Framework通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。

  • 为了使这个动态子类工作,Spring bean容器子类不能 final 的类,以及要重写的方法也不能 final

  • 对具有 abstract 方法的类进行单元测试要求您自己对该类进行子类化,并提供 abstract 方法的存根实现。

  • 组件扫描也需要具体方法,这需要具体的类来获取。

  • 另一个关键限制是查找方法不适用于工厂方法,特别是配置类中没有 @Bean 方法,因为在这种情况下,容器不负责创建实例,因此无法动态创建运行时生成的子类。

对于前面代码片段中的 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();
}

在包含要注入的方法的客户端类(在本例中为 CommandManager )中,要注入的方法需要以下形式的签名:

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

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>

标识为 commandManager 的bean在需要 myCommand bean的新实例时调用自己的 createCommand() 方法。如果实际需要的话,您必须小心将 myCommandbean部署为原型。如果是 singleton ,则每次都返回 myCommand bean的相同实例。

或者,在基于注释的组件模型中,您可以通过 @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();
}

或者,更具惯用性,您可以依赖于针对查找方法的声明返回类型解析目标bean:

public abstract class CommandManager {

public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}

@Lookup
protected abstract MyCommand createCommand();
}

请注意,您通常应该使用具体的存根实现来声明这种带注释的查找方法,以使它们与Spring的组件扫描规则兼容,其中默认情况下抽象类被忽略。此限制不适用于显式注册或显式导入的bean类。

任意方法更换

与查找方法注入相比,一种不太有用的方法注入形式是能够使用另一个方法实现替换托管bean中的任意方法。您可以安全地跳过本节的其余部分,直到您确实需要此功能。

使用基于XML的配置元数据,您可以使用 replaced-method 元素将已存在的方法实现替换为已部署的bean。考虑以下类,它有一个我们想要覆盖的名为computeValue 的方法:

public class MyValueCalculator {

public String computeValue(String input) {
// some real code...
}

// some other methods...
}

实现 org.springframework.beans.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 ...;
}
}

部署原始类并指定方法覆盖的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"/>

您可以在 <replaced-method/> 元素中使用一个或多个 <arg-type/> 元素来指示被覆盖的方法的方法签名。仅当方法重载且类中存在多个变体时,才需要参数的签名。为方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下全部匹配 java.lang.String

java.lang.String
String
Str

代码演示:

look-up:

public abstract class Car {

    //用于lookup-method注入
@Lookup("taxi")
public abstract Taxi createTaxi(); private Taxi taxi; public Taxi getTaxi() {
return taxi;
} //setter注入
public void setTaxi(Taxi taxi) {
this.taxi = taxi;
} }
public class Taxi {
public void say() {
System.out.println("I am a Taxi...");
}
}

xml文件

<!-- ====================lookup-method属性注入==================== -->
<bean id="car" class="spring2.Car">
<!--注意:下面这句配置和lookup-method注入没有关系,我们只是为了出于演示和说明配置该bean -->
<property name="taxi" ref="taxi" />
<!--lookup-method注入 -->
<lookup-method name="createTaxi" bean="taxi" />
</bean>
<bean id="taxi"
class="spring2.Taxi" scope="prototype" />

测试类:

public class Test1 {

       ClassPathXmlApplicationContext xmlBeanFactory = null;

        @Before
public void initXmlBeanFactory() {
System.out.println("\n========测试方法开始=======\n");
xmlBeanFactory = new ClassPathXmlApplicationContext("spring2.xml");
} @After
public void after() {
System.out.println("\n========测试方法结束=======\n");
} @Test
public void test8() {
// 测试lookup-method注入
Car car1 = xmlBeanFactory.getBean("car", Car.class);
Car car2 = xmlBeanFactory.getBean("car", Car.class); System.out.println("Car:singleton,所以animal1==animal2应该为" + (car1 == car2)); Taxi dog1 = car1.getTaxi();
Taxi dog2 = car2.getTaxi();
System.out.println("Taxi:prototype,Car:singleton,未使用lookup-method注入所以dog1==dog2应该为" + (dog1 == dog2)); //注意:这里是通过createDog()方法获取
Taxi taxi3 = car1.createTaxi();
Taxi taxi4 = car2.createTaxi();
System.out.println("Taxi:prototype,Car:singleton,使用了lookup-method注入所以dog3==dog4应该为" + (taxi3 == taxi4)); } }

结果:

========测试方法开始=======
Car:singleton,所以animal1==animal2应该为true
Taxi:prototype,Car:singleton,未使用lookup-method注入所以dog1==dog2应该为true
Taxi:prototype,Car:singleton,使用了lookup-method注入所以dog3==dog4应该为false ========测试方法结束=======

replaced-method

public class ReplaceDog implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("Hello, I am a white dog..."); Arrays.stream(args).forEach(str -> System.out.println("参数:" + str));
return obj;
}
}
public class OriginalDog {
public void sayHello() {
System.out.println("Hello,I am a black dog...");
} public void sayHello(String name) {
System.out.println("Hello,I am a black dog, my name is " + name);
}
}

xml配置

<!-- ====================replace-method属性注入==================== -->
<bean id="dogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.ReplaceDog"/>
<bean id="originalDogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.OriginalDog">
<replaced-method name="sayHello" replacer="dogReplaceMethod">
<arg-type match="java.lang.String"></arg-type>
</replaced-method>
</bean>

测试类:

@Test
public void test9() {
//测试replace-method注入
OriginalDog originalDog = xmlBeanFactory.getBean("originalDogReplaceMethod", OriginalDog.class);
originalDog.sayHello("输出结果已经被替换了。。。");
}

结果:

========测试方法开始=======

十二月 09, 2019 3:59:33 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3caeaf62: startup date [Mon Dec 09 15:59:33 CST 2019]; root of context hierarchy
十二月 09, 2019 3:59:33 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring2.xml]
Hello, I am a white dog...
参数:输出结果已经被替换了。。。 ========测试方法结束=======

lookup-method和replace-method注入的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object

    有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...

  2. 重构 改善既有代码的设计 Replace Method with Method Object(以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段.然后你可以在同一个对象中将这个大型函数分解为多个小型 ...

  3. Modified Least Square Method and Ransan Method to Fit Circle from Data

    In OpenCv, it only provide the function fitEllipse to fit Ellipse, but doesn't provide function to f ...

  4. 关于.ToList(): LINQ to Entities does not recognize the method ‘xxx’ method, and this method cannot be translated into a store expression.

    LINQ to Entities works by translating LINQ queries to SQL queries, then executing the resulting quer ...

  5. java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. ...

  6. Invalid character found in method name. HTTP method names must be tokens

      o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header Note: further occurrenc ...

  7. SpringBoot:Invalid character found in method name. HTTP method names must be tokens

    问题背景 关于SpringBoot应用挂了很久之后,会发生Invalid character found in method name. HTTP method names must be token ...

  8. Day04 -玩弄Ruby的方法:instance method与class method

    前情提要在第三天时,我们解说了如何在class里用include与extend,去使用module的method. Include is for adding methods to an instan ...

  9. tomcat 启动报错 Invalid character found in method name. HTTP method names must be tokens

    解决:Invalid character found in method name. HTTP method names must be tokens   阿里云上弄了一个tomcat,经常半夜发送崩 ...

  10. [Python] Python 之 function, unbound method 和 bound method

    首先看一下以下示例.(Python 2.7) #!/usr/bin/env python # -*- coding: utf-8 -*- class C(object): def foo(self): ...

随机推荐

  1. Oracle中的=:

    dept_code=:dCode =:在这里的意思是变量绑定

  2. oracle登录后无法使用,显示Connected to an idle instance

    1.登录情况: [oracle@localhost ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.3.0 Production on Mon Jul ...

  3. C++中的多重继承(一)

    1,C++ 中是否允许一个类继承自多个父类? 1,可以: 2,这种情况就是多重继承: 3,多重继承的表象就是一个类有多个父类: 4,这是 C++ 非常特别的一个特性,在其他的程序设计语言中比如 C#. ...

  4. python-day2(学前了解)

    编程语言分类 编程语言是用来和计算机交互的,但计算机只认识0和1 机器语言(低级语言) 直接和硬件交互 用0和1和计算机交流 优点:执行效率高 缺点:开发效率低 汇编语言 直接和硬件交互 优点:开发效 ...

  5. 编辑器IDE之VSCode

    很多时候面临换项目组,公司内部换等等,需要清除之前的权限,电脑更换等... 确实很烦人,所以记录也是给自己下次更加快速方便的使用 插件安装 个人常用的一些插件,发现好用的会更新 插件名 功能 vsco ...

  6. 缓存---CDN(内容分发网络)

    4.CDN   内容分发网络(content distribution network,CDN)是一种互联的网络系统,它利用更靠近用户的服务器从而更快更可靠的将静态资源分发给用户. cdn主要有以下优 ...

  7. IntelliJ IDEA 2017 提示“Unmapped Spring configuration files found.Please configure Spring facet.”解决办法

    当把自己的一个项目导入IDEA之后,Event Log提示“Unmapped Spring configuration files found.Please configure Spring face ...

  8. 6U VPX 高性能计算存储板卡

    基于6U VPX的 XC7VX690T+C6678的双FMC接口雷达通信处理板   一.板卡概述 高性能VPX信号处理板基于标准6U VPX架构,提供两个标准FMC插槽,适用于电子对抗或雷达信号等领域 ...

  9. auto_ptr为什么不能做为vector的元素

    昨天看effectve c++的时候,发现了auto_ptr这个东西.由于我待过的公司都是用的老版c++,代码里智能指针什么的完全没有出现过,都是直接操作的原始指针.虽说我很少出错,但是总归还是不太安 ...

  10. VS2013 删除"附加依赖项"中“继承的值”

    经过好几次尝试,都无法在VS2013中直接删除“继承的值”,于是另辟蹊径,找到了一种解决方法. 相对而言,在 VS2010 中干这件事会容易一点,或者说,成功率更高一点,于是,我的思路就是再装一个 V ...