在Spring容器中,最常见的为singleton与prototype作用域的Bean,当我们多次获取singleton作用域的Bean时,得到的都是同一个实例,而prototype作用域里则每次都产生一个新的Bean实例。我们知道,Spring容器在初始化某个Bean前,先会创建被依赖的Bean,然后再对该Bean进行初始化,如果此时为singleton作用域的Bean依赖prototype作用域的Bean,由于singleton作用域的Bean只初始化一次,故prototype作用域的Bean也只被注入一次,之后我们每次通过singleton Bean去访问prototype Bean时,得到的永远是最初的那个prototype Bean,也可以理解为singleton Bean把它所依赖的prototype Bean变成了singleton,这就违背了我们设置prototype Bean的初衷。
有以下两种解决思路:
不使用依赖注入,当singleton 作用域的Bean每次需要prototype作用域的Bean是,主动向容器请求新的Bean实例。使用lookup方法注入不建议使用第一种方法,因为此做法会导致程序代码与Spring API耦合,造成代码污染。使用lookup方法注入可以让Spring容器重写容器中调用者Bean的抽象或具体方法,返回查找容器中其他Bean的结果。为了使用lookup方法注入,大致需要如下两步
将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean在<bean.../>元素中添加<lookup-method.../>子元素让Spring为调用者Bean的实现类实现指定的抽象方法以下给出demo:
beans.xml部分代码
<bean id="chinese" class="spring实例.Chinese" > <!--name:指定需要让Spring实现的方法;bean:指定该方法的返回值--> <!-- Spring只要检测到lookup-method元素,会自动为该元素的name属性所指定的方法提供实现体 --> <!--实现getDog()方法,返回id="dog"的Bean--> <lookup-method name="getDog" bean="dog"/> </bean> <!--设置作用域为prototype的Bean--> <bean id="dog" class="spring实例.Dog" scope="prototype"> <property name="name" value="柯基"></property> </bean>定义调用者Bean的实现类Chinese类为抽象类,并定义抽象方法getDog()
public abstract class Chinese implements Person { private Dog dog; public abstract Dog getDog(); public void hunt(){ System.out.println("我带着ID为:"+getDog()+"出去打猎"); getDog().run(); } }Dog类
public class Dog { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void run(){ System.out.println("名为["+getName()+"]的小狗不要命地跑!"); } }主程序部分代码
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Chinese c=ctx.getBean("chinese", Chinese.class); c.hunt(); c.hunt();运行结果 我带着ID为:spring实例.Dog@1b279c9出去打猎 名为[柯基]的小狗不要命地跑! 我带着ID为:spring实例.Dog@d282e0出去打猎 名为[柯基]的小狗不要命地跑!
运行结果显示,每次通过Chinese实例调用getDog()方法,都会返回新的Dog实例。这里补充一下,实质上Spring实现方法的逻辑是固定的,如下代码:
public Dog getDog(){ //获取Spring容器ctx ... //返回值 return ctx.getBean("Dog"); }