- 用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。(在方法或者抽象方法上使用@Lookup注解,将会根据该方法的返回值,自动在BeanFactory中调用getBean()来注入该Bean)
语法
<bean class="beanClass">
<lookup-method name="method" bean="non-singleton-bean"/>
</bean>
参数 |
描述 |
name |
method是beanClass中的一个方法,可以不是抽象方法 |
bean |
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 |
不允许有参数。 |
- 假设一个单例模式的 bean A 需要引用另外一个非单例模式的 bean B
- 为了在每次引用的时候都能拿到最新的 bean B,可以让 bean A 通过实现 ApplicationContextWare 来感知 applicationContext(即可以获得容器上下文),从而能在运行时通过 ApplicationContext.getBean(String beanName) 的方法来获取最新的 bean B。
- 但是如果用 ApplicationContextAware 接口,就与 Spring 代码耦合了,违背了控制反转使用 bean 完全由 Spring 容器管理
- 所以 Spring 提供了方法注入的方式来实现以上的场景。方法注入方式主要是通过 <lookup-method/> 标签。
- 假设有一个果盘,果盘里放了一些水果,比如苹果,香蕉等,我们希望我们每次在果盘里拿到的都是最新鲜的水果。
实例
- Java
// 定义一个水果类
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();
}
- applicationCnotext.xml
<!-- 这是2个非单例模式的bean --> <beans> <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="fruitPlate" class="cn.com.willchen.test.di.FruitPlate"> <lookup-method name="getFruit" bean="apple" /> </bean>
<!-- 拿香蕉 --> <bean id="fruitPlate" class="cn.com.willchen.test.di.FruitPlate"> <lookup-method name="getFruit" bean="bananer" /> </bean> <beans> |
- Test.java
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
FruitPlate fp1= (FruitPlate) app.getBean("fruitPlate1");
FruitPlate fp2 = (FruitPlate) app.getBean("fruitPlate2");
fp1.getFruit();
fp2.getFruit();
}
运行结果:
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 生成的动态代理类的方法,自动实现抽象类中的方法