协调作用域不同步的Bean

当Spring容器中作用域不同的Bean相互依赖时,可能出现一些问题:当两个singleton作用域Bean存在依赖关系时,或当prototype作用域Bean依赖singleton作用域Bean时,通过属性定义依赖关系就足够了。


但当singleton作用域Bean依赖prototype作用域Bean时,singleton作用域Bean只有一次初始化的机会,它的依赖关系也只在初始化阶段被设置,它所依赖的prototype作用域Bean则需要每次都得到一个全新的Bean实例,这将导致singleton作用域的Bean的依赖得不到即时更新。


例如客户端程序多次请求singleton时,由于singleton具有单例行为,Spring返回给客户端的将是同一个singleton Bean实例,这不存在任何问题。但如果是客户端多次请求singleton Bean、并调用该singleton Bean去调用prototype Bean的方法时,始终都是调用同一个prototype Bean实例,这就违背了设置prototype Bean 的初衷:本来希望它具有prototype行为,但实际上它却表现出singleton行为了。


产生的问题:当singleton作用域Bean依赖于prototype作用域Bean时,会产生不同步的现象。

解决思路:> 部分放弃依赖注入:singleton作用域Bean每次需要prototype作用域Bean时,主动向容器请求新的Bean实例,即可保证每次注入的prototype Bean实例都是最新的实例。

                  > 利用方法注入

第一种方式显然不是一个好的做法,代码主动请求新的Bean实例,必然导致代码与Spring API 耦合,造成严重代码污染。

通常情况下,我们采用第二种做法,使用方法注入。


方法注入通常使用lookup方法注入,利用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个non-singleton Bean(尽管也可以是一个singleton的)。Spring通过使用CGLIB库修改客户端的二进制码,从而实现上述的要求。


package demo;

public class ScopeSteelAxe implements Axe {

	public ScopeSteelAxe() {
		System.out.println("Spring实例化依赖Bean:SteelAxe实例。");
	}

	@Override
	public String chop() {
		// TODO Auto-generated method stub
		return "钢斧砍柴真快";
	}
}
上面的ScopeSteelAxe将被部署成prototype作用域Bean,并被一个singleton作用域Bean所依赖。如果让Spring容器直接将prototype作用域的Bean注入singleton作用域Bean,就会出现前面描述的问题。为了解决这个问题,我们在singleton Bean 里新增一个抽象方法,该方法的返回值类型是被依赖的Bean——注意该方法时一个抽象方法,程序员没有为该方法提供实现,该方法的实现由Spring完成。下面为该Bean的代码:
package demo;

public abstract class ScopeChinese implements Person {

	public ScopeChinese() {
		System.out.println("Spring实例化主调bean:Chinese实例.");
	}

	//定义一个抽象方法,该方法将由Spring负责实现
	public abstract Axe getAxe();
	
	@Override
	public void useAxe() {
		System.out.println("正在使用"+getAxe()+"砍柴!");
		System.out.println(getAxe().chop());
	}
}
上面的程序中定义了一个抽象的getAxe()方法,通常情况下,程序不能调用这个抽象方法。但Spring框架将会负责为该方法提供实现体,这样这个方法就会变成具体方法了,程序也就可以调用该方法了。

为了让Spring知道如何实现该方法,我们需要再配置文件中使用<lookup=method.../>元素来配置这个方法。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.springframework.org/schema/beans"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"
>
	<!-- 协调作用域不同步的Bean -->
	<bean id="steelAxe" class="demo.ScopeSteelAxe" scope="prototype"/>
	<bean id="chinese" class="demo.ScopeChinese">
		<!-- 指定getAxe方法返回steelAxe,每次调用getAxe方法都将获取新的steelAxe对象 -->
		<lookup-method name="getAxe" bean="steelAxe"/>
	</bean>
</beans>

package demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScopeTest {

	public static void main(String[] args) throws Exception {
		
		ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");

		Person p = ctx.getBean("chinese",Person.class);
		//两次通过p对象使用Axe对象
		p.useAxe();
		p.useAxe();
	}
}


执行结果表明:使用lookup方法注入后,系统每次调用getAxe()方法都将生成一个新的SteelAxe实例,这就可以保证当singleton作用域的Bean需要全新的Bean实例时,直接调用getAxe()方法既可,从而可避免一直使用最早注入的Bean实例。


PS:要保证lookup方法注入每次产生新的Bean实例,必须将目标Bean(上例是ScopeSteelAxe)部署成prototype作用域。否则,如果容器中只有一个目标Bean实例,即使采用lookup方法注入,每次依然返回同一个Bean实例。

lookup方法注入不仅能用于设置注入,也可用于构造注入。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值