天天看點

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結

  • 問題
  • lookup方法注入
    • 概述
    • 執行個體
      • 方法一 通過在配置檔案中配置的方式實作
      • 方法二 通過實作接口代碼的方式實作
    • 小結
  • 方法替換MethodReplacer接口
    • 概述
    • 執行個體
    • 小結
  • 總結

問題

無狀态Bean的作用域一般可以配置為singleton(單例模式),如果我們往singleton的Pilot類中注入prototype的Plane類,并希望每次調用Pilot的getPlane()方法都能傳回一個新的plane Bean ,該怎麼辦呢?

如果我們使用傳統的注入方式将無法實作這樣的需求, 因為Singleton的Bean注入關聯Bean的動作僅有一次,雖然 plane Bean的作用範圍是prototype,但是 Pilot通過getPlane()方法傳回的對象還是最開始注入的那個plane Bean .

如果希望每次每次調用getPlane()方法都傳回一個新的plane Bean, 一種可選的方法是讓Pilot類實作BeanFactoryAware接口,且能夠通路容器的引用。

但是上面的方法依賴SPring架構接口,十分不友好。 有沒有其他辦法呢?

通過方法注入的方案完美的解決這個問題。

lookup方法注入

概述

Spring IoC容器擁有複寫Bean方法的能力,主要源于CGLib類包。

CGlib可以找運作期間動态操作class位元組碼,為Bean動态建立子類或者實作類。

執行個體

代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結
package com.xgj.ioc.lookup;


public class Plane {

    public void fly() {
        System.out.println("ready to fly");
    }
}           

複制

方法一 通過在配置檔案中配置的方式實作

我們聲明一個MagicPilot接口,并聲明一個getPlane()接口方法

package com.xgj.ioc.lookup;

public interface MagicPilot {

    Plane getPlane();

}           

複制

下面不編寫任何實作類,僅通過配置為該接口提供動态的實作,讓getPlane接口方法每次都傳回新的plane Bean。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <bean id="plane" class="com.xgj.ioc.lookup.Plane" scope="prototype" />

    
    <bean id="magicPilot" class="com.xgj.ioc.lookup.MagicPilot">
        <lookup-method name="getPlane" bean="plane" />
    bean>
beans>           

複制

通過lookup-method元素标簽為MagicPlane的getPlane方法提供動态實作,傳回prototype類型的Plane bean , 這樣Spring将在運作期為MagicPlane接口提供動态實作。 等同于方式二 。

測試類

package com.xgj.ioc.lookup;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LookupTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/lookup/beans.xml");

        Plane plane = ctx.getBean("magicPilot", MagicPilot.class).getPlane();
        plane.fly();
        System.out.println(ctx.isPrototype("plane"));

    }
}           

複制

測試結果:

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結

方法二 通過實作接口代碼的方式實作

編寫MagicPilot的實作類,實作MagicPilot 和 ApplicationContextAware 接口

package com.xgj.ioc.lookup;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class MagicPilotImpl implements MagicPilot, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public Plane getPlane() {
        return (Plane) applicationContext.getBean("plane");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;

    }
}           

複制

修改配置檔案

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <bean id="plane" class="com.xgj.ioc.lookup.Plane" scope="prototype" />

    <bean id="magicPilotImpl" class="com.xgj.ioc.lookup.MagicPilotImpl"/>

beans>           

複制

修改測試類

package com.xgj.ioc.lookup;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LookupTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/lookup/beans.xml");

        MagicPilotImpl magicPilotImpl = ctx.getBean("magicPilotImpl",
                MagicPilotImpl.class);
        magicPilotImpl.getPlane().fly();
        System.out.println(ctx.isPrototype("plane"));
    }
}           

複制

運作結果

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結

每次調用MagicPlane的getPlane方法都會從容器中擷取plane bean, 由于plane Bean的作用域是prototype,是以每次都能傳回新的plane執行個體。

如果将plane Bean的作用域設定為預設的singleton ,雖然也可以潤興,但是這個時候lookup所提供的方法注入就沒有意義了。 因為我們可以很輕松的編寫一個magicPlane的實作類,用屬性注入的方式達到相同的目的 ,是以lookup 方法注入是有一定使用範圍的,一般在希望通過一個singleton Bean擷取一個prototype Bean時使用

小結

lookup 方法的使用場景: 一般在希望通過一個singleton Bean擷取一個prototype Bean時使用

方法替換MethodReplacer接口

概述

使用某個Bean的方法替換另外一個Bean的方法。

必須實作 org.springframework.beans.factory.support.MethodReplacer 接口,重寫reimplement方法。

執行個體

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結

POJO

package com.xgj.ioc.methodReplace;

public class Plane {

    private String brand;

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        System.out.println("brand:" + brand);
        return brand;
    }

}           

複制

POJO

package com.xgj.ioc.methodReplace;

public class PilotOne {

    public Plane getPlane() {
        Plane plane = new Plane();
        plane.setBrand("PilotOne-F22");
        return plane;
    }
}           

複制

POJO

package com.xgj.ioc.methodReplace;

import java.lang.reflect.Method;

import org.springframework.beans.factory.support.MethodReplacer;

public class PilotTwo implements MethodReplacer {

    public Object reimplement(Object obj, Method method, Object[] args)
            throws Throwable {
        Plane plane = new Plane();
        plane.setBrand("PilotTwo-F35");
        return plane;
    }
}           

複制

配置檔案

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">


    
    <bean id="pilotOne" class="com.xgj.ioc.methodReplace.PilotOne">
        <replaced-method name="getPlane" replacer="pilotTwo"/>
    bean>

    <bean id="pilotTwo" class="com.xgj.ioc.methodReplace.PilotTwo"/>

beans>           

複制

測試類

package com.xgj.ioc.methodReplace;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MethodReplacerTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/methodReplace/beans.xml");

        ctx.getBean("pilotOne", PilotOne.class).getPlane().getBrand();
    }
}           

複制

運作結果

Spring-方法注入lookup、方法替換MethodReplacer接口問題lookup方法注入方法替換MethodReplacer接口總結

傳回了 第二個Bean的 brand:PilotTwo-F35 ,可見替換成功。

小結

用于替換他人的Bean必須實作MethodReplacer接口,Spring利用該接口的方法去替換目标Bean的方法。

總結

像lookup和methodreplacer進階功能,在實際中使用的很少,而屬性注入、構造函數注入等反而在實際項目中使用的最多,我們了解即可。