Dubbo 源码解析(一)Dubbo SPI(深度剖析dubbo源码)

  本篇文章为你整理了Dubbo 源码解析(一)Dubbo SPI(深度剖析dubbo源码)的详细内容,包含有dubbo源码解析20pdf 深度剖析dubbo源码 dubbo示例代码 dubbo开源时间 Dubbo 源码解析(一)Dubbo SPI,希望能帮助你了解 Dubbo 源码解析(一)Dubbo SPI。

  Dubbo 扩展机制 SPI

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

  JDK SPI

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

  Dubbo SPI

  Dubbo 实现了自己的 SPI 机制

  除了可以配置在 META-INF/services 目录下,还可以配置在 META-INF/dubbo 和 META-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() 在 cachedNames、extensionClasses、cachedActivates 等缓存起来

  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 的实现类有 AdaptiveExtensionFactory、SpiExtensionFactory 和 SpringExtensionFactory。其中 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,也就是 SpiExtensionFactory 和 SpringExtensionFactory 存到 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 进行传递的。

  以上就是Dubbo 源码解析(一)Dubbo SPI(深度剖析dubbo源码)的详细内容,想要了解更多 Dubbo 源码解析(一)Dubbo SPI的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: