lookup-method和replace-method注入
官方文档说明:
在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()
方法。如果实际需要的话,您必须小心将 myCommand
bean部署为原型。如果是 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注入的更多相关文章
- 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object
有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...
- 重构 改善既有代码的设计 Replace Method with Method Object(以函数对象取代函数)
你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段.然后你可以在同一个对象中将这个大型函数分解为多个小型 ...
- 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 ...
- 关于.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 ...
- java代码中init method和destroy method的三种使用方式
在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. ...
- 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 ...
- 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 ...
- Day04 -玩弄Ruby的方法:instance method与class method
前情提要在第三天时,我们解说了如何在class里用include与extend,去使用module的method. Include is for adding methods to an instan ...
- 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,经常半夜发送崩 ...
- [Python] Python 之 function, unbound method 和 bound method
首先看一下以下示例.(Python 2.7) #!/usr/bin/env python # -*- coding: utf-8 -*- class C(object): def foo(self): ...
随机推荐
- go的变量定义
package main //理解包的概念 import "fmt" var ( aa = 1 bb = "kkk" ss = true) func varia ...
- [转帖]达梦数据库(DM6)和ORACLE 10g的异同点
达梦数据库(DM6)和ORACLE 10g的异同点 https://bbs.aliyun.com/detail/351337.html 花花浪子 级别: 小白 发帖 0 云币 -41 加关注 ...
- 思考-继续思考在数据库中两个表join的问题
##在资源有限的情况下,如何做两个大表的join? --- 假设系统资源:内存1G,大表10G,小表2G. --- 都拿到内存中进行笛卡尔集肯定不行,内存没有那么大. 最简单的办法是对两个表建索引,但 ...
- java方法形参是引用类型
public void 方法名(Student s) 这里形参需要的是该类的对象或者子类对象(父类引用子类对象). 1.若为普通类:则可传入该类的实例对象即可,方法名(new Student()): ...
- 云服务器以及linux操作系统打开防火墙,在墙上开一个小口
在服务器运行时,需要在某个端口上开一个小口,以供外部访问 执行命令/sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT 8080为端口号,需要开的 ...
- shell基础#1
shell:能直接调用命令(python)1.bash的基本特性 ctrl+L 清屏2.IO重定向与管道符 都由shell提供 命令是一个可执行的二进制程序3.编程基础 编程原理 程序:执行某个功能的 ...
- 140. 后缀数组(hash + 二分 / 后缀数组)
题目链接 : https://www.acwing.com/problem/content/description/142/ Hash + 二分 #include <bits/stdc++.h& ...
- 3424:Candies(差分约束,Dijkstra)(配对堆优化
题面链接 题解 令x-y<=z表示x最大比y大z. 若b-a<=k1, c-b<=k2, c-a<=k3,那么c-a最大为多少呢?显然应该等于min(k1+k2, k3).可以 ...
- 对Elastic集群内部配置TLS加密通信及身份验证
1.介绍 官方宣布从6.8和7.1开始,免费提供多项安全功能.其中包括tls加密通信,基于角色访问控制等功能. 可以使用企业CA证书来完成这一步骤,但是一般情况下,我们可以通过elasticsearc ...
- 定义一个外部类Father,有成员变量name并赋一个初值。
1.内部类的使用:(1)定义一个外部类Father,有成员变量name并赋一个初值.(2)定义一个内部类Child,并定义一个getValue()方法,在方法中调用外部类Father的name变量.( ...