当Spring容器中做用域不一样的Bean相互依赖时,可能出现一些问题,例如:一个做用域为Singleton的Bean(设为A)依赖于一个做用域为prototype的Bean(设为B)。因为A是单例的,只有一次初始化的机会,它的依赖关系也只在初始化阶段被设置,但它所依赖的B每次都会建立一个全新的实例,这将使A中的B不能及时获得更新。这样将致使若是客户端屡次请求A,并调用A中B的某个方法(或获取A中B的某个属性),服务端老是返回同一个B,但客户端直接请求B却能得到最新的对象,这就产生了对象不一样步的状况。这样就违背了B初衷:原本但愿B具备prototype的行为,可是却表现出singleton的行为了。那么,问题如何解决呢?
java
办法有二:
app
部分放弃依赖注入:当A每次须要B时,主动向容器请求新的Bean实例,便可保证每次注入的B都是最新的实例。框架
利用方法注入。ide
第一种方式显然不是一个好的作法,代码主动请求Bean实例,必然致使代码与SpringAPI耦合在一块儿,形成严重的代码污染。一般状况下,咱们会采用第二种作法。使用方法注入。
测试
方法注入一般使用lookup方法注入,利用lookup方法注入可让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其余Bean的结果,被查找的Bean一般是一个non-singleton的Bean(尽管也能够是一个singleton的Bean)。Spring经过使用CGLIB库修改客户端的二进制码,从而实现上述要求。看下面的例子:spa
public class CellPhone implements Phone { public CellPhone() { System.out.println("Spring实例化依赖的Bean...CellPhone实例"); } public return call() { return "正在打电话..."; } }
上面的CellPhone将被部署成prototype的Bean,并被一个singleton的Bean所依赖。若是让Spring容器直接将prototype的Bean注入到singleton中,就会出现上面的问题。为了解决这个问题,咱们在singleton的Bean里增长一个抽象方法,该方法的返回类型是一个被依赖的Bean——注意这个方法是一个抽象方法,由于程序中没有为该方法提供实现,这个实现过程由Spring完成。下面是该singleton做用域的Bean的代码:
prototype
public abstract class Developer implements Person { public Developer() { System.out.println("Spring实例化主调的Bean...Developer实例"); } //定义一个抽象方法,该方法将由Spring实现 public abstract Phone getPhone(); @Override public void call() { System.out.println("正在使用 " + getPhone() + " 打电话"); System.out.println(getPhone().call()); } }
上面的代码定义了一个抽象的getPhone方法,一般状况下,程序不能调用这个方法,但Spring框架将会负责为该方法提供是先提,这样这个方法就会变成具体方法了,程序也就能够调用该方法了。为了让Spring知道如何实现该方法,咱们须要在配置文件中使用<lookup-method>标签,这个标签须要指定以下两个属性:code
name:指定须要让Spring实现的方法xml
bean:指定Spring实现该方法后返回的值对象
下面是配置片断:
<!-- 将CellPhone部署成prototype的范围 --> <bean id="cellPhone" class="com.abc.CellPhone" scope="prototype" /> <bean id="developer" class="com.abc.Developer"> <!-- getPhone方法返回CellPhone,每次调用将获取新的CellPhone --> <lookup-method name="getPhone" bean="cellPhone" /> </bean>
上面配置的<lookup-method>指定Spring将负责实现getPhone方法,该方法将返回容器中的prototype类型的cellPhone实例。下面是测试类:
public class Test { public static void main(String args[]) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Developer d = context.getBean("developer", Developer.class); d.call(); d.call(); } }
执行结果以下:
Spring实例化主调的Bean...Developer实例 Spring实例化依赖的Bean...CellPhone实例 正在使用 com.abc.CellPhone@3e12ad 打电话 Spring实例化依赖的Bean...CellPhone实例 正在打电话... Spring实例化依赖的Bean...CellPhone实例 正在使用 com.abc.CellPhone@41af2e 打电话 Spring实例化依赖的Bean...CellPhone实例 正在打电话...
结果代表:当lookup方法注入后,系统每次调用getPhone都会返回最新的CellPhone实例而非最先的CellPhone实例。
注意:要保证lookup方法注入每次产生的Bean实例,必须将目标Bean(本例为cellPhone)布署成prototype做用域。不然,若是容器中只有一个目标Bean实例,即便采用lookup方法注入,每次依然返回同一个Bean实例。另外,lookup方法注入不只能用于设值注入,还能用于构造注入。