歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)

點選上方“Java技術驛站”,選擇“置頂公眾號”。

有內涵、有價值的文章第一時間送達!

精品專欄

 

在上篇部落格【死磕Spring】—– IOC 之解析 Bean 標簽: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() 獲取。該子元素的解析過程如下:

  1.    public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {

  2.        NodeList nl = ele.getChildNodes();

  3.        for (int i = 0; i < nl.getLength(); i++) {

  4.            Node node = nl.item(i);

  5.            if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {

  6.                Element metaElement = (Element) node;

  7.                String key = metaElement.getAttribute(KEY_ATTRIBUTE);

  8.                String value = metaElement.getAttribute(VALUE_ATTRIBUTE);

  9.                BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);

  10.                attribute.setSource(extractSource(metaElement));

  11.                attributeAccessor.addMetadataAttribute(attribute);

  12.            }

  13.        }

  14.    }

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

  1.    public void addMetadataAttribute(BeanMetadataAttribute attribute) {

  2.        super.setAttribute(attribute.getName(), attribute);

  3.    }

委託 AttributeAccessorSupport 實現,如下:

  1.    public void setAttribute(String name, @Nullable Object value) {

  2.        Assert.notNull(name, "Name must not be null");

  3.        if (value != null) {

  4.            this.attributes.put(name, value);

  5.        }

  6.        else {

  7.            removeAttribute(name);

  8.        }

  9.    }

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

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

  1.    public Object getAttribute(String name) {

  2.        BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);

  3.        return (attribute != null ? attribute.getValue() : null);

  4.    }

lookup-method 子元素

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

直接上例子:

  1. public interface Car {

  2.    void display();

  3. }

  4. public class Bmw implements Car{

  5.    @Override

  6.    public void display() {

  7.        System.out.println("我是 BMW");

  8.    }

  9. }

  10. public class Hongqi implements Car{

  11.    @Override

  12.    public void display() {

  13.        System.out.println("我是 hongqi");

  14.    }

  15. }

  16. public abstract class Display {

  17.    public void display(){

  18.        getCar().display();

  19.    }

  20.    public abstract Car getCar();

  21. }

  22.   public static void main(String[] args) {

  23.        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

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

  25.        display.display();

  26.    }

  27. }

配置內容如下:

  1.     id="display" class="org.springframework.core.test1.Display">

  2.         name="getCar" bean="hongqi"/>

  3.    

執行結果為:

  1. 我是 hongqi

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

  1. 我是 BMW

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

  1.    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {

  2.        NodeList nl = beanEle.getChildNodes();

  3.        for (int i = 0; i < nl.getLength(); i++) {

  4.            Node node = nl.item(i);

  5.            if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {

  6.                Element ele = (Element) node;

  7.                String methodName = ele.getAttribute(NAME_ATTRIBUTE);

  8.                String beanRef = ele.getAttribute(BEAN_ELEMENT);

  9.                LookupOverride override = new LookupOverride(methodName, beanRef);

  10.                override.setSource(extractSource(ele));

  11.                overrides.addOverride(override);

  12.            }

  13.        }

  14.    }

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

replace-method 子元素

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

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

  1. public class Method {

  2.    public void display(){

  3.        System.out.println("我是原始方法");

  4.    }

  5. }

  6. public class MethodReplace implements MethodReplacer {

  7.    @Override

  8.    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {

  9.        System.out.println("我是替換方法");

  10.        return null;

  11.    }

  12. }

  13.    public static void main(String[] args) {

  14.        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

  15.        Method method = (Method) context.getBean("method");

  16.        method.display();

  17.    }

如果 spring.xml 檔案如下:

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

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

則執行結果為:

  1. 我是原始方法

增加 replaced-method 子元素:

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

  2.     id="method" class="org.springframework.core.test1.Method">

  3.         name="display" replacer="methodReplace"/>

  4.    

執行結果為:

  1. 我是替換方法

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

  1.    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {

  2.        NodeList nl = beanEle.getChildNodes();

  3.        for (int i = 0; i < nl.getLength(); i++) {

  4.            Node node = nl.item(i);

  5.            if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {

  6.                Element replacedMethodEle = (Element) node;

  7.                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);

  8.                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);

  9.                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);

  10.                // Look for arg-type match elements.

  11.                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);

  12.                for (Element argTypeEle : argTypeEles) {

  13.                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);

  14.                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));

  15.                    if (StringUtils.hasText(match)) {

  16.                        replaceOverride.addTypeIdentifier(match);

  17.                    }

  18.                }

  19.                replaceOverride.setSource(extractSource(replacedMethodEle));

  20.                overrides.addOverride(replaceOverride);

  21.            }

  22.        }

  23.    }

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

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

【死磕 Spring】----- IOC 之解析 bean 標簽:開啟解析行程

【死磕 Spring】----- IOC 之解析 bean 標簽:BeanDefinition

【死磕 Spring】----- IOC 之 獲取 Document 物件

【死磕 Spring】----- IOC 之 註冊 BeanDefinition

【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 Spring 統一資源載入策略

【死磕 Spring】----- IOC 之深入理解 Spring IoC

END

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