【Dubbo】SPI的IOC与AOP

【Dubbo】SPI的IOC与AOP

Scroll Down

前言

Dubbo的API,除了填补Java的SPI缺点之外,其实还有别的作用,比如它还实现自己的一个IOC(当然也有基于Spring的),还有基于扩展点的AOP,这次就来简单看看它的源码是什么样子的。

Dubbo Version: 2.7.7

Dubbb IOC

injectExtension

首先回到老地方org.apache.dubbo.common.extension.ExtensionLoader,之前就说过,SPI的操作基本都是围绕这它转的,这次也不例外。

来到这个方法org.apache.dubbo.common.extension.ExtensionLoader#createExtension,其实IOC和AOP的逻辑都在这里面:

    @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        // 读取、预处理
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 实现IOC核心,完成一些依赖注入的工作,里面顺便能看到ExtensionFactory,它也是一个比较关心的地方
            injectExtension(instance);

            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                // AOP的核心就是下面这段
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }
            // 将包装后的类在注入
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

这里跟进injectExtension方法:
org.apache.dubbo.common.extension.ExtensionLoader#createExtension

    private T injectExtension(T instance) {

        // 这里的objectFactory
        if (objectFactory == null) {
            return instance;
        }

        try {
            // 遍历所有set方法
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                // 获取要set的参数类型
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    // 获取要set的属性名
                    String property = getSetterProperty(method);
                    // 获取要set的对象
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        // 反射调用set注入对象,完成属性注入
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
                    logger.error("Failed to inject via method " + method.getName()
                            + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
  • 可以看到注入其实很简单,就是单纯的依靠反射去完成而已,这里关键还是如何获取到要set进去的object,即objectFactory.getExtension(pt, property);逻辑。

objectFactory

我们可以追溯一下objectFactory是怎么诞生的,其实就在就在ExtensionLoader的构造函数中:
org.apache.dubbo.common.extension.ExtensionLoader#ExtensionLoader

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

之前讲Dubbo加载配置的时候就说过了,加载扩展类的时候,是根据SPI描述文件加载的,于是我在不同文件找到如下类型:
image.png
image.png

在ExtensionLoader第一次构造的时候,就会加载AdaptiveExtensionFactory,然后在AdaptiveExtensionFactory中加载SpiExtensionFactorySpringExtensionFactory,下面来看看AdaptiveExtensionFactory代码:

package org.apache.dubbo.common.extension.factory;

import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * AdaptiveExtensionFactory
 */
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        // 这段逻辑其实就是在加载SpringExtensionFactory和SpiExtensionFactory
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            // 尝试从Spring上下文获取 或者 从cachedAdaptiveInstance中获取
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }
}

再打个断点瞧瞧:
image.png

由上面代码我们可以得知,除了SpiExtensionFactorySpringExtensionFactory外,其它情况经过injectExtension方法时,objectFactory都是AdaptiveExtensionFactory的实例了,不信你可以再打断点看看。

好了,现在我们已经知道这里的objectFactory就是AdaptiveExtensionFactory了,视角重新回到injectExtension方法的objectFactory.getExtension(pt, property);中,第一次getExtension则会进入到AdaptiveExtensionFactory的实现中:
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory#getExtension

    @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;
    }
  • 这里的factories就是上面说的SpiExtensionFactory和SpringExtensionFactory。

然后来看看SpringExtensionFactory和SpiExtensionFactory的getExtension方法实现:
org.apache.dubbo.config.spring.extension.SpringExtensionFactory#getExtension

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {

        //SPI should be get from SpiExtensionFactory
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }

        // 从上下文获取对象
        for (ApplicationContext context : CONTEXTS) {
            T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
            if (bean != null) {
                return bean;
            }
        }

        //logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());

        return null;
    }

org.apache.dubbo.common.extension.factory.SpiExtensionFactory#getExtension

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

上面Spring的IOC加载相信就不用我多说了,这里稍微跟一下getAdaptiveExtension()方法,里面有一段动态生成字节码的逻辑,稍微跟一下。

动态生成字节码

这里只会看看它的简单逻辑,不会对其深入,对javassist感兴趣的就自行Google补充吧

