Dubbo 源码解析(一)Dubbo SPI

Dubbo 扩展机制 SPI

在 Dubbo 中,SPI 贯穿在整个 Dubbo 的核心。所以有必要先对 Dubbo 中 SPI 做一个详细的介绍。

JDK SPI

之前写过一篇介绍 JDK SPI 的博客,可以点击查看。

Dubbo SPI

Dubbo 实现了自己的 SPI 机制

  • 除了可以配置在 META-INF/services 目录下,还可以配置在 META-INF/dubboMETA-INF/dubbo/internal
  • 配置文件内容采用 key=value 的形式。这样可以按需实例化加载类,而不用像 JDK 一样在启动时一次性加载实例化扩展点的所有实现,浪费性能。
  • 出现异常时可以更准确定位
  • 增加了对 Duboo 自己实现的 IOC 和 AOP 的支持

源码解析

这里使用的版本是 dubbo-2.6.4

这里的分析流程是根据例子来分析内部源码的实现

注意,在阅读下面的源码解析时,有些跟当前流程无关的代码我会标注 忽略点 - X,表示这段代码先跳过,不影响当前流程,且在后面用到的地方我会再重新解释该忽略点的意思。

1、Dubbo SPI 的基本示例

接口,注意加 @SPI 注解

@SPI
public interface Robot {

	void sayHello();

}

实现类

public class MIRobot implements Robot {

	@Override
	public void sayHello() {
		System.out.println("大家好,我是小米机器人...");
	}
}

在文件夹 resources/META-INF/services 下添加配置文件,文件名 com.lin.spi.Robot(接口的路径)

文件内容

miRobot = com.lin.spi.MIRobot

在测试类中调用

public class DubboSPITest {

	@Test
	public void test() {

		ExtensionLoader<Robot> extensionLoader =
				ExtensionLoader.getExtensionLoader(Robot.class);

		Robot miRobot = extensionLoader.getExtension("miRobot");
		miRobot.sayHello();
	}
}

输出

大家好,我是小米机器人...

Process finished with exit code 0

上面就是 Dubbo SPI 的基本使用,接下来开始源码分析。

(1)入口方法 ExtensionLoader#getExtensionLoader(Class<T> type)
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    // ...
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

getExtensionLoader(Class<T> type) 的作用只是简单的根据 type 包装一个 ExtensionLoader 实例,缓存在 EXTENSION_LOADERS 中,然后返回 ExtensionLoader 实例。

其中 ExtensionLoader 实例化时 objectFactory 的创建我们这里先忽略

(2)调用方法 ExtensionLoader#getExtension(String name) 获取具体的实例
@SuppressWarnings("unchecked")
public T getExtension(String name) {
    // ...
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

getExtension(String name) 的作用就是创建我们在配置文件配置的扩展点实现类(例子中的 MIRobot 实例),包装在 Holder 中然后缓存进 cachedInstances,返回实例。

创建扩展类

@SuppressWarnings("unchecked")
private T createExtension(String name) {
    // 根据 type 去配置文件中加载配置的所有扩展类存进 Map<String, Class<?>> 缓存,再根据 name 得到特定的 clazz
    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);
        }
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                                        type + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

createExtension(String name) 方法的逻辑包含如下步骤

  • 获取所有的扩展类(实例中是返回 miRobot -> "class com.lin.spi.MIRobot" )
  • 通过反射创建拓展对象
  • 向拓展对象中注入依赖(这里暂时忽略)
  • 将拓展对象包裹在相应的 Wrapper 对象中(这里暂时忽略)
(2.1)获取所有的扩展类
private Map<String, Class<?>> getExtensionClasses() {
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
    // 获取 @SPI 注解
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                // 检测 @SPI 注解内容是否合法
                throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                                                + ": " + Arrays.toString(names));
            }
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    // 加载指定文件夹下的配置文件  
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // META-INF/dubbo/internal
    loadDirectory(extensionClasses, DUBBO_DIRECTORY); // META-INF/dubbo
    loadDirectory(extensionClasses, SERVICES_DIRECTORY); // META-INF/services
    return extensionClasses;
}

loadDirectory 方法先通过 classLoader 获取所有资源链接(配置文件链接),然后再通过 loadResource 方法读取和解析配置文件,把解析出的每一个 clazz 调用 loadClass()cachedNamesextensionClassescachedActivates 等缓存起来

2、Dubbo AOP 实现

例子

在上面的例子基础上,新建类 RobotWrapper

public class RobotWrapper implements Robot {

	private Robot robot;

	public RobotWrapper(Robot robot) {
		this.robot = robot;
	}

	@Override
	public void sayHello() {
		System.out.println("before...");
		this.robot.sayHello();
		System.out.println("after...");
	}
}

在配置文件中写上

miRobot = com.lin.spi.MIRobot
wrapper = com.lin.spi.RobotWrapper

