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的属性中,从而实现了切面。

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