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实现。

 

posted on 2020-06-02 00:05  了了在小  阅读(536)  评论(0编辑  收藏  举报

导航