关于 RobotWrapper,一定要有构造函数才会生效,同时在配置文件中可以不用写 wrapper=,也可以同时存在多个 xxxWrapper 实现

接下来不用改动原来的代码,直接运行,

输出:

before...
大家好,我是小米机器人...
after...
源码分析

getExtensionClass(key) -> getExtensionClasses() -> loadExtensionClasses() -> loadDirectory() -> loadResource() -> loadClass()

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
 	// ...
    if () {
        
    } else if (isWrapperClass(clazz)) { // 判断 clazz 是否有构造函数  忽略点-3的解释
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz); // 把 clazz 存进缓存中
    } else {
        
    }
}
private boolean isWrapperClass(Class<?> clazz) {
    try {
        clazz.getConstructor(type); // clazz 是否有 type 作为参数的构造函数
        return true;
    } catch (NoSuchMethodException e) {
        return false;
    }
}

接下来在 clazz 实例化时,

@SuppressWarnings("unchecked")
private T createExtension(String name) {
    // ...
    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
        // 取出所有的 wrapperClass,遍历,取出每一个,把 instance 传进 wrapperClass 的构造函数并实例化,赋值给 instance
        // 最后返回的 instance 就是被 wrapper 层层包裹的结果
        // 代入例子中,instance = RobotWrapper.class.getConstructor(Robot.class).newInstance(miRobot)
        // 类似 instance = new RobotWrapper(miRobot)
        for (Class<?> wrapperClass : wrapperClasses) {
            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }
    }
    // ...
}

3、Dubbo IOC 实现

官网解释

Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。

Dubbo IOC 这部分可以结合 Dubbo 的自适应扩展来讲

4、什么是自适应扩展?

前面我们讲解 Dubbo 可以通过 ExtensionLoader.getExtensionLoader(XXXClass).getExtension(key) 的形式来获取某个接口的实现类,但这种形式属于硬编码来指定引用哪个实现。而现在某些扩展希望可以在被调用时,根据运行时参数进行加载,这需要自适应扩展来实现。

首先先来看和自适应扩展相关的两个参数,一个是注解 @Adaptive,一个是 com.alibaba.dubbo.common.URL

@Adaptive 可修饰在类和方法上,

  • 当修饰在类上时,表示该类为所实现的接口的代理类实现
  • 当修饰在方法上时,Dubbo 会自动生成适配器类,会从 URL 中取值作为扩展点名去加载实现类并实例化,最后再使用这个实例调用对应的方法。

例子:

@SPI
public interface Robot {

	@Adaptive("robot")
	void sayHello(URL url);
}
public class AdaptiveRobot implements Robot {

	private Robot robot;

	public void setRobot(Robot robot) {
		this.robot = robot;
	}

	@Override
	public void sayHello(URL url) {
		System.out.println("在代理类内部通过 URL 和 SPI 机制获取和加载具体的 Robot 实现类,并调用目标方法");
		this.robot.sayHello(url);
	}
}
public class MIRobot implements Robot {

	@Override
	public void sayHello(URL url) {
		System.out.println("hello");
	}
}
import com.alibaba.dubbo.common.URL;

public class DubboSPITest {

	@Test
	public void test() {

		ExtensionLoader<Robot> extensionLoader =
				ExtensionLoader.getExtensionLoader(Robot.class);

		Map<String, String> map = new HashMap<>();
		map.put("robot", "miRobot"); // key=robot 由注解 @Adaptive("robot") 决定
		URL url = new URL("", "", 1, map);
		Robot adaptiveRobot = extensionLoader.getExtension("adaptiveRobot");
		adaptiveRobot.sayHello(url);
	}
}

输出:

在代理类内部通过 URL 和 SPI 机制获取和加载具体的 Robot 实现类,并调用目标方法
hello
源码分析

在执行 Class<?> clazz = getExtensionClasses().get("adaptiveRobot"); 并实例化 instance 后,调用 injectExtension(instance) 方法向扩展实例中注入依赖

