Dubbo源码篇08---依赖注入和AOP在Dubbo中的实现

  • 引言
  • 依赖注入
  • 使用实践
  • Wrapper机制
  • 使用实践
  • 注意



引言

前面三篇文章,我们从使用到原理,详细分析了一遍Dubbo SPI机制的实现原理

有了前面的铺垫,本文理解起来将会十分的轻松,对于依赖注入,我们首先想到的就是Spring中的@Autowired和@Resource注解,而AOP功能,则会首先联想到@Aspect注解。

对于Dubbo而言,其采用的是微核心+插件式架构,通常微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。考虑 Dubbo 的适用面,不想强依赖 Spring 等 IoC 容器,自已造一个小的 IoC 容器,也觉得有点过度设计,所以采用最简单的 Factory 方式管理插件。

所以对于Dubbo而言,其依赖注入和AOP也都是在其内部IOC基础上实现的,实现相比于Spring而言简单许多,所以废话不多说,我们直接开始Dubbo 依赖注入和AOP实现原理研究。

本文以普通扩展类的加载为总线,从使用层面验证之前原理篇中分析过的,关于依赖注入和Wrapper机制的代码。


依赖注入

我们先来简单回顾一下依赖注入部分的源代码:

createExtension方法是创建普通扩展类的核心方法:

dubbo中spi也增加了AOP dubbo aop_java-zookeeper


injectExtension依赖注入的核心代码如下所示:

private T injectExtension(T instance) {
    // 这里的扩展注入器不为空,在ExtensionLoader创建时会获取ExtensionInjector的自适应扩展类
    // 这里的injector即是ExtensionInjector扩展接口的的自适应扩展类AdaptiveExtensionInjector
    // 如果为空则直接返回当前实例对象,不进行依赖注入
        if (injector == null) {
            return instance;
        }
        try {
             // 遍历所有方法 --- 只包括本类和父类的public方法
            for (Method method : instance.getClass().getMethods()) {
                // 如果当前方法不是一个setXXXX()方法则继续处理下一个方法
                // public + set开头 + 只有一个参数
                if (!isSetter(method)) {
                    continue;
                }
                //校验当前方法上携带了@DisableInject注解吗,即禁止注入的当前属性,符合则跳过
                if (method.isAnnotationPresent(DisableInject.class)) {
                    continue;
                }
                // 第一个参数是原生类型(String、Boolean、Integer ...) 跳过
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }
                try {
                    // 获取set方法对应的成员变量如setProtocol 属性为protocol
                    String property = getSetterProperty(method);
                    // 根据参数类型如Protocol和属性名字如protocol获取应该注入的对象
                    Object object = injector.getInstance(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                }...
            }
        }...
        return instance;
    }

扩展依赖注入默认情况下为AdaptiveExtensionInjector:

dubbo中spi也增加了AOP dubbo aop_dubbo_02


AdaptiveExtensionInjector作为默认的扩展依赖注入自适应扩展点,当其被初始化时,会通过getExtensionLoader方法拿到ExtensionInjector扩展类型的所有扩展实现:

dubbo中spi也增加了AOP dubbo aop_java-zookeeper_03

