美文网首页
逐步深入 Dubbo SPI 原理

逐步深入 Dubbo SPI 原理

作者: 吴老板 | 来源:发表于2020-08-21 18:51 被阅读0次

    什么是 SPI ?

    SPI(Service Provider Interface) 是一种服务发现机制, 主要原理是在运行时根据具体参数去查找约定路径(JDK 默认是在/META-INF/services/)下的配置实现类信息。 通过然后类加载机制(ClassLoader)实现对类信息的加载以及后面的实例化,实现黑盒扩展的作用。简单点来说就是在运行时动态指定并加载实现类,实现指定功能点扩展。

    推荐连接高级开发必须理解的Java中SPI机制

    Dubbo SPI 和JDK SPI 的区别

    是的,Dubbo并没有沿用JDK内置的SPI机制。 而是自行实现了一套SPI机制,从Dubbo的官方描述来看,Dubbo是对JDK的SPI进行了一次改进。 那这里就有必要说明下。JDK SPI有哪些问题呢? Dubbo 又是如何对其进行改进的呢?

    JDK SPI问题

    • 需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。
    • 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
    • 扩展如果依赖其他的扩展,做不到自动注入和装配
    • 不提供类似于Spring的IOC和AOP功能
    • 扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持

    Dubbo SPI的增强

    • 通过Map缓存的机制, 对Class类进行缓存,在运行时调用明确名称的类。 获取对应的类信息,进行实例化。 提高了启动性能
    • Dubbo SPI 实现了类似IOC 和AOP 的机制。提供了查找和动态扩展的功能。
    • Dubbo 也提供了从扩展点容器获取实例对象的功能, 如通过SpringExtensionFactory就实现了通过Spring的容器获取依赖的功能。 可以更好的和其他框架进行集成。

    Dubbo SPI示例

    代码示例

    @SPI
    public interface Car {
        void test();
    }
    
    //宝马
    public class BWMCar implements Car {
    
        @Override
        public void test() {
            System.out.println("hi, 我是宝马车");
        }
    }
    
    public static void main(String[] args) {
            ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
    
            Car car = extensionLoader.getExtension("bwm");
            car.test();
    }
    

    配置示例
    在/META-INF/dubbo/目录下创建文件com.wgt.dubbo.samples.spi.demo1.car.Car文件,配置信息如下

    bwm=com.wgt.dubbo.samples.spi.demo1.car.BWMCar
    

    输出结果

    hi, 我是宝马车
    

    从示例中的来看。 流程时先ExtensionLoader.getExtensionLoader(Class class) 获取到一个ExtensionLoader示例, 然后调用ExtensionLoader的getExtension(String name)方法获取到目标实例, 那么我们从ExtensionLoader.getExtensionLoader(Car.class)开始,进入源码,弄清原理。

    代码的示例很简单, 但是里面的学问有很多, 不过不需要心急,我们需要确保看源码的过程中不会迷失。 接下来的源码解析将围绕着本段示例代码来进行步步深入分析, 尽量做到细微入致。

    Dubbo 加载拓展点

    • 创建ExtensionLoader实例源码
    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        // 判断type 是否为接口
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        // 判断是否标注了@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
            // 从缓存中获取ExtensionLoader。 
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            //缓存中没有的话 则创建一个新的ExtensionLoader加入缓存中。
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
    
    //构造方法
    private ExtensionLoader(Class<?> type) {
            this.type = type;
                //此处默认的objectFactory 为org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory,
                // 该处的objectFactory 相当于一个IOC容器。 后面在IOC部分详细说明
            objectFactory = (type == ExtensionFactory.class ? null :            ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
    

    从中可以看出创建ExtensionLoader实例的源码是比较简单的。 疑问点objectFactory对象工厂我们放到后面再说。接下来我们先看getExtension方法都做了什么。

    • getExtension方法源码分析
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 缓存中根据name 获取加载过的实例
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
       
        // double check synchronized 确保线程安全
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 创建新的Extension 信息
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    
    // 创建新的实例对象
    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
      // 此处会读取/META-INF/services/ 下的扩展点文件信息。 将所有类信息加载进来后根据name 获取具体的实例对象
      // 当前示例主要说明点。 
      Class<?> clazz = getExtensionClasses().get(name);
      if (clazz == null) {
        throw findException(name);
      }
      try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
          // 根据Class 信息通过反射进行实例化。 
          EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
          instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        
        // 进行依赖注入 在IOC 内容详细说明
        injectExtension(instance);
        
        // AOP 内容。 使用装饰器模式实现 在AOP部分详细说明
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
          for (Class<?> wrapperClass : wrapperClasses) {
            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
          }
        }
        // 调用instance 的初始化方法。 如果class 实现了Lifecycle 接口的话
        initExtension(instance);
        return instance;
      } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                                        type + ") couldn't be instantiated: " + t.getMessage(), t);
      }
    }
    

    此处我们可以看出获取扩展点实例的大致流程如下:

    加载接口类型的所有实现类Class信息并缓存 ----> 根据name去获取指定扩展实现类Class ----> 通过反射机制对实现类Class进行实例化并缓存 ----> 基于dubbo 的IOC机制对实例对象进行依赖注入----> 判断是否有包装类(wrapperClasses),决定是否要生成代理对象 ----> 最后对对象进行初始化之后返回该实例对象

    • 第一步实现类的加载 getExtensionClasses()
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // 加载所有class
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
    private Map<String, Class<?>> loadExtensionClasses() {
                // 通过@SPI("DefaultExtensionName") 缓存默认的extension name
            cacheDefaultExtensionName();
      
                    //根据type.name  查找各个配置文件路径下的实现类拓展配置
            Map<String, Class<?>> extensionClasses = new HashMap<>();
            // internal extension load from ExtensionLoader's ClassLoader first
            loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
            loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
    
            loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
            loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
            loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
            loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
            return extensionClasses;
    }
    
    private void loadDirectory(Map<String, Class<?>> extensionClasses, 
                               String dir, 
                               String type, 
                               boolean extensionLoaderClassLoaderFirst) {
            String fileName = dir + type;
            try {
                Enumeration<java.net.URL> urls = null;
              
                // 获取ClassLoader
                ClassLoader classLoader = findClassLoader();
                
                // try to load from ExtensionLoader's ClassLoader first
                // 如果需要先尝试使用ExtensionLoader 查找文件资源
                if (extensionLoaderClassLoaderFirst) {
                    ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                    if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                        urls = extensionLoaderClassLoader.getResources(fileName);
                    }
                }
              
                // 使用默认加载器获取资源文件
                if(urls == null || !urls.hasMoreElements()) {
                    if (classLoader != null) {
                        urls = classLoader.getResources(fileName);
                    } else {
                        urls = ClassLoader.getSystemResources(fileName);
                    }
                }
                            
                if (urls != null) {
                    while (urls.hasMoreElements()) {
                        java.net.URL resourceURL = urls.nextElement();
                        // 读取类配置, 加载类信息
                        loadResource(extensionClasses, classLoader, resourceURL);
                    }
                }
            } catch (Throwable t) {
                logger.error("Exception occurred when loading extension class (interface: " +
                        type + ", description file: " + fileName + ").", t);
            }
    }
    
    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
      
      //.... 此处省略文件解析部分代码,不在主逻辑范畴,有兴趣的同学可以自行深入
    
      // 遍历加载class实现类, 通过Class.forName(line, true, classLoader) 实现类加载
      loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
      //.... 此处省略异常处理
    }
    
    //解析类信息
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
            if (!type.isAssignableFrom(clazz)) {
                throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                        type + ", class line: " + clazz.getName() + "), class "
                        + clazz.getName() + " is not subtype of interface.");
            }
                // 缓存标注了@Adaptive的扩展实现类, 具体作用后面自适应问题详细说明
            if (clazz.isAnnotationPresent(Adaptive.class)) {
                cacheAdaptiveClass(clazz);
            // 判断是否为包装类, 缓存包装类
            } else if (isWrapperClass(clazz)) {
                cacheWrapperClass(clazz);
            } else {
                clazz.getConstructor();
                // 如果没有指定name 生成name
                if (StringUtils.isEmpty(name)) {
                    name = findAnnotationName(clazz);
                    if (name.length() == 0) {
                        throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                    }
                }
                            // 分割name, 因为可以多个name 指向同一个实现类。例: aa, bb = org.apache.dubbo.extension.C
                String[] names = NAME_SEPARATOR.split(name);
                if (ArrayUtils.isNotEmpty(names)) {
                    // 缓存@Activate标注的类信息
                    cacheActivateClass(clazz, names[0]);
                    for (String n : names) {
                        // 遍历缓存 name和class 信息
                        cacheName(clazz, n);
                        saveInExtensionClass(extensionClasses, clazz, n);
                    }
                }
            }
    }
    

    到此基本了解了拓展类的加载过程, 基本逻辑也非常简单。 获取到classLoader并调用Class.forname("className")将扩展类信息加载进JVM中, 之后就是从功能扩展的维度,对class进行不同纬度的缓存, 方便后面在某项功能点中快速获取类信息。

    Dubbo IOC实现

    • 基于IOC的依赖注入 injectExtension(instance)
    private T injectExtension(T instance) {
    
        if (objectFactory == null) {
            return instance;
        }
    
        try {
            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;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }
    
                try {
                    String property = getSetterProperty(method);
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        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;
    }
    

    从上面的逻辑很简单的看出大致逻辑就是先获取所有的setter方法,获取setter方法的参数的类型和名称, 最后通过我们前面看到过的objectFactory获取到依赖对象, 然后对其进行依赖注入。 那这里我们就需要深扒一下这个objectFactory 到底是个神马。前面我在objectfactory的注释说明了默认为AdaptiveExtensionFactory类, 我们先来看下他的源码

    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
            // 保存了ExtensionFactory的实现类。 
        private final List<ExtensionFactory> factories;
            
        public AdaptiveExtensionFactory() {
            // 初始化将 其他ExtensionFactory拓展类加载缓存
            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) {
                T extension = factory.getExtension(type, name);
                if (extension != null) {
                    return extension;
                }
            }
            return null;
        }
    }
    

    很明显该类是一个策略类, 启动时候会讲其他的ExtensionFactory实现类加载进来, 然后再获取Extension实现的时候直接遍历各个容器来进行查找。那我们来看下ExtensionFactory默认实现的子类。

    // SPI 默认的IOC实现,
    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;
        }
    }
    
    // Spring 实现
    public class SpringExtensionFactory implements ExtensionFactory {
        private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
    
        private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
    
        public static void addApplicationContext(ApplicationContext context) {
            CONTEXTS.add(context);
            if (context instanceof ConfigurableApplicationContext) {
                ((ConfigurableApplicationContext) context).registerShutdownHook();
            }
        }
    
        public static void removeApplicationContext(ApplicationContext context) {
            CONTEXTS.remove(context);
        }
    
        public static Set<ApplicationContext> getContexts() {
            return CONTEXTS;
        }
    
        // currently for test purpose
        public static void clearContexts() {
            CONTEXTS.clear();
        }
    
        @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;
        }
    }
    

    以上ExtensionFactory主要分为两类

    • SpiExtensionFactory是Dubbo 的IOC默认容器,对象是从前面加载缓存中获取的SPI的扩展对象。 这是Dubbo 的默认机制

    • SpringExtensionFactory是使用Spring的IOC容器。我们可以将Spring容器中的Bean注入到Extension实例中,通过该容器我们可以更好的继承Spring框架甚至其他组件。

    Dubbo Aop实现

    可能用过一些容器框架的同学都知道AOP的基本原理。 甚至有的同学可能首先想到的就是动态代理模式,这并没有错。 但是方法不是唯一的, Dubbo中获取的代理对象是通过装饰器模式实现的,接下来我们继续深入看下源码。

    通过前面创建Extension我们可以知道, 是否需要生成对实力对象进行封装的依据是cachedWrapperClasses是否为空。同时通过类信息加载过程可以知道,在加载类的时候会对cachedWrapperClasses进行缓存, 那我们这边举例一个wrapper类,然后再次运行。

    • CarWrapper
    public class CarWrapper implements Car{
    
        private Car car;
    
        public CarWrapper(Car car){
            this.car = car;
        }
    
        @Override
        public void test(CarBrand brand) {
            System.out.println("包装类前置执行");
            car.test(brand);
            System.out.println("包装类后置执行");
        }
    }
    
    • 配置信息
    
    bwm=com.wgt.dubbo.samples.spi.demo1.car.BWMCar
    audi=com.wgt.dubbo.samples.spi.demo1.car.AudiCar
    
    # 代理类配置
    com.wgt.dubbo.samples.spi.demo1.car.CarWrapper
    
    • 控制台输出
    包装类前置执行
    hi, 我是宝马车
    包装类后置执行
    

    从输出结果来看, 很明显在我根据(name="bwm"), 去获取Extension实例的时候,实际获取到的是CarWrapper类, 只不过BWMCar是作为一个代理对象传入到CarWrapper中。

    • 源码参考
    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    if (CollectionUtils.isNotEmpty(wrapperClasses)) {
        for (Class<?> wrapperClass : wrapperClasses) {
            // 获取CarWrapper类的有参构造起, 并将instance作为构造参数返回新的包装类。
            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }
    }
    

    当前方式可以获取到包装后的实例对象, 但是此种方法还是不够灵活。 Dubbo 中还有一种自适应的机制, 可以根据请求参数动态的获取拓展类进行调用, 通过策略+装饰器的模式实现更加完善的代理

    Dubbo 自适应机制

    第一次听到自适应机制难免有些陌生, 扩展的自适应实例其实就是一个Extension的代理,它实现了扩展点接口。在调用扩展点的接口方法时,会根据实际的参数来决定要使用哪个扩展。

    • @Adaptive

      @Adaptive注解是作用在做自适应扩展点的注解类, 可以作用在类上和方法上

      • 当@Adaptive标注在类上时,在调用getAdaptiveExtension方法时,直接返回该类,表示代理类由手工实现,并不需要Dubbo自动生成实现类。 可以参考 AdaptiveCompiler 和 AdaptiveExtensionFactory 实现
      • 当@Adaptive标注在接口的方法上时, 表明调用该方法可以通过URL参数动态调用扩展实现类的对应方法。
      • 被@Adaptive修饰得方法得参数 必须满足参数中有一个是URL类型,或者有至少一个参数有一个公共的返回URL的get方法
      • 在调用动态生成代理类的非@Adaptive方法是, 默认会抛出UnsupportedOperationException异常
    • 代码示例

      说明: @Adaptive标注在类上的逻辑相当简单。 这里就不赘述了。 直接以标注在方法上的例子为例

      @SPI
      public interface Car {
            
            //动态生成代理类接口
          @Adaptive
          void test(CarBrand brand);
      }
      
      //自适应节点, 获取org.apache.dubbo.common.URL参数的接口
      public interface CustomerAdaptiveNode {
          URL getUrl();
      }
      
      //车品牌
      public interface CarBrand extends CustomerAdaptiveNode {
      
          CarBrand BWM = getCarBrand("bwm");
          CarBrand AUDI = getCarBrand("audi");
          CarBrand BENZ = getCarBrand("benz");
      
          static CarBrand getCarBrand(String brandName){
              return new CarBrand() {
                  @Override
                  public URL getUrl() {
                      URL url = new URL(null, null, 0);
                      url = url.addParameter("car", brandName);
                      return url;
                  }
              };
          }
      }
      
      //宝马车
      public class BWMCar implements Car {
      
          @Override
          public void test(CarBrand brand) {
              System.out.println("hi, 我是宝马车");
          }
      }
      //..... 此处省略 奔驰车和奥迪车源码
      
      
      //Main方法
      public static void main(String[] args) {
              ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
      
              //Car car = extensionLoader.getExtension("bwm");
                    // 获取自适应代理实例
              Car car = extensionLoader.getAdaptiveExtension();
                    //通过车品牌调用接口
              car.test(CarBrand.BENZ);
              car.test(CarBrand.BWM);
              car.test(CarBrand.AUDI);
      }
      
    • 控制台输出
    hi, 我是奔驰车
    hi,我是宝马车
    hi, 我是奥迪车
    

    可以看到,我们在调用同一个实例的同一个方法, 我们根据不同的CarBrand 参数实现了自适应的去调用对应扩展实例的方法。

    • 源码参考

      public T getAdaptiveExtension() {
            // 一如既往的缓存套路
          Object instance = cachedAdaptiveInstance.get();
          if (instance == null) {
              if (createAdaptiveInstanceError != null) {
                  throw new IllegalStateException("Failed to create adaptive instance: " +
                          createAdaptiveInstanceError.toString(),
                          createAdaptiveInstanceError);
              }
                    
              synchronized (cachedAdaptiveInstance) {
                  instance = cachedAdaptiveInstance.get();
                  if (instance == null) {
                      try {
                            // 创建自适应实例
                          instance = createAdaptiveExtension();
                          cachedAdaptiveInstance.set(instance);
                      } catch (Throwable t) {
                          createAdaptiveInstanceError = t;
                          throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                      }
                  }
              }
          }
          return (T) instance;
      }
      
      private T createAdaptiveExtension() {
              try {
                    // 获取自适应扩展类Class, 调用newInstance方法进行实例化,然后进行注入
                  return injectExtension((T) getAdaptiveExtensionClass().newInstance());
              } catch (Exception e) {
                  throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
              }
      }
      
      private Class<?> getAdaptiveExtensionClass() {
                    // 眼熟的初始加载加载扩展累信息
              getExtensionClasses();
                    // 判断是否有标注在类上的@Adaptive 扩展类
              if (cachedAdaptiveClass != null) {
                  return cachedAdaptiveClass;
              }
                    //动态生成自适应扩展累
              return cachedAdaptiveClass = createAdaptiveExtensionClass();
      }
      
      private Class<?> createAdaptiveExtensionClass() {
                    // 自适应扩展累代码生成器 生成源代码,
                    // 此处不在深入生成代码原理, 有兴趣的小伙伴自行研究, 下面会贴出生成好的代码
              String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
                    //获取classLoader
              ClassLoader classLoader = findClassLoader();
                    // 获取编译器
              org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
                    // 将源代码进行编译生成Class 对象
              return compiler.compile(code, classLoader);
      }
      
    • 自适应扩展累源码

      package com.wgt.dubbo.samples.spi.demo1.car;
      
      import org.apache.dubbo.common.extension.ExtensionLoader;
      
      public class Car$Adaptive implements com.wgt.dubbo.samples.spi.demo1.car.Car {
          public void test(com.wgt.dubbo.samples.spi.demo1.brand.CarBrand arg0) {
            if (arg0 == null)
                throw new IllegalArgumentException("com.wgt.dubbo.samples.spi.demo1.brand.CarBrand argument == null");
              
            if (arg0.getUrl() == null)
                throw new IllegalArgumentException("com.wgt.dubbo.samples.spi.demo1.brand.CarBrand argument getUrl() == null");
              
            org.apache.dubbo.common.URL url = arg0.getUrl();
            String extName = url.getParameter("car", "bwm");
            if (extName == null)
                throw new IllegalStateException("Failed to get extension (com.wgt.dubbo.samples.spi.demo1.car.Car) name from url (" + url.toString() + ") use keys([car])");
            
            com.wgt.dubbo.samples.spi.demo1.car.Car extension = (com.wgt.dubbo.samples.spi.demo1.car.Car)       ExtensionLoader.getExtensionLoader(com.wgt.dubbo.samples.spi.demo1.car.Car.class).getExtension(extName);
            extension.test(arg0);
          }
      }
      

      从以上的源代码可以看出, 所谓的根据参数自适应,其实就是通过URL对象中的parameter 参数去获取对应的扩展实现。 获取的扩展的方式同样是通过策略模式进行实现。 不过此种方式可以确保在运行时才能确定具体执行的扩展累,对于SPI机制的灵活性来说非常的有意义的。

    Dubbo @Activate

    ​ 相较于前面的内容,都是根据一些特定参数获取具体的扩展类, 但其实除此之外, 我们还会有同时用到多个扩展类,最常见的就是Dubbo 的Filter 机制,当我们需要在执行rpc调用的前后做一个操作时, 我们就可以通过实现Filter接口,并将其配置到org.apache.dubbo.rpc.Filter下。 但是我们在一次rpc调用时可能会有多个filter, 那么如何决定每个filter 的作用域,以及执行顺序呢?

    此时@Activate的作用的出现了,我们可以通过group, 和value 两者来决定是否此次的调用启用对应拦截器,并通过order 排序决定执行顺序。

    • 源码参考
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface Activate {
        /**
         *  根据分组 
         *  例如 
         *      CommonConstants.PROVIDER
         *      CommonConstants.CONSUMER
         * @return
         */
        String[] group() default {};
            
        /**
         *  根据org.apache.dubbo.common.URL 中的parameters 参数决定是否启用
         * @return
         */
        String[] value() default {};
    
        @Deprecated
        String[] before() default {};
    
        @Deprecated
        String[] after() default {};
        
        /**
         * Filter 顺序
         * @return
         */
        int order() default 0;
    }
    
    • 拦截器获取源码逻辑
      public List<T> getActivateExtension(URL url, String[] values, String group) {
              List<T> activateExtensions = new ArrayList<>();
              List<String> names = values == null ? new ArrayList<>(0) : asList(values);
              if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
                  getExtensionClasses();
                  for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                      String name = entry.getKey();
                      Object activate = entry.getValue();
      
                      String[] activateGroup, activateValue;
      
                      if (activate instanceof Activate) {
                          activateGroup = ((Activate) activate).group();
                          activateValue = ((Activate) activate).value();
                      } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                          activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                          activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                      } else {
                          continue;
                      }
                      if (isMatchGroup(group, activateGroup)
                              && !names.contains(name)
                              && !names.contains(REMOVE_VALUE_PREFIX + name)
                              && isActive(activateValue, url)) {
                          activateExtensions.add(getExtension(name));
                      }
                  }
                  activateExtensions.sort(ActivateComparator.COMPARATOR);
              }
              List<T> loadedExtensions = new ArrayList<>();
              for (int i = 0; i < names.size(); i++) {
                  String name = names.get(i);
                  if (!name.startsWith(REMOVE_VALUE_PREFIX)
                          && !names.contains(REMOVE_VALUE_PREFIX + name)) {
                      if (DEFAULT_KEY.equals(name)) {
                          if (!loadedExtensions.isEmpty()) {
                              activateExtensions.addAll(0, loadedExtensions);
                              loadedExtensions.clear();
                          }
                      } else {
                          loadedExtensions.add(getExtension(name));
                      }
                  }
              }
              if (!loadedExtensions.isEmpty()) {
                  activateExtensions.addAll(loadedExtensions);
              }
              return activateExtensions;
          }
    

    相关文章

      网友评论

          本文标题:逐步深入 Dubbo SPI 原理

          本文链接:https://www.haomeiwen.com/subject/rerpjktx.html