如果一个singleton Bean 引用了一个 prototype Bean(或者说把一个prototype Bean注入到singleton Bean中),那么就会产生一个问题:
singleton Bean在被预初始化后,接下来singleton Bean中的singleton Bean都是同一个对象,也 就意味着singleton Bean中的 prototype Bean一直都是最开始的那一个。那么就违背了prototype Bean的设计初衷(既然把它设计成prototype 作用域,肯定是想每次用到的bean都是一个全新的bean对象)
解决这个问题有两种方案:
方案一: 放弃一依赖注入,改用在singleton Bean中手动获取prototype Bean对象,并且每次获取Bean对象都要返回全新的Bean。
栗子:
public class Person implements ApplicationContextAware
{
private ApplicationContext ctx;
public Dog getDog()
{
return ctx.getBean("dog",Dog.class);
}
public void hunt()
{
//每次都得到一个全新的狗
System.out.println("带着" + getDog() + "去打猎!");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
}
public class GunDog implements Dog
{
private String name;
//省略getter和setter方法
}
<bean id="person" class="com.lyx.service.Person" />
<bean id="dog" class="com.lyx.service.GunDog" scope="prototype">
<property name="name" value="猎狗旺财"/>
</bean>
public class DriverJava
{
public static void main(String[] args)
{
var ctx = new ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("person",Person.class);
Person p2 = ctx.getBean("person",Person.class);
p.hunt();//带着com.lyx.service.GunDog@2ccca26f去打猎!
p2.hunt();//带着com.lyx.service.GunDog@66b7550d去打猎!
}
}
方案二:
上面的过程有些复杂,Spirng用lookup-method标签大大简化了代码
此时用于注入bean的方法(本例中的:Dog getDog())直接写成抽象方法,其他的交割Spirng来完成,当然了需要指定注入的bean,用lookup-method:
lookup-method中有两个属性
name: 指定注入bean的方法的方法名
bean: 指定注入的bean对象
栗子:
public abstract class Person
{
private ApplicationContext ctx;
public abstract Dog getDog();
public void hunt()
{
//每次都得到一个全新的狗
System.out.println("带着" + getDog() + "去打猎!");
}
}
public class GunDog implements Dog
{
private String name;
//省略getter和setter方法
}
<bean id="person" class="com.lyx.service.Person" >
<lookup-method name="getDog" bean="dog"/>
</bean>
<bean id="dog" class="com.lyx.service.GunDog" scope="prototype">
<property name="name" value="猎狗旺财"/>
</bean>
第二种方案明显简便很多,而且避免的代码污染。
其实方案二原理和方案一类似,只不过这些繁琐的操作Spirng在底层利用动态代理帮我们完成了。