dubbo系列(三) DUBBO IOC、AOP

1. 介紹

  在dubbo系列前面的章節中,我們可以創建指定的擴展點對象,那如果擴展點中包含另一個擴展點屬性,屬性是如何創建的,如果我們期望在擴展點目標方法前後增加切面,dubbo又是如何處理的,本章將介紹這些內容。

2.Dubbo IOC示例

  業務場景說明:bird有一個Animal屬性,我們在運行時會通過Url總線設置這個屬性的類型爲cat,然後通過bird調用cat的speak方法。
  修改擴展點接口,新增加一個speak(Url url)方法,並且標註自適應註解

@SPI
public interface Animal {
    public void speak();

    @Adaptive("friend")
    public void speak(URL url);
}

  對應實現類:

public class Cat implements Animal{
    @Override
    public void speak() {
        System.out.println("I'm a cat");
    }

    @Override
    public void speak(URL url) {
        System.out.println("I'm cat");
    }
}
public class Bird implements Animal{

    private Animal friend;

    public void setFriend(Animal friend) {
        this.friend = friend;
    }

    @Override
    public void speak() {
        System.out.println("I'm a bird");
      }

    @Override
    public void speak(URL url) {
        friend.speak(url);
    }

}

  測試類:

public static void main(String[] args) {
    ExtensionLoader<Animal> extensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
    Animal animal = extensionLoader.getExtension("bird");

    Map<String,String> map = new HashMap<>();
    map.put("friend","cat");
    URL url = new URL("","",1,map);

    animal.speak(url);
}

執行結果:I’m cat

3.Dubbo IOC原理

  熟悉spring的同學應該清楚,在spring的ioc中,例如A中有B屬性,那麼要注入B,會通過xml或註解指定B的引用,然後在A實例化時,會通過指定的引用實例化B。但是在上面的例子中,在我們創建bird擴展點時,並沒有指定friend屬性的類型,而是在運行speak方法時,通過url參數指定了friend的類型,這個動作似乎和上一篇文章介紹的自適應擴展點很相似,沒錯,dubbo的ioc就是通過自適應擴展點來實現的,在創建bird時,他的friend是一個自適應擴展點,然後在運行擴展方法時,才知道具體類型。

4. Dubbo IOC源碼分析

  Dubbo IOC 是通過 setter 方法注入依賴。Dubbo 首先會通過反射獲取到實例的所有方法,然後再遍歷方法列表,檢測方法名是否具有 setter 方法特徵。若有,則通過 ObjectFactory 獲取依賴對象,最後通過反射調用 setter 方法將依賴設置到目標對象中。整個過程對應的代碼如下:

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            // 遍歷目標類的所有方法
            for (Method method : instance.getClass().getMethods()) {
                // 檢測方法是否以 set 開頭,且方法僅有一個參數,且方法訪問級別爲 public
                if (method.getName().startsWith("set")
                    && method.getParameterTypes().length == 1
                    && Modifier.isPublic(method.getModifiers())) {
                    // 獲取 setter 方法參數類型
                    Class<?> pt = method.getParameterTypes()[0];
                    try {
                        // 獲取屬性名,比如 setName 方法對應屬性名 name
                        String property = method.getName().length() > 3 ? 
                            method.getName().substring(3, 4).toLowerCase() + 
                            	method.getName().substring(4) : "";
                        // 從 ObjectFactory 中獲取依賴對象
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            // 通過反射調用 setter 方法設置依賴
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method...");
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

  在上面代碼中,objectFactory 變量的類型爲 AdaptiveExtensionFactory,AdaptiveExtensionFactory 內部維護了一個 ExtensionFactory 列表,用於存儲其他類型的 ExtensionFactory。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
       ...省略
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }
}

  在上面的getExtension方法中,factories表示當前支持的所有factory擴展點,具體包括SpiExtensionFactoryhe SpringExtensionFactory,當前的場景時從SpiExtensionFactoryhe獲取擴展點,代碼如下:

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

  又看到了熟悉的loader.getAdaptiveExtension(),至此真相大白。

2.Dubbo AOP示例

  如果我們期望在speak前後增加切面動作,那麼代碼改動如下:

public class AnimalLogWrapper implements Animal {

    private Animal animal;

    public AnimalLogWrapper(Animal animal){
        this.animal = animal;
    }

    @Override
    public void speak() {
        System.out.println("before speak");
        animal.speak();
        System.out.println("after speak");
    }

    @Override
    public void speak(URL url) {

    }
}

  新建一個wrapper類,實現Animal接口,並且包含一個Animal屬性,且擁有帶參構造方法。

bird=com.hy.study.spi.Bird
cat=com.hy.study.spi.Cat
dog=com.hy.study.spi.Dog
com.hy.study.spi.AnimalLogWrapper

  把當前的wrapper類寫入配置文件

 public static void main(String[] args) {
        ExtensionLoader<Animal> extensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
        Animal animal = extensionLoader.getExtension("bird");
        animal.speak();
    }

  運行結果:
  before speak
  I’m a bird
  after speak

3.Dubbo AOP源碼分析

 private T createExtension(String name) {
       ...省略
       Set<Class<?>> wrapperClasses = cachedWrapperClasses;
       if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
           for (Class<?> wrapperClass : wrapperClasses) {
               instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
           }
       }
       return instance;
       ...省略
    }

  cachedWrapperClasses是從配置文件中獲取的wrapper對象,可以包含多個wrapper對象,然後把當前bird擴展點的實現注入到wrapper的屬性中,從而實現了切面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章