7.9.3:协调作用域不同步的Bean

文章详细介绍了在Spring容器中singleton作用域Bean依赖prototype作用域Bean时产生的不同步现象,以及使用lookup方法注入解决该问题的方案。通过在singletonBean中新增抽象方法并使用lookup配置,确保每次获取prototypeBean实例都是最新的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当两个singleton作用域Bean存在依赖关系时,或prototype作用域Bean依赖singleton作用域Bean时,不会有任何问题。

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

singleton Bean依赖于prototype Bean时,当Spring容器初始化时,它会初始化容器中所有singleton Bean,此时prototype Bean被创建出来,并注入到singleton Bean中------当singleton Bean创建完成后,它就持有了一个prototype Bean,容器再不会为singleton Bean执行注入了。

由于singleton Bean具有单例行为,当客户端多次请求singleton Bean时,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。Spring通过使用CGLIB库修改客户端的二进制码,从而实现上述的要求。

Axe.java :

public interface Axe {
	public String chop();
}
SteelAxe.java :

public class SteelAxe implements Axe {

	@Override
	public String chop() {
		return "钢斧砍柴真快";
	}

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

public interface Person {
	public void useAxe();
}
Chinese.java :

public abstract class Chinese implements Person{
	
	public Chinese() {
		System.out.println("Spring实例化主调Bean:Chinese实例...");
	}

	//singleton Bean里增加一个抽象方法
	//方法的返回值类型是被依赖的Bean
	public abstract Axe getAxe();

	@Override
	public void useAxe() {
		
		System.out.println("正在使用"+getAxe()+"砍柴!");
		System.out.println(getAxe().chop());
	}

	
}
bean.xml核心配置:

 <bean id="chinese" class="com.bean.Chinese">
    <lookup-method name="getAxe" bean="steelAxe"/>
 </bean>
  
 <bean id="steelAxe" class="com.bean.SteelAxe" scope="prototype"/>
Test.java :

public class Test {

	public static void main(String[] args) {
		
		ApplicationContext ctx=new ClassPathXmlApplicationContext("bean.xml");
		Person p=(Person) ctx.getBean("chinese");
		p.useAxe();
		p.useAxe();
	}

}
运行Test.java,控制台输出:

SteelAxe被部署成prototype作用域Bean,并被一个singleton作用域Bean所依赖。如果让Spring容器直接将prototype作用域的Bean注入singleton作用域Bean,就会出现不同步的问题。为了解决这个问题,我们在singleton Bean里新增一个抽象方法,该方法的返回值类型是被依赖的Bean,该方法是一个抽象方法,其实现由Spring完成。问题是:Spring怎么知道如何实现该方法呢?为了让Spring知道如何实现该方法,我们需要在配置文件中使用<lookup-method.../>元素来配置这个方法。

使用<lookup-method.../>元素需要指定如下两个属性:

① name:指定需要让Spring实现的方法。

② bean:指定Spring实现该方法后的返回值。

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值