@Override
    public void initialize() throws IllegalStateException {
        ExtensionLoader<ExtensionInjector> loader = extensionAccessor.getExtensionLoader(ExtensionInjector.class);
        List<ExtensionInjector> list = new ArrayList<ExtensionInjector>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        injectors = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getInstance(Class<T> type, String name) {
      // 遍历所有的扩展注入器并调用getinstance()方法,并取第一个返回
        for (ExtensionInjector injector : injectors) {
            T extension = injector.getInstance(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

ExtensionInjector是 Dubbo 源码中为数不多的自适应扩展实现实例,ExtensionInjector扩展接口的自适应扩展实现就是AdaptiveExtensionInjector,接口实现类默认有三个

  • SpiExtensionInjector: 根据实例 class 从 ExtensionLoader 中获取实例
  • ScopeBeanExtensionInjector: 从 Dubbo 自定义的beanfactory中获取实例
  • SpringExtenisonInjector: 从 Spring 的beanfactory中获取实例

这个AdaptiveExtensionInjector在初始化的时候会获取所有的ExtensionInjector的扩展,非自适应的,它本身是自适应的扩展。

dubbo中spi也增加了AOP dubbo aop_dubbo中spi也增加了AOP_04


使用实践

测试环境:

@SPI("spring")
public interface FrameWork {
    String getName(URL url);
    String getInfo();
}

public class Spring implements FrameWork {
    private FrameWork springBoot;

    public void setSpringBoot(FrameWork springBoot) {
        this.springBoot = springBoot;
    }

    @Override
    public String getName(URL url) {
        return "spring";
    }

    @Override
    public String getInfo() {
        return springBoot.getInfo()+" 流行的Spring框架";
    }
}


public class SpringBoot implements FrameWork{
    @Override
    public String getName(URL url) {
        return "springBoot";
    }

    @Override
    public String getInfo() {
        return "自动化的SpringBoot框架";
    }
}

SPI文件:

spring=com.adaptive.Spring
springBoot=com.adaptive.SpringBoot
guice=com.adaptive.Guice

测试类:

ApplicationModel applicationModel = ApplicationModel.defaultModel();
        ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
        FrameWork frameWork = extensionLoader.getExtension("spring");
        System.out.println(frameWork.getInfo());

直接运行上面的测试用例,会抛出异常,因为我们期望的是借助SpiExtensionInjector获取别名为springBoot的扩展实例进行注入,但是SpiExtensionInjector默认的行为是获取当前类型的自适应扩展点:

dubbo中spi也增加了AOP dubbo aop_依赖注入_05


我们的扩展接口FrameWork 中并没有使用@Adaptive注解标注需要自适应扩展的接口方法,所以会因为找不到扩展标记点而抛出异常。

为了达到我们的预期,我们可以自定义一个CustomSpiExtensionInjector:

public class CustomSpiExtensionInjector implements ExtensionInjector {
    private ExtensionAccessor extensionAccessor;

    @Override
    public <T> T getInstance(Class<T> type, String name) {
        return extensionAccessor.getExtension(type,name);
    }

    @Override
    public void setExtensionAccessor(ExtensionAccessor extensionAccessor) {
        this.extensionAccessor = extensionAccessor;
    }
}

对应SPI文件:

customSpiExtensionInjector=com.adaptive.CustomSpiExtensionInjector

但是直接向上面这样写还是会存在问题,根本原因在于返回的ExtensionInjector集合中的顺序问题:

dubbo中spi也增加了AOP dubbo aop_dubbo_06


loader.getSupportedExtensions()方法返回的是经过字母表排序过的扩展类集合:

dubbo中spi也增加了AOP dubbo aop_java_07


所以我们目前无法直接对ExtensionInjector进行排序,只能通过扩展实现类的别名来间接控制顺序。

为了防止我们自定义的ExtensionInjector把dubbo内部默认的依赖注入过程搅乱,需要通过注解打标记,限制我们自定义的ExtensionInjector所能处理的依赖注入范围:

public class CustomSpiExtensionInjector implements ExtensionInjector {
    private ExtensionAccessor extensionAccessor;

    @Override
    public <T> T getInstance(Class<T> type, String name) {
        if (!type.isAnnotationPresent(CustomInjector.class)) {
            return null;
        }
        return extensionAccessor.getExtension(type, name);
    }

    @Override
    public void setExtensionAccessor(ExtensionAccessor extensionAccessor) {
        this.extensionAccessor = extensionAccessor;
    }
}

在扩展类接口上打标记:

@CustomInjector
@SPI("spring")
public interface FrameWork {
    String getName(URL url);
    String getInfo();
}

再次运行测试用例,可以得到期望输出:

dubbo中spi也增加了AOP dubbo aop_依赖注入_08


扩展依赖注入器(ExtensionInjector)集合无法自定义排序规则 #12402


Wrapper机制

wrapper机制核心代码就在扩展实例依赖注入处理过后,源码如下,我们来简单复习一下:

private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        ...
        try {
            T instance = (T) extensionInstances.get(clazz);
            if (instance == null) {
                extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
                instance = (T) extensionInstances.get(clazz);
                instance = postProcessBeforeInitialization(instance, name);
                injectExtension(instance);
                instance = postProcessAfterInitialization(instance, name);
            }
            //和自适应扩展点创建的不同逻辑: 判断是否需要对当前扩展实例进行装饰
            if (wrap) {
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                //当前扩展类相关wrapper类型搜集工作在getExtensionClasses中完成
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }
                
                //wrapper class搜集是满足存在一个单参数的拷贝构造函数,并且参数类型为当前扩展类类型
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    //不断循环,套娃创建一层层的装饰器对象
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        //Wrapper注解用于实现按条件装饰
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        //如果wrapper class类上不存在Wrapper注解,那么表示装饰不需要满足任何条件
                        //否则,需要判断条件是否满足,满足才会进行装饰
                        boolean match = (wrapper == null) ||
                            ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                                !ArrayUtils.contains(wrapper.mismatches(), name));
                        if (match) {
                            //满足则进入装饰流程
                            //1.实例化当前装饰类,采用的是单参的拷贝构造函数
                            //2.执行依赖注入流程
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                            //3.执行后置处理流程
                            instance = postProcessAfterInitialization(instance, name);
                        }
                    }
                }
            }

            // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
            //调用初始化接口---注意上面警告信息,也就是说经过包装后,我们的包装对象未必继承lifecycle接口,因此初始化调用也就不会发生了
            initExtension(instance);
            return instance;
        } ...
    }