从上面的loader.getAdaptiveExtension();逐步跟进:
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass

    private Class<?> getAdaptiveExtensionClass() {
        // 讲配置加载的时候已经看过了
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 跟进这里,里面有javassist生成字节码的相关逻辑
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

这里继续跟进createAdaptiveExtensionClass()方法:
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass

    private Class<?> createAdaptiveExtensionClass() {
        // 构建Java代码,非字节码,给接口生成实现(有Adaptive注解的方法有具体实现,没有注解的方法就直接抛UnsupportedOperationException异常)
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        // javassist动态构建
        return compiler.compile(code, classLoader);
    }

关于字节码的动态构建,这里就不详细展开了,有兴趣的自行Google,我也还没细看过,下面简单贴下两个生成前后的接口和类:

原接口

package org.apache.dubbo.rpc;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

import java.util.Collections;
import java.util.List;

/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("dubbo")
public interface Protocol {

    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

    default List<ProtocolServer> getServers() {
        return Collections.emptyList();
    }

}

生成后:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    public void destroy()  {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public int getDefaultPort()  {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
    public java.util.List getServers()  {
        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
}
  • 可见接口上没带Adaptive注解的方法,方法体内全都是抛出了UnsupportedOperationExceptioni异常。

Dubbo的IOC代码阅读就到这了,虽然有些细节没一一展开讲,但是相信并不影响整体理解,这里稍微再小结一下:

  • Dubbo的依赖注入是依靠反射完成的。
  • 在正式注入到目标对象之前,会获取要注入的对象,获取方式有两种,一种是Dubbo的SPI描述文件中已经有实现类了,就直接从缓存里面获取;另一种就是通过@SPI接口本身动态构建字节码,且只对带@Adaptive的方法进行实际实现,不带@Adaptive的则方法体内抛出UnsupportedOperationExceptioni异常。

Dubbo AOP

Dubbo的AOP其实很容易理解,实现其实就是疯狂包装,在包装类上进行增强,用到了装饰者模式,下面先来看看源码。

回到org.apache.dubbo.common.extension.ExtensionLoader#createExtension方法中,找到如下代码:

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                // 之前讲加载配置的时候就讲到过这个了,这里不再补充,如果不明白,可看下面提供的demo
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                // AOP的核心就是下面这段
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }

核心是这一段(T) wrapperClass.getConstructor(type).newInstance(instance)可见是调用构造器注入原始对象进行包装的,我们可以看看官方的代码是不是这样:

image.png

为了便于理解,这里再自定义一个例子,注意这是在一个Dubbo项目当中,请注意相关依赖的配置:

image.png

  • 红框以外的代码都无需关注。

Person.java

import org.apache.dubbo.common.extension.SPI;

/**
 * @author wenjie
 */
@SPI("person")
public interface Person {

    void hello();
}

Student.java

import store.wenjie.www.dubboproviderdemo.test.Person;

/**
 * @author wenjie
 */
public class Student implements Person {
    public void hello() {
        System.out.println("I am student");
    }

}

StudentWrapper.java

import store.wenjie.www.dubboproviderdemo.test.Person;

/**
 * @author wenjie
 */
public class StudentWrapper implements Person {

    private Person person;

    public StudentWrapper(Person person) {
        this.person = person;
    }

    public void hello() {
        System.out.println("before");
        person.hello();
        System.out.println("after");

    }

}

测试类Test.java


import org.apache.dubbo.common.extension.ExtensionLoader;

public class Test1 {

    public static void main(String[] args) {
        ExtensionLoader<Person> loader = ExtensionLoader.getExtensionLoader(Person.class);
        Person studesnt = loader.getExtension("student");
        studesnt.hello();
    }
}

配置store.wenjie.www.dubboproviderdemo.test.Person,等下还要加上包装类:

student=store.wenjie.www.dubboproviderdemo.test.impl.Student

执行上面的Test.java主函数,执行结果如下:

image.png

更改配置store.wenjie.www.dubboproviderdemo.test.Person如下:

student=store.wenjie.www.dubboproviderdemo.test.impl.Student
filter=store.wenjie.www.dubboproviderdemo.test.wrapper.StudentWrapper

再执行一次测试:

image.png

就是这么简单= =,相信不用我再过多阐述了。

AOP的源码阅读就先这样了,如果有什么遗漏的欢迎指出补充~