dubbo|dubbo源码系列2——spi源码解读(下)

今天是2021年10月1号,在此祝伟大的祖国母亲生日快乐,繁荣昌盛!
上一节对sdk spi机制从源码角度进行了分析,文末留下思考:jdk spi有什么缺点?总结如下:

  • 无法按需加载
    ServiceLoader通过LazyIterator实现延迟加载,但是加载时仍然是遍历所有的类进行实例化,无法按需加载;
  • 多线程非安全
    ServiceLoader方法都是static,在并发时可能出现意想不到的线程安全问题;
针对以上问题,dubbo在借鉴sdk spi的基础上,通过分离配置加载和实例化实现按需加载,并且提供了ioc以及aop,进行功能增强。接下来看看dubbo如何实现相关功能?本节安排如下:
  1. demo
  2. 拓展点分析
  3. ioc
  4. aop
  5. 总结
1、demo 1.1、接口 //运动接口
package xu.jiang.hua.dubbo.api.service; import org.apache.dubbo.common.extension.SPI; @SPI("volleyball") public interface Sport {public void play(); }

定义Sport接口,并且指定默认实现为volleyball.
@SPI public interface Drink {void drink(); }

1.2、实现 //篮球实现
package xu.jiang.hua.dubbo.api.service.impl; import xu.jiang.hua.dubbo.api.service.Drink; import xu.jiang.hua.dubbo.api.service.Sport; public class BasketBallSport implements Sport {private Drink drink; public void setDrink(Drink drink) {this.drink = drink; }@Override public void play() {System.out.print("运动前喝点饮料补充养分:"); drink.drink(); System.out.println("play basketball"); } }

//排球实现
package xu.jiang.hua.dubbo.api.service.impl; import xu.jiang.hua.dubbo.api.service.Sport; public class VolleyballSport implements Sport {@Override public void play() {System.out.println("play volleyballSport"); } }

//FootBallSport
package xu.jiang.hua.dubbo.api.service.impl; import org.apache.dubbo.common.extension.Adaptive; import xu.jiang.hua.dubbo.api.service.Sport; @Adaptive public class FootBallSport implements Sport {@Override public void play() {System.out.println("play football"); } }

并通过@Adaptive标示FootBallSport 为Sport的自适应类。
//运动切面
package xu.jiang.hua.dubbo.api.service.impl; import xu.jiang.hua.dubbo.api.service.Sport; public class SportWrapper1 implements Sport {private Sport sport; public SportWrapper1(Sport sport) {this.sport = sport; } @Override public void play() {System.out.println("运动前1"); sport.play(); System.out.println("运动后1"); } }

【dubbo|dubbo源码系列2——spi源码解读(下)】//饮料实现
package xu.jiang.hua.dubbo.api.service.impl; import org.apache.dubbo.common.extension.Adaptive; import xu.jiang.hua.dubbo.api.service.Drink; @Adaptive public class MineralWater implements Drink {@Override public void drink() {System.out.println("喝矿泉水"); } }

并通过@Adaptive标示MineralWater为Drink的自适应类。
1.3、配置
  • xu.jiang.hua.dubbo.api.service.Sport
    basketBall=xu.jiang.hua.dubbo.api.service.impl.BasketBallSport
    volleyball=xu.jiang.hua.dubbo.api.service.impl.VolleyballSport
    footBall=xu.jiang.hua.dubbo.api.service.impl.FootBallSport
    xu.jiang.hua.dubbo.api.service.impl.SportWrapper1
  • xu.jiang.hua.dubbo.api.service.Drink
    mineralWater=xu.jiang.hua.dubbo.api.service.impl.MineralWater
1.4、运行结果
public class MainTest {public static void main(String[] args) {System.out.println("...........getExtension............"); Sport basketBall = ExtensionLoader.getExtensionLoader(Sport.class).getExtension("basketBall"); basketBall.play(); System.out.println("...........getDefaultExtension............"); Sport defaultSport = ExtensionLoader.getExtensionLoader(Sport.class).getDefaultExtension(); defaultSport.play(); System.out.println("...........getAdaptiveExtension............"); Sport adaptiveSport = ExtensionLoader.getExtensionLoader(Sport.class).getAdaptiveExtension(); adaptiveSport.play(); } }

dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

从执行结果来看,basketBall这个拓展点的方法执行前后有SportWrapper1类进行拦截,同时通过dubbo spi机制注入Drink接口。
因Sport接口上@SPI(“volleyball”)上指定了默认实现,因此getDefaultExtension返回VolleyballSport;
2、拓展点分析 下面以上面demo为例深入源码分析内部实现逻辑。
2.1、getExtensionLoader 可以看到加载任何拓展点都需要先执行getExtensionLoader
public static ExtensionLoader getExtensionLoader(Class type) {//省略非关键代码 ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type); if (loader == null) {//new ExtensionLoader(type) EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type)); loader = (ExtensionLoader) EXTENSION_LOADERS.get(type); } return loader; }

继续分析new ExtensionLoader(type),如下:
private ExtensionLoader(Class type) {this.type = type; /** 逻辑解读: *1、传入的type为非ExtensionFactory时,则执行ExtensionLoader.getExtensionLoader(ExtensionFactory.class); *2、则再次调用new ExtensionLoader(Class type),则再次执行ExtensionLoader的构造函数,此时type为ExtensionFactory,objectFactory为null; *3、根据返回的ExtensionLoader,再执行方法getAdaptiveExtension; *4、加载META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory的内容,里面有SpiExtensionFactory和AdaptiveExtensionFactory, *因AdaptiveExtensionFactory被@Adaptive注解标注,则getAdaptiveExtension返回AdaptiveExtensionFactory的实例对象 *5、接下来对AdaptiveExtensionFactory进行实例化,AdaptiveExtensionFactory属性factories仅有SpiExtensionFactory(通过cachedClasses获取) */ objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }

对type为非ExtensionFactory生成的ExtensionLoader,objectFactory为SpiExtensionFactory。
2.2、getExtension
public T getExtension(String name, boolean wrap) {//省略非关键代码 if ("true".equals(name)) {return getDefaultExtension(); } final Holder holder = getOrCreateHolder(name); Object instance = holder.get(); //双重检查 if (instance == null) {synchronized (holder) {instance = holder.get(); if (instance == null) {//创建拓展实例 instance = createExtension(name, wrap); holder.set(instance); } } } return (T) instance; }
解析来分析createExtension方法(重点)
private T createExtension(String name, boolean wrap) {//A、加载配置返回map,并根据name获取对应的类 Class clazz = getExtensionClasses().get(name); if (clazz == null || unacceptableExceptions.contains(name)) {throw findException(name); } try {T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) {//实例化 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //B、ioc注入 injectExtension(instance); //C、aop处理 if (wrap) {List> wrapperClassesList = new ArrayList<>(); //找出所有的Wrapper并排序 if (cachedWrapperClasses != null) {wrapperClassesList.addAll(cachedWrapperClasses); wrapperClassesList.sort(WrapperComparator.COMPARATOR); Collections.reverse(wrapperClassesList); //倒叙List }if (CollectionUtils.isNotEmpty(wrapperClassesList)) {for (Class wrapperClass : wrapperClassesList) {//获取wrapper注解,如果为非空,则需要判断注解的上的match和mismatch是否与当前name匹配。如果不匹配,不生成aop实例 Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); if (wrapper == null || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {/** * wrapperClass.getConstructor(type).newInstance(instance) 实例化warpper类(aop类),将新生成的 * 实例赋值给instance,从而实现aop * * injectExtension 对warpper类实现ioc set */ instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } } }initExtension(instance); return instance; } catch (Throwable t) {//省略非关键代码 } }

createExtension方法主要分成以下三部分:
  • getExtensionClasses加载配置文件
  • ioc处理
  • aop处理
将加载配置和实例化分开,从而实现按需实例化,避免不必要的资源浪费(相比jdk spi的改进点)。先接下来看看内部如何实现的?
2.3、getExtensionClasses
private Map> loadExtensionClasses() {//A、加载当前接口的默认实现类 cacheDefaultExtensionName(); Map> extensionClasses = new HashMap<>(); /**B、加载配置文件 * * 策略模式,分别加载以下路径: * 1、META-INF/dubbo/internal/ * 2、META-INF/dubbo * 3、META-INF/Service */ for (LoadingStrategy strategy : strategies) {loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); }return extensionClasses; }

第一部分默认实现类的处理:
private void cacheDefaultExtensionName() {final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation == null) {return; } //通过注解获取默认值, String value = https://www.it610.com/article/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 on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) {//存在cachedDefaultName中 cachedDefaultName = names[0]; } } }

比如Sport有默认实现volleyball,如下所示:
dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

再看第二部分加载配置文件,目前支持以下三种路径:
  • META-INF/dubbo/internal/
  • META-INF/dubbo
  • META-INF/Service
    为了兼容dubbo移交给apache成为旗下的顶级项目,同时也会将type接口名中的org.apache换成com.alibaba。接下来看loadDirectory方法:
private void loadDirectory(Map> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {String fileName = dir + type; //省略非关键代码 if (urls != null) {while (urls.hasMoreElements()) {java.net.URL resourceURL = urls.nextElement(); //加载Resourece loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); } } } catch (Throwable t) {//省略非关键代码 } }

接下来看loadResource方法
private void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String... excludedPackages) {try {try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {String line; String clazz = null; //依次读取里面的内容 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; int i = line.indexOf('='); if (i > 0) {name = line.substring(0, i).trim(); clazz = line.substring(i + 1).trim(); } else {clazz = line; //如果无=,则认为是当前接口的warpper类 } //加载的类非空且不在排除的类集合里面,则执行loadClass if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden); } } catch (Throwable t) {//省略非关键代码 } } } } } catch (Throwable t) {//省略非关键代码 } }

loadResource说明:依次读取每行,过滤掉注释内容,然后判断当前行是否包含=。如果不包含,则认为该行是该接口Wrapper类(aop实现类),继续看loadClass方法:
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name, boolean overridden) throws NoSuchMethodException {//省略关键代码 //A、自适应类 if (clazz.isAnnotationPresent(Adaptive.class)) {cacheAdaptiveClass(clazz, overridden); //cachedAdaptiveClass //B、判断是否为wrapper类—判断该类是否存在一个参数为该接口的构造函数。如存在,则认为为wrapper类 } else if (isWrapperClass(clazz)) {cacheWrapperClass(clazz); //cachedWrapperClasses } else {//省略关键代码 String[] names = NAME_SEPARATOR.split(name); //一个类可以当做多个拓展点,因此进行切分 if (ArrayUtils.isNotEmpty(names)) {cacheActivateClass(clazz, names[0]); //cachedActivates for (String n : names) {cacheName(clazz, n); //存入map saveInExtensionClass(extensionClasses, clazz, n, overridden); } } } }

loadClass可以拆解成三部分:
  • Adaptive注解处理
    如果该类被@Adaptive注解标注,则将该类保存在以下爱属性中。
    private volatile Class cachedAdaptiveClass = null;
  • Wrapper类处理
private boolean isWrapperClass(Class clazz) {try {//通过加载判断是否存在带接口的构造函数 clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) {return false; } }

通过判断当前类是否存在以该接口为参数的构造函数。如果存在则认为为wrapper类,如果存在则将该列保存在private Set> cachedWrapperClasses;
  • 其他处理
    将name进行切分,分别存入Map> extensionClasses中,如下所示:
private void saveInExtensionClass(Map> extensionClasses, Class clazz, String name, boolean overridden) {Class c = extensionClasses.get(name); if (c == null || overridden) {extensionClasses.put(name, clazz); } else if (c != clazz) {// duplicate implementation is unacceptable unacceptableExceptions.add(name); String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName(); logger.error(duplicateMsg); throw new IllegalStateException(duplicateMsg); } }

总结一下getExtensionClasses加载配置后的保存情况:
  • 接口默认实现类保存在
private String cachedDefaultName;

  • 被@Adaptive标注的实现类
private volatile Class cachedAdaptiveClass = null;

  • warpper类
private Set> cachedWrapperClasses;

  • 其他类
private final Holder>> cachedClasses = new Holder<>();

cachedClasses则作为getExtensionClasses的结果返回。
3、ioc ioc处理位于createExtension方法中,回顾一下createExtension方法:
private T createExtension(String name, boolean wrap) {//加载配置,并根据name返回对应的类 Class clazz = getExtensionClasses().get(name); if (clazz == null || unacceptableExceptions.contains(name)) {throw findException(name); } try {T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) {//实例化 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //ioc注入 injectExtension(instance); //省略其他 }

通过getExtensionClasses将配置加载到map中并根据name获取对应类,然后进行实例化,实例化后执行injectExtension处理ioc。下面看看injectExtension方法:
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 */ //set方法如果不想被ioc使用,则通过注解标示 if (method.getAnnotation(DisableInject.class) != null) {continue; } Class pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) {continue; }try {String property = getSetterProperty(method); //通过type和名字从objectFactory获取 Object object = objectFactory.getExtension(pt, property); if (object != null) {//反射调用方法 method.invoke(instance, object); } } catch (Exception e) {//省略非关键代码 }} } catch (Exception e) {logger.error(e.getMessage(), e); } return instance; }

接下来看如何通过objectFactory.getExtension(pt, property)从上下文获取实例。根据ExtensionLoader实例化分析可知,objectFactory为AdaptiveExtensionFactory,如下:
dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

public T getExtension(Class type, String name) {for (ExtensionFactory factory : factories) {T extension = factory.getExtension(type, name); if (extension != null) {return extension; } } return null; }

此时facory中仅有SpiExtensionFactory,执行getExtension方法返回实例。下面看看getExtension如何执行的?
public class SpiExtensionFactory implements ExtensionFactory {@Override public T getExtension(Class type, String name) {if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type); /** * 如果type仅有一个实现类并且类上有一个@Adaptive标注,此时loader.getSupportedExtensions() * 返回为空,则getExtension返回null。为什么? */ if (!loader.getSupportedExtensions().isEmpty()) {//返回自适应类 return loader.getAdaptiveExtension(); } } return null; }}

分析getAdaptiveExtension:
public T getAdaptiveExtension() {Object instance = cachedAdaptiveInstance.get(); //省略非关键代码 synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get(); if (instance == null) {try {//创建adaptiveExtension实例 instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) {createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } }return (T) instance; }

继续分析createAdaptiveExtension:
private T createAdaptiveExtension() {try {//获取adaptiveExtension类并实例化,然后执ioc return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) {//省略非关键代码 } }

重点看getAdaptiveExtensionClass()方法
private Class getAdaptiveExtensionClass() {getExtensionClasses(); //优先使用被@Adaptive标注的类 if (cachedAdaptiveClass != null) {return cachedAdaptiveClass; } //如果当前接口不存在Adaptive注解标注的实现类,那么需要手动编译方法 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }

可以看到ioc注入的实例获取的优先级:
  • @Adaptive标注的类
  • 动态生成自适应类(方法上必须有@Adaptive注解,否则抛异常)。运行时通过传递url指定接口的实现类,如果传递的url未匹配上且有默认实现,则使用默认实现
下面就以以上面两种情况进行演示:
3.1、@Adaptive在类上
@Adaptive public class MineralWater implements Drink {@Override public void drink(URL url) {System.out.println("喝矿泉水"); } }

dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

3.2、@Adaptive在方法上 将MineralWater上的@Adaptive注解去掉,同时方法加上@Adaptive并设定key为drink,则运行通过url里面map参数指定具体的实现。
@SPI("mineralWater") public interface Drink {@Adaptive({ "drink"}) void drink(URL url); }

dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

private Class createAdaptiveExtensionClass() {//生成代理类时判断方法上是否有@Adaptive注解修饰,如果没有,则抛出异常 String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = //AdaptiveCompiler ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }

3.3、自动生成的自适应类
package xu.jiang.hua.dubbo.api.service; import org.apache.dubbo.common.extension.ExtensionLoader; public class Drink$Adaptive implements xu.jiang.hua.dubbo.api.service.Drink {public void drink(org.apache.dubbo.common.URL arg0){ if (arg0 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg0; //因为接口上指定了默认实现为mineralWater,则如果取不到则使用默认的实现 String extName = url.getParameter("drink", "mineralWater"); if(extName == null) throw new IllegalStateException("Failed to get extension (xu.jiang.hua.dubbo.api.service.Drink) name from url (" + url.toString() + ") use keys([drink])"); xu.jiang.hua.dubbo.api.service.Drink extension = (xu.jiang.hua.dubbo.api.service.Drink)ExtensionLoader.getExtensionLoader(xu.jiang.hua.dubbo.api.service.Drink.class).getExtension(extName); extension.drink(arg0); } }

dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

将生成的自适应类通过反射赋给BasketBallSport实例, 运行时通过URL指定具体的实现:
URL url=URL.valueOf(“http://127.0.0.1”);
url= url.addParameter(“drink”,“mineralWater”);
drink.drink(url);
3.3、ioc总结 通过injectExtension方法和类上的set方法实现依赖注入,获取的实例的优先级如下:
  • A、@Adaptive标记类生成的实例;
  • B、根据@Adaptive标记的方法自动编译生成自适应类,运行时如果匹配失败且有默认实现类,则使用默认类。
4、aop aop处理同样位于createExtension方法中,回顾一下createExtension方法:
private T createExtension(String name, boolean wrap) {//省略非关键代码 //ioc注入 injectExtension(instance); //aop处理 if (wrap) {List> wrapperClassesList = new ArrayList<>(); //找出所有的Wrapper并排序 if (cachedWrapperClasses != null) {wrapperClassesList.addAll(cachedWrapperClasses); wrapperClassesList.sort(WrapperComparator.COMPARATOR); Collections.reverse(wrapperClassesList); //倒叙List }if (CollectionUtils.isNotEmpty(wrapperClassesList)) {for (Class wrapperClass : wrapperClassesList) {//获取wrapper注解,如果为非空,则需要判断注解的上的match和mismatch是否与当前name匹配。如果不匹配,不生成aop实例 Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); if (wrapper == null || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {/** * wrapperClass.getConstructor(type).newInstance(instance) 实例化warpper类(aop类),将新生成的 * 实例赋值给instance,从而实现aop * * injectExtension 对warpper类实现ioc set */ instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } } }initExtension(instance); return instance; } catch (Throwable t) {} }

前面getExtensionClasses方法中已经分析过warpper类会保存在
private Set> cachedWrapperClasses;
取出所有的cachedWrapperClasses并排序。接下来重点看
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))
从warpper类中取出具有接口参数的构造函数,并向构造函数传入当前实例进行warpper类的实例化,然后进行ioc处理(warpper类可能也有依赖注入),然后赋值给instance(前后instance不是同一个对象),如下所示:
dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

dubbo|dubbo源码系列2——spi源码解读(下)
文章图片

可以看到instantce不是同一个对象,新生成的instance包含之前的instance。
5、总结 每个拓展点都会通过getExtensionLoader生成ExtensionLoader实例。
  • getExtension
    getExtension通过传入的name实现按需实例化;
  • getDefaultExtension
    getDefaultExtension返回接口的默认实现, 默认实现的 类名保存在
    private String cachedDefaultName;
  • getAdaptiveExtension
    getDefaultExtension返回接口的自适应类。优先使用@Adaptive标注的类,保存在cachedAdaptiveClass。如果无@Adaptive标记的类,则根据@Adaptive标记的方法自动生成自适应类;
private volatile Class cachedAdaptiveClass = null;

  • ioc
    获取当前接口getAdaptiveExtension返回实例进行依赖注入;如果getAdaptiveExtension为空且有默认实现,则使用默认实现,否则不注入;
  • aop
    根据getExtensionClasses得到接口的warpper类集合实现链式增强并作为新实例返回。
private Set> cachedWrapperClasses;

    推荐阅读