官方文档说明:

在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. python 正则sub的使用

     self.content = re.sub(r'>|<',lambda x: '&gt' if x.group()[0] == '>' else '&lt' , s ...

  2. 查看主机CPU信息

    一.关于CPU的几个概念 CPU的作用 计算机中的中央处理单元(CPU)执行基本的计算工作 -- 运行程序.但是,一个单核的CPU同一时间只能一次执行一个任务,为了提高计算机的处理能力,也就出现了多C ...

  3. C语言Ⅰ博客作业05

    这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-3/homework/9827 我在这个课程的目 ...

  4. 小记---------idea新手操作

    加载jar包    file---project structrue ---modules---dependencies----  点+号  选择idea软件的位置的lib 添加自带的jar包,or  ...

  5. java使用face++简单实现人脸识别注册登录

    java使用face++简单实现人脸识别注册登录 前言 人脸识别,好高大上!!! 理解之后很简单. 支付宝使用的就是face++, 至于face++账号信息,apikey…..,本文不做讲述,网上很多 ...

  6. RegexKitLite库的使用

    首先在官网:  http://regexkit.sourceforge.net/下载RegexKitLite.h和RegexKitLite.m两个文件 将其添加到工程目录下 取消自动引用计数ARC 手 ...

  7. mysql之general log 日志

    开启 general log 将所有到达MySQL Server的SQL语句记录下来. 一般不会开启开功能,因为log的量会非常庞大.但个别情况下可能会临时的开一会儿general log以供排障使用 ...

  8. 最近用到的postgresql 的一些操作

    通过Linux命令行连接:在本机上连接操作 第一步:su - postgres进入到这个:-bash-4.2$ 第二步:psql进入到数据库 切换数据库: 命令:\c dbname 进入到数据库中 查 ...

  9. Coursera机器学习笔记(一) - 监督学习vs无监督学习

    转载 http://daniellaah.github.io/2016/Machine-Learning-Andrew-Ng-My-Notes-Week-1-Introduction.html 一. ...

  10. 用ajax写机器人聊天的案例

    HTML 中的文档 <body> <h3>简单的Ajax实例</h3> <div class="chatbox"> <!-- ...