引言java
假设一个单例模式的bean A须要引用另一个非单例模式的bean B,为了在咱们每次引用的时候都能拿到最新的bean B,咱们可让bean A经过实现ApplicationContextWare来感知applicationContext(便可以得到容器上下文),从而能在运行时经过ApplicationContext.getBean(String beanName)的方法来获取最新的bean B。可是若是用ApplicationContextAware接口,就让咱们与Spring代码耦合了,违背了反转控制原则(IoC,即bean彻底由Spring容器管理,咱们本身的代码只须要用bean就能够了)。spring
因此Spring为咱们提供了方法注入的方式来实现以上的场景。方法注入方式主要是经过<lookup-method/>标签。app
实例测试
下面咱们用一个例子来讲明lookup-method的用法。ui
假设有一个果盘,果盘里放了一些水果,好比苹果,香蕉等,咱们但愿咱们每次在果盘里拿到的都是最新鲜的水果。spa
java代码:prototype
// 定义一个水果类
public class Fruit { public Fruit() { System.out.println("I got Fruit"); } } // 苹果
public class Apple extends Fruit { public Apple() { System.out.println("I got a fresh apple"); } } // 香蕉
public class Bananer extends Fruit { public Bananer () { System.out.println("I got a fresh bananer"); } } // 水果盘,能够拿到水果
public abstract class FruitPlate{ // 抽象方法获取新鲜水果
protected abstract Fruit getFruit(); }
spring配置:代理
<!-- 这是2个非单例模式的bean -->
<bean id="apple" class="cn.com.willchen.test.di.Apple" scope="prototype"/>
<bean id="bananer" class="cn.com.willchen.test.di.Bananer " scope="prototype"/>
<bean id="fruitPlate1" class="cn.com.willchen.test.di.FruitPlate">
<lookup-method name="getFruit" bean="apple"/>
</bean>
<bean id="fruitPlate2" class="cn.com.willchen.test.di.FruitPlate">
<lookup-method name="getFruit" bean="bananer"/>
</bean>
测试代码:code
public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("classpath:resource/applicationContext.xml"); FruitPlate fp1= (FruitPlate)app.getBean("fruitPlate1"); FruitPlate fp2 = (FruitPlate)app.getBean("fruitPlate2"); fp1.getFruit(); fp2.getFruit(); }
测试结果:xml
I got Fruit
I got a fresh apple
I got Fruit
I got a fresh bananer
示例说明:
从上面例子咱们能够看到,在代码中,咱们没有用到Spring的任何类和接口,实现了与Spring代码的耦合。
其中,最为核心的部分就是lookup-method的配置和FruitPlate.getFruit()方法。上面代码中,咱们能够看到getFruit()方法是个抽象方法,咱们并无实现它啊,那它是怎么拿到水果的呢。这里的奥妙就是Srping应用了CGLIB(动态代理)类库。Spring在初始化容器的时候对配置<lookup-method/>的bean作了特殊处理,Spring会对bean指定的class作动态代理,代理<lookup-method/>标签中name属性所指定的方法,返回bean属性指定的bean实例对象。每次咱们调用fruitPlate1或者fruitPlate2这2个bean的getFruit()方法时,实际上是调用了CGLIB生成的动态代理类的方法。关于CGLIB你们可自行在网上查阅。
lookup-method实现方式说明:
<bean class="beanClass"> <lookup-method name="method" bean="non-singleton-bean"/> </bean>
method是beanClass中的一个方法,beanClass和method是否是抽象都无所谓,不会影响CGLIB的动态代理,根据项目实际需求去定义。non-singleton-bean指的是lookup-method中bean属性指向的必须是一个非单例模式的bean,固然若是不是也不会报错,只是每次获得的都是相同引用的bean(同一个实例),这样用lookup-method就没有意义了。
另外对于method在代码中的签名有下面的标准:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
public|protected要求方法必须是能够被子类重写和调用的;
abstract可选,若是是抽象方法,CGLIB的动态代理类就会实现这个方法,若是不是抽象方法,就会覆盖这个方法,因此没什么影响;
return-type就是non-singleton-bean的类型咯,固然能够是它的父类或者接口。
no-arguments不容许有参数。