1. 程式人生 > >lookup-method方式實現依賴注入

lookup-method方式實現依賴注入

引言

假設一個單例模式的bean A需要引用另外一個非單例模式的bean B,為了在我們每次引用的時候都能拿到最新的bean B,我們可以讓bean A通過實現ApplicationContextWare來感知applicationContext(即可以獲得容器上下文),從而能在執行時通過ApplicationContext.getBean(String beanName)的方法來獲取最新的bean B。但是如果用ApplicationContextAware介面,就讓我們與Spring程式碼耦合了,違背了反轉控制原則(IoC,即bean完全由Spring容器管理,我們自己的程式碼只需要用bean就可以了)。

所以Spring為我們提供了方法注入的方式來實現以上的場景。方法注入方式主要是通過<lookup-method/>標籤。

例項

下面我們用一個例子來說明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(); }
複製程式碼

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>
複製程式碼

測試程式碼:

複製程式碼
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();
}
複製程式碼

測試結果:

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不允許有引數。