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