`
liaofeng_xiao
  • 浏览: 127291 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring day by day--day 3:lookup方法注入

阅读更多
    场景:当我们希望通过无状态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的几何查找中实现快速几何

    geojson-geometries-lookup :high_voltage: 大型GeoJSON的几何查找中的快速几何。 编码为 :red_heart: 作者西蒙妮·普里马罗萨( Simone Primarosa) 。 概要使用此包,您可以在给定的GeoJSON上执行以下操作(以及更...

    Excel中函数及函数功能汇总.pdf

    - `LOOKUP`:在数组或向量中查找指定值,并返回匹配项。 - `MATCH`:在数组或引用中查找值的位置。 - `OFFSET`:基于给定引用偏移一定的行数和列数返回新的引用。 - `ROW`:返回引用的行号。 - `ROWS`:返回...

    nested-lookup-feedstock:一个用于嵌套查找的conda-smithy存储库

    通过使用以下方法将conda-forge添加到您的通道中,可以从conda-forge通道安装nested-lookup : conda config --add channels conda-forge 启用conda-forge频道后,可以使用以下命令安装nested-lookup : conda ...

    spring-jndi-lookup:如何使用Spring从JNDI查找数据源

    如何使用Spring从JNDI查找数据源 Server.xml &lt;资源名称=“ jdbc / javatechie”全局=“ jdbc / javatechie” auth =“容器” type =“ javax.sql.DataSource” driverClassName =“ com.mysql.jdbc.Driver” url...

    android studio快捷键大全

    **Open Quick Definition Lookup (Ctrl+Shift+I):** - **用途:** 打开快速定义查看器。 - **说明:** 显示当前选中元素的定义。 **Go to Type Declaration (Ctrl+Shift+B):** - **用途:** 跳转到类型声明。 - **...

    Spring-Data-MongoDB3.2

    - 实现Repository接口:创建自定义的Repository接口,继承Spring Data MongoDB提供的基类,并定义所需的查询方法。 **5. 使用示例** 例如,定义一个UserRepository接口: ```java public interface ...

    全面解析PHP常用函数.doc

    - `apache_lookup_uri`:获取与URI相关的所有信息。 - `apache_note`:获取或设置Apache服务器请求日志的条目。 - `getallheaders`:获取所有HTTP头部的值。 - `virtual`:执行Apache服务器的子请求(sub-...

    CRM4.0培训教程

    - 参数3: 过滤条件(SQL语句中的WHERE条件)。 **2.3 getFieldValue2** - **用途:** 根据一个字段获取相关字段的值。 - **参数:** - 参数1: lookup字段的ID。 - 参数2: 查询的实体名称。 - 参数3: 返回的字段...

    深入理解Spring中的Lookup(方法注入)

    在Spring框架中,Lookup方法注入是一种特殊的依赖注入方式,它允许Spring容器动态替换bean中的某个方法,以便在运行时返回不同实例。这种方法主要用于处理单例bean依赖非单例bean的情况,确保每次调用都能获得一个新...

    web服务器缓存实现原理,通过chrome f12观察web服务器缓存

    web服务器缓存实现原理,通过chrome f12观察web服务器缓存 后台action命中缓存hit Via X-cache X-Cache-Lookup

    电话簿管理系统

    #### 3. CList 类 —— 链表类 - **成员变量**: - `pHead`:指向链表头部的指针。 - **成员函数**: - 构造函数:可以指定一个节点作为头节点。 - `SetpHead`:设置头节点。 - `AddNode`:在链表中添加一个...

    Javaspace---Deduct:显示如何使用 JavaSpaces 发送消息的简单示例

    JavaSpaces是Java RMI(远程方法调用)的扩展,提供了一种分布式的、无中心控制的通信方式。在JavaSpaces中,不同节点的应用程序可以直接通信,无需中间服务器或消息队列。这种通信方式允许并发、异步操作,且支持...

    devicon-lookup:在每个文件名的开头添加正确的devicon

    用例用例包括:将devicons放在目录中的文件之前ls | devicon-lookup --color向grep结果添加图标rg test | devicon-lookup --prefix :大型结果集的流式结果rg str --color always | devicon-lookup -c -p : | fzf --...

    unigui0.83.5.820

    - 0000776: UniDBLookUpXXX: ListSource cursor position does not follow Lookup value - 0000773: UniDBGrid: Column.Title.Font/Color - 0000771: UniDBGrid: Column.Font property - 0000772: UniDBGrid: ...

    country-code-lookup:通过各种国家_地区代码查找国家_地区

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

    rc-apps.brazilian-zip-code-lookup:一个简单的实验性RocketChat应用程序,用于检索巴西邮政编码信息

    巴西邮政编码查找Rocket.Chat应用 ... 请随意填写,以提出改进建议并在Github存储库的“问题”下报告问题。 用法 您可以使用像这样的斜杠命令来请求rocket.cat: / cep 00000-000 甚至 / cep 0000000 ...

    node-affilinet-lookup:一个围绕affilinet SearchProducts api的简单包装器

    npm install affilinet-lookup 用法 affilinet-lookup -i &lt;publisher&gt; -p &lt;api&gt; -k 有关更多信息,请参阅affilinet-lookup --help 例子 基本的: var affilinet = require ( 'affilinet-lookup' ) ; affilinet ...

    lookup-dns-cache:通过避免线程池并为特定主机名使用DNS TTL缓存来加速nodejs`dns.lookup`方法的实现

    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-countries-lookup 使用输入的基本容错能力的多种语言按国家/地区名称查找国家/地区代码(ISO 3166-1 alpha-2)。 返回给定国家/地区名称的ISO 3166-1 alpha-2国家/地区代码。 支持基于以及基于备用名称。 安装...

    Cisco路由器命令大全-网管学习.pdf

    这份文档中包含的命令,描述了Cisco设备上操作和配置的基本方法,是学习Cisco网络管理的核心工具。它涉及的内容广泛,从基本的配置到路由协议的设置,再到网络安全和VLAN的配置,都是网络工程师必须掌握的知识点。...

Global site tag (gtag.js) - Google Analytics