• Dubbo源码手记-SPI机制之AOP


      Dubbo作为目前业界首屈一指的RPC框架,其根本之一在于其强大的自适应扩展机制。谈到Dubbo的自适应扩展机制,就离不开一个重要的组件SPI。SPI实现两大重要的架构基础能力。其中之一就是AOP,这个能力为Dubbo的Inverker调用链提供了稳定的生态。来上Demo:

    比方说,我们现在有Car这样一个接口:(这里SPI注解是启动DubboSpi机制所必须的)

    1 @SPI
    2 public interface Car {
    3     void run();
    4 }

    对应奥迪、奔驰两个实现类:

    1 public class Audi implements Car {
    2     @Override
    3     public void run() {
    4         System.out.println("Audi Run");
    5     }
    6 }
    1 public class Benz implements Car {
    2     @Override
    3     public void run() {
    4         System.out.println("Benz Run");
    5     }
    6 }

    对应的/resources/META-INF/dubbo/internal/com.luban.dubbo_spi.Car的配置为:

    1 audi=com.luban.dubbo_spi.car.Audi
    2 benz=com.luban.dubbo_spi.car.Benz

    测试类:

    1 ExtensionLoader<Car> carLoader = ExtensionLoader.getExtensionLoader(Car.class);
    2 Car benz = carLoader.getExtension("benz");
    3 benz.run();
    4 Car audi = carLoader.getExtension("audi");
    5 audi.run();

    打印结果:

    Benz Run
    Audi Run

    如果说我们现在要对所有的Car实现类进行统一的前置功能增强、后置功能增强。也就是说增加若干个个切面。比方说我们增加,点火/熄火、启动检查/熄火检查 两个切面。

     1 public class CarFireWrapper implements Car {
     2     private Car car;
     3     // 这里的构造器是必须的,否则无法加入该切面
     4     public CarFireWrapper(Car car) {
     5         this.car = car;
     6     }
     7     @Override
     8     public void run() {
     9         System.out.println("点火");
    10         car.run();
    11         System.out.println("熄火");
    12     }
    13 }
     1 public class CarCheckWrapper implements Car {
     2     private Car car;
     3     public CarCheckWrapper(Car car) {
     4         this.car = car;
     5     }
     6     @Override
     7     public void run() {
     8         System.out.println("电子控制系统启动检查");
     9         car.run();
    10         System.out.println("电子控制系统关闭检查");
    11     }
    12 }

    并添加到com.luban.dubbo_spi.Car:

    audi=com.luban.dubbo_spi.car.Audi
    benz=com.luban.dubbo_spi.car.Benz
    com.luban.dubbo_spi.car.CarFireWrapper
    com.luban.dubbo_spi.car.CarCheckWrapper

    重新执行测试方法:

    点火
    电子控制系统启动检查
    Benz Run
    电子控制系统关闭检查
    熄火
    ----
    点火
    电子控制系统启动检查
    Audi Run
    电子控制系统关闭检查
    熄火

    如此,切面业务已经加入进来了。关于org.apache.dubbo.common.extension.ExtensionLoader#objectFactory属性的初始化,这里先不讲。来上Dubbo源代码,看下com.luban.dubbo_spi.Car的读取以及对象生成:

    以上边的测试用例为例,首先在执行这一行 carLoader..getExtension("benz") 代码的时候,因为首次调用,所以Car接口所有的实现类尚未被Dubbo缓存,所以会调用到getExtension方法的第18行。

     1 public T getExtension(String name) {
     2         if (StringUtils.isEmpty(name)) {
     3             throw new IllegalArgumentException("Extension name == null");
     4         }
     5         if ("true".equals(name)) {
     6             return getDefaultExtension();
     7         }
     8         Holder<Object> holder = cachedInstances.get(name);
     9         if (holder == null) {
    10             cachedInstances.putIfAbsent(name, new Holder<Object>());
    11             holder = cachedInstances.get(name);
    12         }
    13         Object instance = holder.get();
    14         if (instance == null) {
    15             synchronized (holder) {
    16                 instance = holder.get();
    17                 if (instance == null) {
    18                     instance = createExtension(name);
    19                     holder.set(instance);
    20                 }
    21             }
    22         }
    23         return (T) instance;
    24     }

    即,读取com.luban.dubbo_spi.Car文件来创建Car的实现类。

    1 private T createExtension(String name) {
    2         Class<?> clazz = getExtensionClasses().get(name);
    3 ...

    我们直接取看createExtension方法的第二行,

     1 private Map<String, Class<?>> loadExtensionClasses() {
     2         final SPI defaultAnnotation = type.getAnnotation(SPI.class);
     3         // SPI后面的值就是默认的实现的类,只能指定一个实现类
     4         if (defaultAnnotation != null) {
     5             String value = defaultAnnotation.value();
     6             if ((value = value.trim()).length() > 0) {
     7                 String[] names = NAME_SEPARATOR.split(value);
     8                 if (names.length > 1) {
     9                     throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
    10                             + ": " + Arrays.toString(names));
    11                 }
    12                 if (names.length == 1) {
    13                     cachedDefaultName = names[0];
    14                 }
    15             }
    16         }
    17 
    18         // 是一个map,key是别名,value是具体的实现类
    19         // 会从不同的地方寻找接口的所有实现类,这就是扩展的实现
    20         // 主要会从三个地方找,1. dubbo内部提供的
    21         // META-INF/dubbo/
    22         // META-INF/dubbo/internal/
    23         // META-INF/services/
    24         Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    25         loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    26         loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    27         loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    28         loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    29         loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    30         loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    31         return extensionClasses;
    32 }

    从loadExtentsionClasses方法的第二行看一看出,Car接口的SPI注解支持设定一个默认的实现,用字符串标识。同com.luban.dubbo_spi.Car文件的key值。从上边的逻辑来看,第25行到第30行就是将不同位置的配置文件读取成Car的实现类,或者切面类。而且放到一个Map里边,自然而然能想到,这里是存在不同位置配置文件的覆盖关系的,即配置文件的位置具有优先级。即:

    META-INF/services/ > META-INF/dubbo/internal/ > META-INF/dubbo/

    上边的方法进一步会通过反射创建Car的实现类以及切面类,进而进入下边逻辑:

     1 private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
     2         // 是不是接口的实现类
     3         if (!type.isAssignableFrom(clazz)) {
     4             throw new IllegalStateException("Error when load extension class(interface: " +
     5                     type + ", class line: " + clazz.getName() + "), class "
     6                     + clazz.getName() + "is not subtype of interface.");
     7         }
     8         // 实现类上是否有Adaptive注解,这段代码的意思就是一个接口的实现类中只有一个Adaptive
     9         if (clazz.isAnnotationPresent(Adaptive.class)) {
    10             if (cachedAdaptiveClass == null) {
    11                 cachedAdaptiveClass = clazz;
    12             } else if (!cachedAdaptiveClass.equals(clazz)) {
    13                 throw new IllegalStateException("More than 1 adaptive class found: "
    14                         + cachedAdaptiveClass.getClass().getName()
    15                         + ", " + clazz.getClass().getName());
    16             }
    17         } else if (isWrapperClass(clazz)) { // 实现类是不是wrapper类,判断逻辑是这个实现类的构造方法的参数是不是有且仅有一个参数,且参数类型是接口类型
    18             // wrapper类可以有多个
    19             Set<Class<?>> wrappers = cachedWrapperClasses;
    20             if (wrappers == null) {
    21                 cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
    22                 wrappers = cachedWrapperClasses;
    23             }
    24             wrappers.add(clazz);
    25         } else {
    26             clazz.getConstructor();
    27             if (StringUtils.isEmpty(name)) {
    28                 // 如果在文件里没有配名字,可以去看实现类上是否有Extension注解,可以取这个注解的值作为名字
    29                 // 如果没有Extension这个注解,则取实现类的simple名,并进行简化,比如接口为CarService, 实现类为RedCarService, 那么名字则为red
    30                 name = findAnnotationName(clazz);
    31                 if (name.length() == 0) {
    32                     throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
    33                 }
    34             }
    35             // 如果配置的名字使用逗号隔开的
    36             String[] names = NAME_SEPARATOR.split(name);
    37             if (names != null && names.length > 0) {
    38                 // 这里找的是Activate注解,不是Adaptive
    39                 // Activate表示激活,如果实现类上配置了Activate注解,这里会先缓存,getActivateExtension()方法
    40                 Activate activate = clazz.getAnnotation(Activate.class);
    41                 if (activate != null) {
    42                     cachedActivates.put(names[0], activate);
    43                 } else {
    44                     // support com.alibaba.dubbo.common.extension.Activate
    45                     com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
    46                     if (oldActivate != null) {
    47                         cachedActivates.put(names[0], oldActivate);
    48                     }
    49                 }
    50                 for (String n : names) {
    51                     // 配了多个名字cachedName也只会缓存一个
    52                     if (!cachedNames.containsKey(clazz)) {
    53                         cachedNames.put(clazz, n);
    54                     }
    55                     // 多个名字会产生多条记录
    56                     Class<?> c = extensionClasses.get(n);
    57                     if (c == null) {
    58                         extensionClasses.put(n, clazz);
    59                     } else if (c != clazz) {
    60                         throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
    61                     }
    62                 }
    63             }
    64         }
    65     }

    上边的第17行判断逻辑,就是是否有特定的构造器:

    1 private boolean isWrapperClass(Class<?> clazz) {
    2         try {
    3             clazz.getConstructor(type);
    4             return true;
    5         } catch (NoSuchMethodException e) {
    6             return false;
    7         }
    8     }

    上边的代码就是将实现类与切面类进行缓存,供下一步切面组装:

    1 T instance = (T) EXTENSION_INSTANCES.get(clazz);
    2 Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    3 if (CollectionUtils.isNotEmpty(wrapperClasses)) {
    4     for (Class<?> wrapperClass : wrapperClasses) {
    5         instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
    6     }
    7 }
    8 return instance;

    这段逻辑的第一行会拿到Car的实现类,然后循环切面类wrapperClasses,将实现类注入切面的构造器,并返回切面,如果还有切面,则将上一次返回的切面继续注入下一个切面,并返回下一个切面。知道循环完所有切面。那么,很明显,第8行将会返回最外层的一个切面对象,而非Car的业务实现类。

     因为点火切面在com.luban.dubbo_spi.Car文件的第一个放着,所以 就对应最外层的切面,一次类推。

    今天的Dubbo的SPI的AOP实现就记录到这里,不难发现,Dubbo实现的AOP非常轻量级,并且精巧,下一节,将会探讨Dubbo SPI的IOC实现。

  • 相关阅读:
    X ASM 磁盘大小限制
    X Oracle 12c Non CDB 数据库 切换成 CDB 测试
    X Scalable Sequences(自适应序列)
    X RMAN新特性- RMAN duplicate PDB into existing CDB
    X Oracle Database 19c中的自动索引
    X 12c中在 RMAN 中提供了表级别恢复 RECOVER TABLE
    X 12c中对于表分区维护的增强
    CF1019C Sergey's problem
    洛谷P6140&P2870 [USACO07NOV]Best Cow Line S
    CF471D MUH and Cube Walls
  • 原文地址:https://www.cnblogs.com/UYGHYTYH/p/13028591.html
Copyright © 2020-2023  润新知