dubbo中spi也增加了AOP dubbo aop_dubbo中spi也增加了AOP_09


这里简单说明一下装饰条件指的是什么:

  • 首先,如果某个扩展类型存在某个扩展实现,该扩展实现类中存在一个拷贝构造函数,类型为当前扩展类型,则该扩展实现类会被搜集作为当前扩展实现的wrapper装饰类
  • 如果我们想限制当前wrapper对象只对满足条件的扩展实现类进行装饰,可以在wrapper对象类上标注@Wrapper注解,利用Wrapper注解中的属性作为装饰条件
@Retention(RetentionPolicy.RUNTIME)
public @interface Wrapper {

    /**
     * 只对扩展别名存在于matches数组中的扩展实现进行装饰
     */
    String[] matches() default {};

    /**
     * 如果扩展别名存在于matches数组中,则不会对当前扩展实现进行装饰
     */
    String[] mismatches() default {};

    /**
     * 用于扩展类型的多个wrapper实现类进行排序
     */
    int order() default 0;
}

使用实践

更改上面测试用例中扩展实现类:

@Wrapper(matches = "spring")
public class SpringBoot implements FrameWork{
    private FrameWork wrapper;

    public SpringBoot(FrameWork frameWork) {
        this.wrapper = frameWork;
    }

    @Override
    public String getName(URL url) {
        return "springBoot";
    }

    @Override
    public String getInfo() {
        return wrapper.getInfo()+" 自动化的SpringBoot框架";
    }
}

@Wrapper(matches = "springBoot")
public class Guice implements FrameWork{
    private FrameWork wrapper;

    public Guice(FrameWork frameWork) {
        this.wrapper = frameWork;
    }
    @Override
    public String getName(URL url) {
        return "guice";
    }

    @Override
    public String getInfo() {
        return wrapper.getInfo()+" google 开源的轻量级IOC框架";
    }
}

public class Spring implements FrameWork {
    @Override
    public String getName(URL url) {
        return "spring";
    }

    @Override
    public String getInfo() {
        return "流行的Spring框架";
    }
}

测试类:

ApplicationModel applicationModel = ApplicationModel.defaultModel();
        ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
        FrameWork frameWork = extensionLoader.getExtension("spring");
        System.out.println(frameWork.getInfo());

dubbo中spi也增加了AOP dubbo aop_java-zookeeper_10


很明显,只有SpringBoot对Spring进行了装饰,而Guice没有对Spring进行装饰,因为其类上的@Wrapper注解限制了其只会对扩展别名为springBoot的扩展实现进行装饰。


注意

如果我们更改测试用例,尝试获取扩展别名为springBoot的扩展实现,则会抛出扩展不存在的异常:

ApplicationModel applicationModel = ApplicationModel.defaultModel();
        ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
        FrameWork frameWork = extensionLoader.getExtension("springBoot");
        System.out.println(frameWork.getInfo());

dubbo中spi也增加了AOP dubbo aop_dubbo_11


这个原因是FrameWork的Wrapper装饰类会被单独搜集起来,而不会作为普通扩展实现类保存起来:

loadClass方法是在dubbo加载当前扩展类型所有SPI文件流程中被调用的:(如有遗忘,回看前面两篇原理篇)

dubbo中spi也增加了AOP dubbo aop_java-zookeeper_12


所以,当我们尝试从extensionClasses集合中获取别名为springBoot的普通扩展类型时,自然会找不到,而抛出异常。