1. 程式人生 > >spring深入學習(八)IOC 之解析 bean 標籤:meta、lookup-method、replace-method

spring深入學習(八)IOC 之解析 bean 標籤:meta、lookup-method、replace-method

BeanDefinition中已經完成了對 Bean 標籤屬性的解析工作,這篇博文開始分析子元素的解析。完成 Bean 標籤基本屬性解析後,會依次呼叫 parseMetaElements()parseLookupOverrideSubElements()parseReplacedMethodSubElements() 對子元素 meta、lookup-method、replace-method 完成解析。三個子元素的作用如下:

 

圖示:

 

  • meta:元資料。
  • lookup-method:Spring 動態改變 bean 裡方法的實現。方法執行返回的物件,使用 Spring 內原有的這類物件替換,通過改變方法返回值來動態改變方法。內部實現為使用 cglib 方法,重新生成子類,重寫配置的方法和返回物件,達到動態改變的效果。
  • replace-method:Spring 動態改變 bean 裡方法的實現。需要改變的方法,使用 Spring 內原有其他類(需要繼承介面org.springframework.beans.factory.support.MethodReplacer)的邏輯,替換這個方法。通過改變方法執行邏輯來動態改變方法。

meta 子元素

meta :元資料。當需要使用裡面的資訊時可以通過key獲取

meta 所宣告的 key 並不會在 Bean 中體現,只是一個額外的宣告,當我們需要使用裡面的資訊時,通過 BeanDefinition 的 getAttribute()

 獲取。該子元素的解析過程如下:

    public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
        NodeList nl = ele.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
                Element metaElement = (Element) node;
                String key = metaElement.getAttribute(KEY_ATTRIBUTE);
                String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
                BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
                attribute.setSource(extractSource(metaElement));
                attributeAccessor.addMetadataAttribute(attribute);
            }
        }
    }

解析過程較為簡單,獲取相應的 key – value 構建 BeanMetadataAttribute 物件,然後通過 addMetadataAttribute()加入到 AbstractBeanDefinition 中。 “ 如下:

    public void addMetadataAttribute(BeanMetadataAttribute attribute) {
        super.setAttribute(attribute.getName(), attribute);
    }

委託 AttributeAccessorSupport 實現,如下:

    public void setAttribute(String name, @Nullable Object value) {
        Assert.notNull(name, "Name must not be null");
        if (value != null) {
            this.attributes.put(name, value);
        }
        else {
            removeAttribute(name);
        }
    }

AttributeAccessorSupport 是介面 AttributeAccessor 的實現者。 AttributeAccessor 介面定義了與其他物件的元資料進行連線和訪問的約定,可以通過該介面對屬性進行獲取、設定、刪除操作。

設定元資料後,則可以通過 getAttribute() 獲取,如下:

    public Object getAttribute(String name) {
        BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
        return (attribute != null ? attribute.getValue() : null);
    }

lookup-method 子元素

lookup-method :獲取器注入,是把一個方法宣告為返回某種型別的 bean 但實際要返回的 bean 是在配置檔案裡面配置的。該方法可以用於設計一些可插拔的功能上,解除程式依賴。

直接上例子:

public interface Car {

    void display();
}

public class Bmw implements Car{
    @Override
    public void display() {
        System.out.println("我是 BMW");
    }
}

public class Hongqi implements Car{
    @Override
    public void display() {
        System.out.println("我是 hongqi");
    }
}

public abstract class Display {


    public void display(){
        getCar().display();
    }

    public abstract Car getCar();
}

   public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

        Display display = (Display) context.getBean("display");
        display.display();
    }
}

配置內容如下:

    <bean id="display" class="org.springframework.core.test1.Display">
        <lookup-method name="getCar" bean="hongqi"/>
    </bean>

執行結果為:

我是 hongqi

如果將 bean="hognqi" 替換為 bean="bmw",則執行結果變成:

我是 BMW

看了這個示例,我們初步瞭解了 looku-method 子元素提供的功能了,其解析過程如下:

    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
                Element ele = (Element) node;
                String methodName = ele.getAttribute(NAME_ATTRIBUTE);
                String beanRef = ele.getAttribute(BEAN_ELEMENT);
                LookupOverride override = new LookupOverride(methodName, beanRef);
                override.setSource(extractSource(ele));
                overrides.addOverride(override);
            }
        }
    }

解析過程和 meta 子元素沒有多大區別,同樣是解析 methodName、beanRef 構造一個 LookupOverride 物件,然後覆蓋即可。在例項化 Bean 的時候,再詳細闡述具體的實現過程,這裡僅僅只是一個標記作用。

replace-method 子元素

replaced-method :可以在執行時呼叫新的方法替換現有的方法,還能動態的更新原有方法的邏輯

該標籤使用方法和 lookup-method 標籤差不多,只不過替代方法的類需要實現 MethodReplacer 介面。如下:

public class Method {
    public void display(){
        System.out.println("我是原始方法");
    }
}

public class MethodReplace implements MethodReplacer {

    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("我是替換方法");

        return null;
    }
}

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

        Method method = (Method) context.getBean("method");
        method.display();
    }

如果 spring.xml 檔案如下:

    <bean id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>

    <bean id="method" class="org.springframework.core.test1.Method"/>

則執行結果為:

我是原始方法

增加 replaced-method 子元素:

    <bean id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>

    <bean id="method" class="org.springframework.core.test1.Method">
        <replaced-method name="display" replacer="methodReplace"/>
    </bean>

執行結果為:

我是替換方法

上面程式碼演示了 replaced-method 子元素的用法,下面再看看該子元素的解析過程。

    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
                Element replacedMethodEle = (Element) node;
                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // Look for arg-type match elements.
                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
                for (Element argTypeEle : argTypeEles) {
                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                overrides.addOverride(replaceOverride);
            }
        }
    }

該子元素和 lookup-method 資源的解析過程差不多,同樣是提取 name 和 replacer 屬性構建 ReplaceOverride 物件,然後記錄到 AbstractBeanDefinition 中的 methodOverrides 屬性中。

對於 lookup-method 和 replaced-method 兩個子元素是如何使用以完成他們所提供的功能,在後續例項化 Bean 的時候會做詳細說明