@SuppressWarnings("unchecked")
private T createExtension(String name) {
    Class<?> clazz = getExtensionClasses().get(name);
    // ...
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        // ...
        injectExtension(instance);
        // ...
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                                        type + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

接下来看 injectExtension(instance) 的具体实现

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 {
                        // 获取属性名,比如 setRobot 方法对应属性名 robot
                        String property = method.getName().length() > 3 ? 
                            method.getName().substring(3, 4).toLowerCase() + 
                            	method.getName().substring(4) : "";
                        // 从 ObjectFactory 中获取依赖对象  eg. XXX$Adaptive
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            // 通过反射调用 setter 方法设置依赖
                            method.invoke(instance, object); // 往示例中的 AdaptiveRobot 实例的 setRobot() 方法中注入 Robot$Adaptive 自适应类
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method...");
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

这部分源码解释看注释就可以,流程很清晰,主要说下里面的 Object object = objectFactory.getExtension(pt, property);objectFactory

objectFactory 的实例化

每一个 objectFactory 的创建都是在 ExtensionLoader 实例化的时候,

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

接下来看 getAdaptiveExtension()

public T getAdaptiveExtension() {
    // 从缓存中获取自适应拓展
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 创建自适应拓展
                        instance = createAdaptiveExtension();
                        // 设置自适应拓展到缓存中
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: ...");
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance:  ...");
        }
    }

    return (T) instance;
}
private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化,调用 injectExtension 方法向拓展实例中注入依赖
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ...");
    }
}
private Class<?> getAdaptiveExtensionClass() {
    // 通过 SPI 获取所有的拓展类,例子中指所有 Robot 接口实现类
    getExtensionClasses();
    // 如果某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。
    // 那么这里直接返回,不创建自适应扩展类
    // 这也就是为什么说被 @Adaptive 注解修饰在类上和方法上不一样
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应拓展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

现在,世界线收束,我们再从 getExtensionClasses(ExtensionFactory.class); 方法看起,一层层进到最里面的 loadClass() 方法时,这个接口 ExtensionFactory.class 的实现类有 AdaptiveExtensionFactorySpiExtensionFactorySpringExtensionFactory。其中 AdaptiveExtensionFactory 类上被注解 @Adaptive 修饰,表示该类是该接口的适配器,不提供具体业务支持,会根据在运行时的一些状态来选择具体调用 ExtensionFactory 的哪个实现。

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    // ...
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        }
    } 
    // ...
}

返回 cachedAdaptiveClass = adaptiveExtensionFactory

因此,当一开始 ExtensionLoader.getExtensionLoader(Robot.class) 初始化后,得到(忽略点 - 1 的解释)

private ExtensionLoader(Robot.class) {
    this.type = Robot.class;
    objectFactory = adaptiveExtensionFactory;
}

搞清楚 objectFactory 等于什么之后,我们继续看 injectExtension(instance) 的内容,

private T injectExtension(T instance) {
    // ...
    // 从 ObjectFactory 中获取依赖对象
    Object object = objectFactory.getExtension(pt, property);
    if (object != null) {
        // 通过反射调用 setter 方法设置依赖
        method.invoke(instance, object);
    }
    // ...
    return instance;
}

objectFactory.getExtension(pt, property) 也就是 adaptiveExtensionFactory.getExtension(pt, property),而 AdaptiveExtensionFactory 在实例化的时候会根据当前系统环境获取 ExtensionFactory,也就是 SpiExtensionFactorySpringExtensionFactory 存到 factories 中。

public AdaptiveExtensionFactory() {
    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);
}

我们看其中一个 SpiExtensionFactory 的实现,

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() 一步步跟进,来到 createAdaptiveExtensionClass(), 创建默认代理类

private Class<?> createAdaptiveExtensionClass() {
    // 通过反射检测接口方法是否包含 Adaptive 注解
    // 构建自适应拓展代码
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    // 获取编译器实现类
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译代码,生成 Class
    return compiler.compile(code, classLoader);
}

如果你对 createAdaptiveExtensionClass() 生成的代理类感觉有点模糊的话,觉得看代码不清晰,我们可以用阿里开源的工具 arthas 反编译查看一下生成的代理类是什么样子的

由上图可知,生成的代理类从 com.alibaba.dubbo.common.URL 中获取参数实例化并调用接口方法,返回生成的自适应类。

在我们举的例子中,injectExtension(T instance) 的作用就是,生成自适应类 object = Robot$Adaptive,并通过 AdaptiveRobot.setRobot(Robot robot) 注入进去。

5、@Activate 自动激活扩展点

表示实现类是否可以被激活。通常被用在一个接口有很多现实类,但是这些实现类在特定条件才需要使用,比如有些实现只想在生产者生效,有些实现类想在消费者生效。它有两个设置过滤条件的字段,group,value 都是字符数组,用来指定这个扩展类在什么条件下激活。

loadClass() 加载扩展类时,把所有实现类中有Activate注解的,放到 cachedActivates 中(忽略点 - 4的解释)

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    // ...
    if (names != null && names.length > 0) {
        Activate activate = clazz.getAnnotation(Activate.class);
        if (activate != null) {
            cachedActivates.put(names[0], activate);
        }
        // ...
    }
}
后记

这篇文章讲解了 Dubbo SPI 的实现机制,关键是要弄懂 @SPI @Adaptive @Activate 注解的含义,知道 Dubbo 中 AOP、IOC 的各自实现,还有就是 SPI 的自适应拓展机制以及 Dubbo 中的 URL 作为配置总线,各个服务的参数配置都是通过 URL 进行传递的。

posted @ 2021-09-26 15:37  心灵蚂蚁  阅读(911)  评论(0编辑  收藏  举报