一、什么是SPI
1.1.JDK SPI示例
1 public interface Robot { 2 void sayHello(); 3 } 4 5 public class OptimusPrime implements Robot { 6 7 @Override 8 public void sayHello() { 9 System.out.println("Hello, I am Optimus Prime."); 10 } 11 } 12 13 public class Bumblebee implements Robot { 14 15 @Override 16 public void sayHello() { 17 System.out.println("Hello, I am Bumblebee."); 18 } 19 }
接下来,在项目(以一个典型的maven项目为例)的“resources/META-INF/services”路径下创建一个文件,名称为Robot的全限定名“org.apache.spi.Robot”。文件内容如下:
org.apache.spi.OptimusPrime org.apache.spi.Bumblebee
编写测试代码,运行之后可以看到两个实现类被加载并调用了sayHello方法(调用结果演示略)。
public class JavaSPITest { @Test public void sayHello() throws Exception { ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class); System.out.println("Java SPI"); serviceLoader.forEach(Robot::sayHello); } }
1.2.Dubbo SPI示例
//使用SPI注解标注的接口 @SPI public interface Robot { void sayHello(); }
接下来,在项目(以一个典型的maven项目为例)的“resources/META-INF/dubbo”路径下创建一个文件,名称为Robot的全限定名“org.apache.spi.Robot”。文件内容如下:
optimusPrime=org.apache.spi.OptimusPrime bumblebee=org.apache.spi.Bumblebee
//测试类 public class DubboSPITest { @Test public void sayHello() throws Exception { //传入一个标注有@SPI的接口Class,通过getExtensionLoader获取该SPI接口的ExtensionLoader实例 ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); //传入要获取的实现类的name(META-INF/dubbo/org.apache.spi.Robot文件下的name),获取实现类实例 Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); } }
通过与JDK SPI示例的比较,发现Dubbo SPI与JDK SPI最大的不同就是Dubbo SPI通过键值对的方式进行配置。这样最大的好处是可以按需加载指定的实现类(通过name指定)。下面就让我们以本例中getExtensionLoader与getExtension两个方法作为引子,分析Dubbo SPI的实现源码。
二、getExtensionLoader
/**************************************** 相关字段 ****************************************/ //ExtensionLoader对应的SPI接口类型 private final Class<?> type; //ExtensionLoader对应的ExtensionFactory实例 private final ExtensionFactory objectFactory; //ExtensionLoader全局缓存,key为SPI接口类型,value为相应的ExtensionLoader实例 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(); /**************************************** 相关方法 ****************************************/ /** * 静态方法,根据SPI接口类型获取相应的ExtensionLoader */ public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //判断type是否为空、是否是接口类型、是否具有@SPI注解 if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension..."); } //从ExtensionLoader的缓存中根据SPI接口类型获取对应的ExtensionLoader实例 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); //若缓存没有该实例,则new一个,并且存放入缓存,key为type if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } /** * 私有构造器,对调用者而言,只能通过getExtensionLoader方法获取ExtensionLoader实例 */ private ExtensionLoader(Class<?> type) { //保存该ExtensionLoader的SPI接口类型信息 this.type = type; //若SPI接口类型为ExtensionFactory,则不设置字段ExtensionFactory, //否则需要设置一个ExtensionFactory。具体的获取逻辑我们后面会讲到 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
三、getExtension
/**************************************** 实例缓存相关字段 ****************************************/ //全部SPI接口的扩展类实例缓存,key为扩展类Class(每一个SPI接口的每一个实现类的Class都不同) //value为对应的扩展类实例。注意该缓存要与另外一个类似的缓存cachedInstances区分开。 private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(); //每个ExtensionLoader对应的SPI接口的扩展类实例缓存, //key为扩展类的name,value为Holder对象,其持有/维护扩展类的实例。 private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); /**************************************** Class缓存相关字段 ****************************************/ //每个ExtensionLoader对应的SPI接口的扩展类Class缓存,key为扩展类的name,value为扩展类Class private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); //SPI接口的扩展类中具有@Adaptive注解的扩展类Class缓存 private volatile Class<?> cachedAdaptiveClass = null; //SPI接口的扩展类中被判定为具有包装功能的扩展类Class缓存 private Set<Class<?>> cachedWrapperClasses; //SPI接口的扩展类中具有@Activate注解的缓存,key是扩展类names[0],value为@Activate的Class private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); /**************************************** name缓存相关字段 ****************************************/ //SPI接口的扩展类的name缓存,key为扩展类的Class,value为扩展类的names[0] private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); //SPI接口的默认扩展类的name private String cachedDefaultName; /**************************************** 其他相关字段 ****************************************/ //ExtensionLoader对应的SPI接口Class private final Class<?> type; /**************************************** 相关方法 ****************************************/ /** * 获取ExtensionLoader对应的SPI接口的扩展类实例 */ public T getExtension(String name) { //检查扩展类的name if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } //如果传入的name值为true,则获取默认的SPI接口扩展类实例 if ("true".equals(name)) { return getDefaultExtension(); } //getOrCreateHolder方法比较简单,它从缓存“cachedInstances”中获取该name对应的Holder实例, //Holder是一个“持有类”,其可能持有扩展类实例 Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); //如果未获取到实例,在监视器锁中进行第二次获取与创建 if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { //创建name对应的扩展类实例,并缓存到cachedInstances中 instance = createExtension(name); holder.set(instance); } } } return (T) instance; } /** * 获取默认的扩展类实例 */ public T getDefaultExtension() { //调用getExtensionClasses方法获取SPI接口配置的扩展类信息,返回结果为一个Map<String, Class>, //其中key为扩展类name,value为该name对应的扩展类Class。 getExtensionClasses(); //如果SPI接口默认扩展类name为空或者为true,则默认的扩展类实例为null if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } //否则调用getExtension根据默认的扩展类name去获取实例 return getExtension(cachedDefaultName); } /** * 创建一个扩展类的实例 */ private T createExtension(String name) { //调用getExtensionClasses方法,从返回结果中获取name对应的扩展类Class,如果Class为null,则抛出异常 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { //从缓存“EXTENSION_INSTANCES”中根据扩展类Class获取实例 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { //缓存未命中则通过反射创建一个实例,并存入缓存 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //向这个扩展类实例注入其所需要的属性(以setXXX为准),该方法比较复杂,我们后面会进行分析 injectExtension(instance); //获取SPI接口扩展类中具有包装功能的扩展类Class缓存,然后对instance进行层层包装(装饰器模式), //对每次包装出来的新实例进行属性注入,全部包装完成后让instance指向最终的包装结果实例 Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance ... couldn't be instantiated: "); } } /** * 获取SPI接口配置的扩展类信息 */ private Map<String, Class<?>> getExtensionClasses() { //从缓存“cachedClasses”中获取已加载的扩展类 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { //缓存为空,调用loadExtensionClasses方法加载SPI接口配置的扩展类信息,并缓存结果 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } /** * 加载SPI接口配置的扩展类信息 */ private Map<String, Class<?>> loadExtensionClasses() { //获取SPI接口默认扩展类的name并进行缓存 cacheDefaultExtensionName(); //读取并解析SPI接口的配置文件,去几个固定的目录下读取 //(1):META-INF/services/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba) //(2):META-INF/dubbo/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba) //(3):META-INF/dubbo/internal/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba) Map<String, Class<?>> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); 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; } /** * 获取SPI接口默认扩展类的name并进行缓存 */ private void cacheDefaultExtensionName() { //获取ExtensionLoader对应的SPI接口上的SPI注解 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { //获取SPI注解的value值并进行校验 String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("More than 1 default extension name ..."); } if (names.length == 1) { //将其作为SPI接口默认扩展类的name并进行缓存 cachedDefaultName = names[0]; } } } } /** * 加载SPI接口的配置文件 */ private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration<java.net.URL> urls; //获取并使用类加载器去加载文件(同名文件进行内容合并) ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { //遍历获取到的URL,并调用loadResource去加载资源 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: ..."); } } /** * 在loadDirectory的基础上,对每个文件中的内容进行加载与解析 */ private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { //按行读取,每一行先去掉#号后面的内容(#号后面的内容为注释) final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { String name = null; //按“=”号截取name和扩展类的全限定类名,可以看出name是可以没有的 int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } //如果line,即扩展类全限定类名不为空,通过Class.forName获取其Class, //然后调用loadClass继续加载该Class if (line.length() > 0) { loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load ..."); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: ..."); } } /** * 在loadResource的基础上,对文件中的每行获取到的Class进行分析,并进行缓存 */ private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { //判断配置的扩展类是否为指定SPI接口的实现类 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading ... is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) { //若扩展类有@Adaptive注解,将这个Class存入缓存“cachedAdaptiveClass” //注意:一个SPI接口配置文件中,只能配置一个有@Adaptive注解的扩展类 cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { //若扩展类具有clazz.getConstructor(type)这样的构造器,则认为其是一个具有包装功能的扩展类, //并将其存入缓存“cachedWrapperClasses”,一个SPI接口可以配置多个用于包装的扩展类 cacheWrapperClass(clazz); } else { //进入到这个分支,表示该Class只是一个普通的SPI接口扩展类。 //判断该扩展类是否具有无参构造器 clazz.getConstructor(); //如果该扩展类在SPI接口配置文件中未定义name,则判断扩展类是否具有@Extension注解, //如果有,则以@Extension的value值作为name;否则以扩展类的SimpleName作为name, //并且,如果扩展类的SimpleName以SPI接口的SimpleName作为后缀结尾,则name需要去掉该后缀 if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class ..."); } } //对name按照","进行分割 String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { //names不为空 //若扩展类具有@Activate注解,则使用names数组的第一个元素作为key,@Activate的Class为value //将映射关系存入缓存“cachedActivates” cacheActivateClass(clazz, names[0]); for (String n : names) { //将该扩展类的name和Class存入缓存“cachedNames”,name保持为names[0] cacheName(clazz, n); //将该扩展类的name和Class存入方法参数extensionClasses saveInExtensionClass(extensionClasses, clazz, name); } } } }
通过以上的源码分析,我们了解到getExtension方法获取一个SPI接口的扩展类实例的流程分为解析配置文件、加载并缓存扩展类、创建并加工扩展类实例几个步骤。
在得到一个最终可用的扩展类实例前,该实例会进行属性注入与层层包装,这些行为在官网上被成为扩展点特性,这里我们把它称之为“扩展类特性”。官方给出了4个特性,分别为“扩展点自动包装”、“扩展点自动装配”、“扩展点自适应”以及“扩展点自动激活”。在下面的其余章节中,我们将重点来研究这几个扩展点特性。
四、扩展点自适应
4.1.什么是自适应扩展类
/** * 一个模拟的SPI接口 */ @SPI public interface WheelMaker { Wheel makeWheel(URL url); } /** * SPI接口的自适应扩展类 */ public class AdaptiveWheelMaker implements WheelMaker { //自适应扩展类该方法的逻辑为通过URL中的参数,动态的获取真正需要执行的SPI接口扩展类 public Wheel makeWheel(URL url) { if (url == null) { throw new IllegalArgumentException("url == null"); } // 1.从URL中获取WheelMaker名称 String wheelMakerName = url.getParameter("Wheel.maker"); if (wheelMakerName == null) { throw new IllegalArgumentException("wheelMakerName == null"); } // 2.通过SPI加载具体的WheelMaker WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName); // 3.调用目标方法 return wheelMaker.makeWheel(URL url); } } /** * 一个模拟的SPI接口 */ @SPI public interface CarMaker { Car makeCar(URL url); } /** * 汽车制造者SPI接口的扩展类 */ public class RaceCarMaker implements CarMaker { WheelMaker wheelMaker; //在injectExtension方法中会通过setter注入AdaptiveWheelMaker //目前我们只知道自动装配行为的结果,原因会在“扩展点自动装配”小节分析 public setWheelMaker(WheelMaker wheelMaker) { this.wheelMaker = wheelMaker; } //实现的方法 public Car makeCar(URL url) { //实际调用AdaptiveWheelMaker的makeWheel方法,获得当前运行环境参数url下需要使用的Wheel扩展类 Wheel wheel = wheelMaker.makeWheel(url); return new RaceCar(wheel, ...); } }
假设运行时传入这样一个url参数“dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker”,那么RaceCar最终的wheel为扩展类“MichelinWheelMaker”制造出来的轮胎。
4.2.获取自适应扩展类实例
/**************************************** 相关字段 ****************************************/ //缓存的自适应扩展类实例 private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); //SPI接口的扩展类中具有@Adaptive注解的扩展类Class缓存 private volatile Class<?> cachedAdaptiveClass = null; //创建自适应扩展类实例的异常信息 private volatile Throwable createAdaptiveInstanceError; /**************************************** 相关方法 ****************************************/ /** * 获取自适应扩展类的实例 */ public T getAdaptiveExtension() { //从ExtensionLoader的缓存字段中获取数据,cachedAdaptiveInstance为一个Holder<Object>对象 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { //需要判断一下创建自适应扩展对象的Throwable缓存是否存在,如果存在,直接抛出 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("......"); } } } } else { throw new IllegalStateException("......"); } } return (T) instance; } /** * 创建自适应扩展类实例 */ private T createAdaptiveExtension() { try { //(1)调用getAdaptiveExtensionClass方法获取自适应扩展类的Class; //(2)通过newInstance实例化一个自适应扩展类的对象; //(3)调用injectExtension方法向自适应拓展类的实例中注入依赖,参考“扩展点自动装配”小节; return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("......"); } } /** * 获取自适应扩展类的Class */ private Class<?> getAdaptiveExtensionClass() { //获取该ExtensionLoader对应SPI接口配置的所有扩展类(参考“getExtensionClsses”小节) getExtensionClasses(); //检查具有@Adaptive注解的扩展类缓存,若缓存不为空,则直接返回缓存 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //如果SPI接口配置的所有扩展类都没有被@Adaptive标注,则创建自适应扩展类 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } /** * 创建自适应扩展类 */ private Class<?> createAdaptiveExtensionClass() { //通过AdaptiveClassCodeGenerator的generate方法创建自适应扩展代码 //该方法会检测SPI接口中是否有被@Adapative注解的方法,对于要生成自适应扩展类的SPI接口 //必须至少包含一个被@Adaptive注解的方法,否则会抛出异常 String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); //获取编译器实现类,一样是通过AdaptiveExtension进行获取,获取之后进行编译 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
通过getAdaptiveExtension方法的流程可以发现,要想获得一个SPI接口的自适应扩展类实例,有2种方式:
- 在SPI接口的配置文件中配置具有@Adaptive注解的扩展类,在执行解析SPI接口配置文件方法getExtensionClasses时,它会调用loadClass方法,该方法判断扩展类是否具有@Adaptive注解,如果有,则将该类Class缓存到ExtensionLoader的字段“cachedAdaptiveClass”中,然后直接实例化该Class的实例并进行自动装配;
- 如果未配置@Adaptive修饰的扩展类,则Dubbo会使用字节码技术创建一个自适应扩展类,前提是SPI接口上至少有一个被@Adaptive注解的方法;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { String[] value() default {}; }
可以看到,@Adaptive注解既可以使用在类上,又可以使用在方法上。其包含一个字符串数组的属性,在通过字节码技术创建自适应扩展类时,该属性参与到生成逻辑中,具体的创建逻辑我们马上通过下一节来了解。
4.3.关于创建自适应扩展类
4.3.1.代码拼接
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
首先传入SPI接口的Class与默认的扩展类名称(即@SPI注解的value值)创建一个AdaptiveClassCodeGenerator实例,之后调用generate方法生成代码,AdaptiveClassCodeGenerator相对应的源码如下:
/**************************************** 相关字段 ****************************************/ //SPI接口类型 private final Class<?> type; //SPI接口默认的扩展类名称,即@SPI注解的value属性值 private String defaultExtName; /**************************************** 相关方法 ****************************************/ /** * 可以看到,构造器并未做太多的事情,只是简单的将传入的参数为成员变量赋值 */ public AdaptiveClassCodeGenerator(Class<?> type, String defaultExtName) { this.type = type; this.defaultExtName = defaultExtName; } /** * 创建自适应扩展类的代码串 */ public String generate() { //遍历SPI接口是否具有@Adaptive注解修饰的方法,如果没有则抛出异常 if (!hasAdaptiveMethod()) { throw new IllegalStateException("......"); } //按照一个JAVA类的代码组成顺序,拼接代码字符串 StringBuilder code = new StringBuilder(); //拼接包信息字符串: "package" + SPI接口所在的包 code.append(generatePackageInfo()); //拼接import字符串: "import" + ExtensionLoader的全限定名 code.append(generateImports()); //拼接类开头字符串: "public class" + SPI接口简单名 + "$Adaptive implements" + SPI接口全限定名 + "{" //注意:使用全限定名的原因为import代码串中只导入ExtensionLoader的类,下同 code.append(generateClassDeclaration()); //拼接每一个方法字符串,逻辑比较复杂,在generateMethod方法源码中详细说明 Method[] methods = type.getMethods(); for (Method method : methods) { code.append(generateMethod(method)); } //拼接类结束字符串: "}" code.append("}"); return code.toString(); } /** * 检测该SPI接口是否具有@Adaptive修饰的方法 */ private boolean hasAdaptiveMethod() { return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class)); } /** * 生成自适应扩展类的方法代码串 */ private String generateMethod(Method method) { //获取方法返回值类型全限定名 String methodReturnType = method.getReturnType().getCanonicalName(); //获取方法名 String methodName = method.getName(); //获取方法内容,有无@Adaptive修饰的方法其方法内容不同,详细见下generateMethodContent源码 String methodContent = generateMethodContent(method); //获取方法参数列表,格式为"参数类型全限定名 arg0, 参数类型全限定名 arg1, ..." String methodArgs = generateMethodArguments(method); //获取方法异常抛出,格式为"throws 异常1全限定名, 异常2全限定名, ..." String methodThrows = generateMethodThrows(method); //获取一个方法代码串 return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); } /** * 生成自适应扩展类的方法体代码串 */ private String generateMethodContent(Method method) { Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); //判断当前要生成的方法是否有@Adaptive注解 if (adaptiveAnnotation == null) { //对于没有@Adaptive注解的方法,生成"throw new UnsupportedOperationException(...)"代码 return generateUnsupported(method); } else { //检查方法参数列表中是否有类型为"com.apache.dubbo.common.URL"的参数 //简单提一下,com.apache.dubbo.common.URL是dubbo框架中各组件的数据总线 int urlTypeIndex = getUrlTypeIndex(method); if (urlTypeIndex != -1) { //如果方法参数列表中有URL类型参数,则为该参数生成判断是否为null以及赋值的代码: //(generateUrlNullCheck方法比较简单,不进行详细的源码分析,这里只给出逻辑) //if (arg%d == null) throw new IllegalArgumentException("url == null"); //com.apache.dubbo.common.URL url = arg%d; //d的值为urlTypeIndex code.append(generateUrlNullCheck(urlTypeIndex)); } else { //如果方法参数列表中没有URL类型参数,则需要遍历参数列表中每一个参数的类型信息, //判断哪一个参数具有"public URL getXXX()"签名形式的方法,如果有则停止遍历且生成如下代码: //(generateUrlAssignmentIndirectly方法比较简单,不进行详细的源码分析,这里只给出逻辑) //if (arg%d == null) 备注:此处的d为具备"public URL getXXX()"方法的参数下标,从0开始 // throw new IllegalArgumentException("参数全限定名 + argument == null"); //if (arg%d.getter方法名() == null) // throw new IllegalArgumentException(参数全限定名 + argument getUrl() == null); //com.apache.dubbo.common.URL url = arg%d.getter方法名(); code.append(generateUrlAssignmentIndirectly(method)); } //获取该方法的@Adaptive注解的属性值value(为一个String数组),这里列出处理逻辑 //如果属性值value为非空数组,直接获取数组内容即可; //如果value为空数组,则需将SPI接口的类名按照驼峰命名法进行检测,对每个驼峰进行分割并插入"."号, //然后转为小写,比如LoadBalance经过处理后,得到load.balance String[] value = getMethodAdaptiveValue(adaptiveAnnotation); //判断当前方法的参数列表中是否有类型为org.apache.dubbo.rpc.Invocation的参数 boolean hasInvocation = hasInvocationArgument(method); //为当前方法参数列表中第一个类型为org.apache.dubbo.rpc.Invocation的参数生成判null语句以及调用语句 //if (arg%d == null) throw new IllegalArgumentException("invocation == null"); //String methodName = arg%d.getMethodName(); //%d是org.apache.dubbo.rpc.Invocation类型的参数在参数列表中的下标 code.append(generateInvocationArgumentNullCheck(method)); //使用前面获取到的字符串数组value以及Invocation参数存在标识拼接获取扩展类extName的代码串, //这是自适应扩展类核心的代码,因为自适应扩展类的目的就是要根据当前运行参数, //判断应该获取SPI接口的哪一个扩展类,而ExtensionLoader.getExtension方法的参数就是这个extName //该方法逻辑比较复杂,判断分支较多,详细的分析在下面generateExtNameAssignment中 code.append(generateExtNameAssignment(value, hasInvocation)); //对上一步获取的局部变量extName拼接其判null代码 //if(extName == null) throw new IllegalStateException("Failed to get extension name from..."); code.append(generateExtNameNullCheck(value)); //生成获取extName对应扩展类实例的代码串,如下: //%s extension = (%<s)ExtensionLoader.getExtensionLoader(%s.class).getExtension(extName); //其中%s为成员变量type.getName,即SPI接口的Class的全限定名 code.append(generateExtensionAssignment()); //生成目标方法调用逻辑,被@Adaptive注解的方法,第一个任务是根据运行时参数获取对应的扩展类实例, //第二个任务就是调用这个实例的同名方法。生成的方法调用代码串格式为: //(1)如果该方法无返回值"extension.方法名(arg0, arg2, ..., argN);" //(2)如果该方法有返回值"return extension.方法名(arg0, arg1, ..., argN);" //其中extension为上一步生成的扩展类实例变量名,arg0、arg1就为当前@Adaptive注解方法的参数名 code.append(generateReturnAndInvocation(method)); } return code.toString(); } /** * 扩展名extName获取代码串 */ private String generateExtNameAssignment(String[] value, boolean hasInvocation) { String getNameCode = null; //反向遍历value,目的是生成从URL中获取拓展名的代码,生成的代码会赋值给getNameCode变量 for (int i = value.length - 1; i >= 0; --i) { //当遍历的元素是最后一个元素时 if (i == value.length - 1) { //若默认扩展名不空(即@SPI注解的value值不空) if (null != defaultExtName) { //由于protocol是URL的成员变量,可通过getProtocol方法获取,其他的则是从 //URL的成员变量parameters(一个Map<String, String>)中获取。 //因为获取方式不同,所以这里要判断value[i]是否为protocol if (!"protocol".equals(value[i])) { //需要判断一下hasInvocation标识(即当前方法是否有Invocation参数) if (hasInvocation) { //生成的代码功能等价于下面的代码: //url.getMethodParameter(methodName, value[i], defaultExtName) //注意,methodName是generateInvocationArgumentNullCheck生成的局部变量,下同 getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); } else { //生成的代码功能等价于下面的代码: //url.getParameter(value[i], defaultExtName) getNameCode = String.format("url.getParameter("%s", "%s")", value[i], defaultExtName); } } else { //生成的代码功能等价于下面代码: //( url.getProtocol() == null ? defaultExtName : url.getProtocol() ) getNameCode = String.format("( url.getProtocol() == null ? "%s" : url.getProtocol() )", defaultExtName); } //若默认扩展名为空 } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { //生成的代码格式同上,即 //url.getMethodParameter(methodName, value[i], defaultExtName) getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); } else { //生成的代码功能等价于:url.getParameter(value[i]) getNameCode = String.format("url.getParameter("%s")", value[i]); } } else { //生成的代码功能等价于:url.getProtocol() getNameCode = "url.getProtocol()"; } } //当遍历的元素不为最后一个时 } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { //生成的代码同上,即: //url.getMethodParameter(methodName, value[i], defaultExtName) getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); } else { //在上一次获取的getNameCode代码结果基础上,再次获取getNameCode,即一层层的获取值 //生成的代码功能等价于下面的代码: //url.getParameter(value[i], getNameCode) //以Transporter接口的connect方法为例,最终生成的代码如下: //url.getParameter("client", url.getParameter("transporter", "netty")) getNameCode = String.format("url.getParameter("%s", %s)", value[i], getNameCode); } } else { //在上一次获取的getNameCode代码结果基础上,再次获取getNameCode,即一层层的获取值 //生成的代码功能等价于下面的代码: //url.getProtocol() == null ? getNameCode : url.getProtocol() //以Protocol接口的connect方法为例,最终生成的代码如下: //url.getProtocol() == null ? "dubbo" : url.getProtocol() getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } //返回生成extName的代码:"String extName = getNameCode;" return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); }
4.3.2.代码编译
ClassLoader classLoader = findClassLoader(); //获取编译器实现类,一样是通过AdaptiveExtension进行获取,获取之后进行编译 //在这里具体获取Compiler的细节略去,最终获取到JavassistCompiler类的实例进行编译 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader);
对于这段代码,我们可以运用本小节学到知识,来分析一下Compiler接口的自适应扩展类是什么。
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler jdk=org.apache.dubbo.common.compiler.support.JdkCompiler javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
一共配置了3个扩展类,根据name的名称,可以肯定AdaptiveCompiler是具有@Adaptive注解的扩展类
@Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } }
根据4.2小节中的代码逻辑,在具有@Adaptive注解修饰的扩展类的前提下,自适应扩展类实例一定获取到这个被修饰的类实例,因此“ExtensionLoader.getExtensionLoader(....Compiler.class).getAdaptiveExtension();”这段代码获取到了一个AdaptiveCompiler的实例。AdaptiveCompiler的compile方法很简单,由于成员变量“DEFAULT_COMPILER”始终为null,其会调用ExtensionLoader的getDefaultExtension方法获取默认的扩展类实例,即@SPI接口注解value值作为name的实例,看看Compiler接口代码:
@SPI("javassist") public interface Compiler { Class<?> compile(String code, ClassLoader classLoader); }
其默认的扩展类的name为“javassist”,即类“org.apache.dubbo.common.compiler.support.JavassistCompiler”,这个类的具体compile方法不多赘述,就是校验代码字符串格式的正确性,并进行加载。
五、扩展点自动装配
/** * 自动装配扩展类实例 */ private T injectExtension(T instance) { try { //如果objectFactory不为空,则进行自动装配 if (objectFactory != null) { //遍历扩展类实例的所有方法 for (Method method : instance.getClass().getMethods()) { //检测方法是否以set开头、只有一个参数、访问修饰符为public if (isSetter(method)) { //对于有@DisableInject的注解的setter方法,不需要注入 if (method.getAnnotation(DisableInject.class) != null) { continue; } //获取setter方法的参数类型 Class<?> pt = method.getParameterTypes()[0]; //判断参数类型是否是原始类型(基本类型+String+基本类型的包装类+Date) if (ReflectUtils.isPrimitives(pt)) { continue; } try { //获取要注入属性名,比如setName方法中对应的属性名为name String property = getSetterProperty(method); //从objectFactory中根据属性名与属性的Class类型获取依赖对象, //获取到的是一个SPI接口的自适应扩展类的对象或者Spring环境下的一个bean, //objectFactory.getExtension方法的细节我们在ExtensionFactory中将详细讨论 Object object = objectFactory.getExtension(pt, property); if (object != null) { //调用setter方法进行注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method ..."); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
这几行简单的代码包含了很多信息:首先,ExtensionFactory自身也是一个SPI接口,其同样能够通过ExtensionLoader进行初始化。其次,当获取一个SPI接口的ExtensionLoader时,如果该SPI接口为ExtensionFactory,则不会设置objectFactory字段值,否则会通过getAdaptiveExtension方法拿到一个ExtensionFactory的实例并将字段objectFactory的值设置为它。
5.1.ExtensionFactory
@SPI public interface ExtensionFactory { <T> T getExtension(Class<T> type, String name); }
ExtensionFactory这个接口的继承树如下图所示:
它有3个实现类,分别是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory,相关的META-INFO配置文件内容如下:
#位于dubbo-common模块下的META-INFO/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件 adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory #位于dubbo-spring模块下的META-INF/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件 spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory
5.2.AdaptiveExtensionFactory
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { //维护了一组ExtensionFctory接口扩展类的实例 private final List<ExtensionFactory> factories; //构造方法,获取所有ExtensionFactory配置的扩展类实例 public AdaptiveExtensionFactory() { //加载ExtensionFactory对应的ExtensionLoader ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();、 //getSupportedExtensions方法用于获取ExtensionLoader的cachedClasses缓存的keySet //即SPI接口配置文件中name的Set集合 for (String name : loader.getSupportedExtensions()) { //根据name名字,调用getExtension方法获取对应扩展类的实例并缓存 list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } @Override public <T> T getExtension(Class<T> type, String name) { for (ExtensionFactory factory : factories) { //调用每一个ExtensionFactory实现类的getExtension方法,并返回第一个不为null的对象 T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } }
5.3.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); //getSupportedExtensions方法用于获取ExtensionLoader的cachedClasses缓存的keySet //即SPI接口配置文件中name的Set集合 if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }
5.4.SpringExtensionFactory
public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener(); public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); DubboShutdownHook.getDubboShutdownHook().unregister(); } //先SpringContext注册dubbo关闭钩子 BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER); } //省略了一些CONTEXTS的CRUD方法... @Override @SuppressWarnings("unchecked") public <T> T getExtension(Class<T> type, String name) { //不支持被@SPI注解标注的接口类型 if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } //首先根据name从Spring上下文获取bean,并验证该bean与type是否一致 for (ApplicationContext context : CONTEXTS) { if (context.containsBean(name)) { Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } logger.warn("No spring extension (bean) named ..."); //如果type的类型为Object,直接返回null if (Object.class == type) { return null; } //尝试根据type从Spring上下文获取bean,如果type对应的bean并非唯一,直接报错 for (ApplicationContext context : CONTEXTS) { try { return context.getBean(type); } catch (NoUniqueBeanDefinitionException multiBeanExe) { logger.warn("Find more than 1 spring extensions (beans) of type ..."); } catch (NoSuchBeanDefinitionException noBeanExe) { if (logger.isDebugEnabled()) { logger.debug("Error when get spring extension(bean) for type: ..."); } } } logger.warn("No spring extension (bean) named: ..."); //未找到,返回null return null; } private static class ShutdownHookListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook(); shutdownHook.doDestroy(); } } } }
SpringExtensionFactory维护了Spring上下文集合“Set<ApplicationContext>”。在getExtension方法中,通过Spring上下文去获取实例。并且,SpringExtensionFactory在设置SpringContext时,会向获取到的Context注册一个Spring容器关闭事件的监听钩子,用于关闭dubbo。
六、扩展点自动包装
package com.alibaba.xxx; import org.apache.dubbo.rpc.Protocol; public class XxxProtocolWrapper implements Protocol { Protocol impl; //单参数构造函数,且参数类型为其实现的SPI接口类型Protocol public XxxProtocolWrapper(Protocol protocol) { impl = protocol; } // 接口方法做一个操作后,再调用extension的方法 public void refer() { //... 一些操作 impl.refer(); // ... 一些操作 } }
自动包装的机制能够近似的实现AOP的功能,通过Wrapper类可以把所有扩展点公共逻辑移至Wrapper中,新加的Wrapper在所有的扩展点上添加了逻辑。