场景:当我们希望通过无状态Bean获得有状态Bean时,每次获得的有状态Bean都会是同一个对象。直白点说,就是当我们希望通过一个作用域为singleton的BeanA获得一个作用域为prototype的BeanB对象的实例时,每次获得的BeanB都是同一个实例。这时我们可以考虑通过方法注入实现之。
例子:一棵苹果树AppleTree,在应用上下文中我们将其作为一个singleton的Bean,其中提供一个方法用于获得Apple,即Apple apple=AppleTree.getApple()。理所当然,我们希望每次通过AppleTree.getApple()获得的Apple都是新的对象,即Apple Bean在上下文中的作用域是prototype。代码:
/**
*苹果树,有一属性Apple,虽然这看起来很怪异,但只是用来测试,姑且将就。
*/
class AppleTree{
private Apple apple;
public Apple getApple(){
return this.apple;
}
public void setApple(Apple apple){
this.apple = apple;
}
}
/**
*苹果,有一属性“年龄”,
*通过这一属性我们可以观察每次通过AppleTree获得的Apple对象是否相同
*/
class Apple {
//苹果年龄计数器,我们设置为每次加一
public static int ageCounter = 0;
private int age;
public void setAge(int age){
this.age = age;
}
public Apple(){
this.age=++ageCounter ;
}
public String toString(){
return "apple with age "+this.age+" days";
}
}
XML文件配置:AppleTree配置为singleton,Apple配置为prototype,我们意欲每次通过AppleTree获得Apple时都获得一个新的Apple:
<bean name="apple" class="org.liaofeng.lookup.Apple" scope="prototype"/>
<bean name="appleTree" class="org.liaofeng.lookup.AppleTree" scope="singleton">
<property name="apple" ref="apple"/>
</bean>
测试代码:
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("lookup.xml"));
AppleTree tree = (AppleTree) factory.getBean("appleTree");
Apple apple1 = tree.getApple();
Apple apple2 = tree.getApple();
System.out.println(apple1);
System.out.println(apple2);
运行结果:
apple with age 1 days
apple with age 1 days
运行结果分析:很明显,两次调用tree.getApple()方法获得的Apple对象是同一个对象,我们也可以通过apple1==apple2进行证明。运行结果也很容易理解,由于AppleTree作为singleton存在,注入其Apple的动作就只有一次。即使Bean apple的作用域设置为prototype,但是根本轮不到它起作用(只调用一次,那么prototype和singleton还有区别么?),之后每次调用getApple()方法都将获得第一次注入的Apple对象。
那么我们怎样达到期望,每次通过AppleTree.getApple()都获得新的Apple呢?首先我们可以考虑修改AppleTree,让其实现BeanFactoryAware接口,然后重新修改getApple()方法,见代码:
class AppleTree implements BeanFactoryAware{
private BeanFactory factory;
public void setBeanFactory(BeanFactory factory){
this.factory = factory;
}
private Apple apple;
public Apple getApple(){
return (Apple)factory.getBean("apple");
}
public void setApple(Apple apple){
this.apple = apple;
}
}
再通过代码测试可以发现我们可以每次都获得新的Apple,但是AppleTree需要实现BeanFactoryAware接口,从而与Spring耦合了,这是Spring本身极不提倡的。有没有什么更好的实现方法呢?那就是方法注入,见改动后的代码:
interface AppleTree {
/**
* AppleTree只提供一个getApple()方法,返回Apple。
* 对于这个返回值Apple,我们可以引用一个Apple Bean来,每次返回引用的Bean,
* 将这个Apple Bean设置为prototype就可以达到每次获得不同Apple目的,
* 感觉有点像:
* <bean id="appleTree" class="...">
* <property name="apple" ref="bean"/>
* </bean>
* 不同的是,此处的AppleTree作为interface(或抽象类)存在,并没有Apple属性,
* 而是一个返回值为Apple的public方法,
* 我们要做得就是通过<lookup-method>来建立这个方法和Apple Bean之间的关系,
* 让Apple Bean作为该方法的返回值。
* @return
*/
public abstract Apple getApple();
}
再修改配置文件:
<bean name="apple" class="org.liaofeng.lookup.Apple" scope="prototype"/>
<bean name="appleTree" class="org.liaofeng.lookup.AppleTree" scope="singleton">
<lookup-method name="getApple" bean="apple" />
<!--name指定方法的名字,bean指定方法的返回值,即这个方法返回值就是bean apple,由于我们将bean apple设置成了prototype,所以每次获得到得apple对象都是不同的
-->
</bean>
再次运行测试代码,就可以发现我们每次通过AppleTree.getApple()都获得不同的Apple对象。让人很不可思议的是,AppleTree明明是一个接口,怎么可以在Bean中进行实例化呢?一般的接口和抽象类,我们都不能直接配置为Bean,因为根本不可能实例化。但是在方法注入中,Spring却恰好支持,这需要了解Spring是怎样实现以上功能的。上述功能Spring是借助cglib实现的,它可以在运行期间动态的修改Class文件,为Bean动态的创建子类或实现类。上面的例子中,运行期间Spring通过cglib创建了一个AppleTree的实现,假设为AppleTreeImpl,然后指定一属性为Apple。通过实现BeanFactoryAware(或ApplicationContextAware)每次返回新建的Apple Bean。其效果等同如以下代码:
class AppleTreeImpl implements AppleTree,BeanFactoryAware{
private BeanFactory factory;
@Override
public Apple getApple() {
return (Apple)factory.getBean("apple");
}
@Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
this.factory = factory;
}
}
看到这里,明白了吧?最后需要注意的是,lookup方法注入需要AppleTree为接口和抽象类,其getApple()抽象方法要是public或protected(废话!)~~~~
分享到:
相关推荐
geojson-geometries-lookup :high_voltage: 大型GeoJSON的几何查找中的快速几何。 编码为 :red_heart: 作者西蒙妮·普里马罗萨( Simone Primarosa) 。 概要使用此包,您可以在给定的GeoJSON上执行以下操作(以及更...
- `LOOKUP`:在数组或向量中查找指定值,并返回匹配项。 - `MATCH`:在数组或引用中查找值的位置。 - `OFFSET`:基于给定引用偏移一定的行数和列数返回新的引用。 - `ROW`:返回引用的行号。 - `ROWS`:返回...
通过使用以下方法将conda-forge添加到您的通道中,可以从conda-forge通道安装nested-lookup : conda config --add channels conda-forge 启用conda-forge频道后,可以使用以下命令安装nested-lookup : conda ...
如何使用Spring从JNDI查找数据源 Server.xml <资源名称=“ jdbc / javatechie”全局=“ jdbc / javatechie” auth =“容器” type =“ javax.sql.DataSource” driverClassName =“ com.mysql.jdbc.Driver” url...
**Open Quick Definition Lookup (Ctrl+Shift+I):** - **用途:** 打开快速定义查看器。 - **说明:** 显示当前选中元素的定义。 **Go to Type Declaration (Ctrl+Shift+B):** - **用途:** 跳转到类型声明。 - **...
- 实现Repository接口:创建自定义的Repository接口,继承Spring Data MongoDB提供的基类,并定义所需的查询方法。 **5. 使用示例** 例如,定义一个UserRepository接口: ```java public interface ...
- `apache_lookup_uri`:获取与URI相关的所有信息。 - `apache_note`:获取或设置Apache服务器请求日志的条目。 - `getallheaders`:获取所有HTTP头部的值。 - `virtual`:执行Apache服务器的子请求(sub-...
- 参数3: 过滤条件(SQL语句中的WHERE条件)。 **2.3 getFieldValue2** - **用途:** 根据一个字段获取相关字段的值。 - **参数:** - 参数1: lookup字段的ID。 - 参数2: 查询的实体名称。 - 参数3: 返回的字段...
在Spring框架中,Lookup方法注入是一种特殊的依赖注入方式,它允许Spring容器动态替换bean中的某个方法,以便在运行时返回不同实例。这种方法主要用于处理单例bean依赖非单例bean的情况,确保每次调用都能获得一个新...
web服务器缓存实现原理,通过chrome f12观察web服务器缓存 后台action命中缓存hit Via X-cache X-Cache-Lookup
#### 3. CList 类 —— 链表类 - **成员变量**: - `pHead`:指向链表头部的指针。 - **成员函数**: - 构造函数:可以指定一个节点作为头节点。 - `SetpHead`:设置头节点。 - `AddNode`:在链表中添加一个...
JavaSpaces是Java RMI(远程方法调用)的扩展,提供了一种分布式的、无中心控制的通信方式。在JavaSpaces中,不同节点的应用程序可以直接通信,无需中间服务器或消息队列。这种通信方式允许并发、异步操作,且支持...
用例用例包括:将devicons放在目录中的文件之前ls | devicon-lookup --color向grep结果添加图标rg test | devicon-lookup --prefix :大型结果集的流式结果rg str --color always | devicon-lookup -c -p : | fzf --...
- 0000776: UniDBLookUpXXX: ListSource cursor position does not follow Lookup value - 0000773: UniDBGrid: Column.Title.Font/Color - 0000771: UniDBGrid: Column.Font property - 0000772: UniDBGrid: ...
FIPS 10-4码(2位) ISO 3166(2位数字) ISO 3166(3位数字) ISO 3166(数字) 互联网代码安装$ npm install country-code-lookup用法const lookup = require ( 'country-code-lookup' )// search by FIPSlookup ...
巴西邮政编码查找Rocket.Chat应用 ... 请随意填写,以提出改进建议并在Github存储库的“问题”下报告问题。 用法 您可以使用像这样的斜杠命令来请求rocket.cat: / cep 00000-000 甚至 / cep 0000000 ...
npm install affilinet-lookup 用法 affilinet-lookup -i <publisher> -p <api> -k 有关更多信息,请参阅affilinet-lookup --help 例子 基本的: var affilinet = require ( 'affilinet-lookup' ) ; affilinet ...
lookup-dns- dns.lookup替换dns.lookup标准方法的DNS缓存超级简单易用const request = require ( 'request' ) ;const { lookup } = require ( 'lookup-dns-cache' ) ;// With "request" modulerequest ( { url : '...
iso-countries-lookup 使用输入的基本容错能力的多种语言按国家/地区名称查找国家/地区代码(ISO 3166-1 alpha-2)。 返回给定国家/地区名称的ISO 3166-1 alpha-2国家/地区代码。 支持基于以及基于备用名称。 安装...
这份文档中包含的命令,描述了Cisco设备上操作和配置的基本方法,是学习Cisco网络管理的核心工具。它涉及的内容广泛,从基本的配置到路由协议的设置,再到网络安全和VLAN的配置,都是网络工程师必须掌握的知识点。...