 
            Dubbo系列-SPI扩展(下)
前言
上一篇文章Dubbo系列-SPI扩展(上),通过对比JDK、Spring、Dubbo对SPI的不同实现,对它们的区别有一个全局的了解,其中Dubbo SPI实现最为复杂,其在整个框架设计与实现上发挥着极大的作用。
本篇会继续深入Dubbo SPI的其它特性,包括Dubbo3引入的作用域模型,及Dubbo SPI扩展点的四大特性(自动包装、自动加载、自适应、自动激活)。
作用域模型
定义
在介绍Dubbo的扩展点时,原来的获取扩展点的静态方法已经标记废弃,如下所示,取而代之的是ApplicationModel的东西进行获取。
@Deprecated
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(type);
}其中ApplicationModel是Dubbo3引入的,其对应着作用域ExtensionScope#APPLICATION,除此之外,还有ExtensionScope#FRAMEWORK、ExtensionScope#MODULE、ExtensionScope#SELF。
SPI扩展点作用域包括:
- ExtensionScope#FRAMEWORK:框架级别作用域,对所有的application和module可见;对应着域模型 FrameworkModel 
- ExtensionScope#APPLICATION:应用级别作用域,对所有module可见;对应着域模型 ApplicationModel 
- ExtensionScope#MODULE:模块级别作用域;对应着域模型 ModuleModel 
- ExtensionScope#SELF:自给自足,为每个作用域创建一个实例,用于特殊的SPI扩展,如ExtensionInjector。 
每个作用域模型缓存着对应作用域的扩展点的相关信息。可以类比ClassLoader理解。
关于作用域有个可见性,在查找作用域对应的扩展加载器ExtensionLoader时就如ClassLoader的双亲委派,上篇文章有提到,会递归查询上一级的作用域。
例如@SPI注解为ExtensionScope.Module,则获取ExtensionLoader过程ExtensionScope.Module -> Application -> Framework
1)、先ModuleScope缓存中查找,找到返回,找不到则进入2)
2)、再Application缓存中找,找到返回,找不到则匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配进入3)
3)、再Framework缓存中找,找到返回,找不到则匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配进入4)
4)、最后回到了ModuleScope匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配不创建,返回null
对应流程图:

作用
为什么会在Dubbo3的新版本中加入这个作用域模型呢?
1、让Dubbo支持多应用的部署,可能大企业才有诉求,即一个JVM启动两个Dubbo应用
可以参考github issue有关场景
2、从架构设计上,之前都是一个作用域,通过静态类进行属性共享,引入域模型解决了静态属性资源共享、清理的问题
3、分层模型可以将应用的管理和模块的管理进行分开管理
设计与实现

- ExtentsionAccessor作为扩展的统一访问接口 - 用于获取扩展管理器ExtensionDirector 
- 获取扩展加载器ExtensionLoader 
- 获取扩展对象(名字、自适应、默认) 
 
- ExtensionDirector扩展管理器 - Class到扩展加载器ExtensionLoader的缓存 
- Class到作用域ExtensionScope的缓存 
- 父扩展管理器ExtensionDirector 
- 所属的模型实例对象scopeModel 
- 扩展点处理器List<ExtensionPostProcessors> 
 
- ScopeModel作用域模型抽象父类 - 表示内部层次结构的内部id 
- 父模型ScopeModel 
- 当前作用域ExtensionScope 
- 当前作用域模型对应的扩展管理器ExtensionDirector 
- 当前作用域模型对应的Bean工厂管理ScopeBeanFactory 
- 当前是否是内部作用域internalScope:boolean 
 
- 三个不同的作用域模型实现 - ModuleModel的构造器需要传入ApplicationModel实例 
- ApplicationModel构造器需要传入FrameworkModel实例 
- ApplicationModel和ModuleModel构造器可见性protected;FrameworkModel可见性public 
 
举例
1、Codec2编解码器,一种Codec2编解码器在一个FrameworkModel中只有一个实例;还有如Protocol等。
2、统计服务MetricsService,一个ApplicationModel只有一个实例;FrameworkModel作用域获取不到。
3、Filter过滤器在一个ModuleModel中有一个实例;在ApplicationModel和FrameworkModel作用域获取不到。
SPI四大特性
Dubbo扩展点的四大特性包括:
- 自动包装 
- 自动加载 
- 自适应 
- 自动激活 
不管哪种特性,都是获取SPI接口的扩展点,都需要加载配置文件。
自动包装
在ExtensionLoader加载扩展时,如果发现这个扩展类包含其它的扩展点作为构造函数的参数,则这个扩展类会被当成Wrapper类。
例如ProtocolFilterWrapper,实现了Protocol,同时使用Protocol作为构造器的入参。这是一种装饰器模式,对逻辑进行封装或对子类的增强。
public class ProtocolFilterWrapper implements Protocol {
	private final Protocol protocol;
	public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
}一个示例
// 接口
@SPI(value = DubboSimpleSpiImpl1.NAME, scope = ExtensionScope.APPLICATION)
public interface DubboSimpleSpi {
    void sayHello();
}
// 实现1 略
// 实现2
public class DubboSimpleSpiImpl2 implements DubboSimpleSpi {
    public static final String NAME = "dubboSimpleSpiImpl2";
    @Override
    public void sayHello() {
        System.out.println("DubboSimpleSpiImpl2:" + this + " say hello");
    }
}
// 包装类
@Wrapper
public class DubboSimpleSpiWrapper implements DubboSimpleSpi {
    // 依赖注入
    private DubboSimpleSpi dubboSimpleSpi;
    public DubboSimpleSpiWrapper(DubboSimpleSpi dubboSimpleSpi) {
        this.dubboSimpleSpi = dubboSimpleSpi;
    }
    @Override
    public void sayHello() {
        System.out.println("DubboSimpleSpiWrapper sayHello");
        dubboSimpleSpi.sayHello();
    }
}
// 演示类
public class DubboSpiWrapperDemo {
    public static void main(String[] args) {
        // 获取DubboSimpleSpiImpl2.NAME对应的实现类
        DubboSimpleSpi dubboSimpleSpi = ApplicationModel.defaultModel().getExtensionLoader(DubboSimpleSpi.class)
                .getExtension(DubboSimpleSpiImpl2.NAME);
        dubboSimpleSpi.sayHello();
    }
}配置文件META-INF/dubbo/top.xudj.spi.dubbo.DubboSimpleSpi:
dubboSimpleSpiImpl1=top.xudj.spi.dubbo.DubboSimpleSpiImpl1
dubboSimpleSpiImpl2=top.xudj.spi.dubbo.DubboSimpleSpiImpl2
dubboSimpleSpiWrapper=top.xudj.spi.dubbo.wrapper.DubboSimpleSpiWrapper注意,也需要配置包装类,它是一个扩展点,只不过是特殊的扩展点:dubboSimpleSpiWrapper=top.xudj.spi.dubbo.wrapper.DubboSimpleSpiWrapper
运行演示类,输出包装类的日志,再调用原扩展点对象的日志:
DubboSimpleSpiWrapper sayHello
DubboSimpleSpiImpl2:top.xudj.spi.dubbo.DubboSimpleSpiImpl2@7920ba90 say hello原理分析
分析原理之前,先看@Wrapper注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Wrapper {
    String[] matches() default {};
    String[] mismatches() default {};
    int order() default 0;
}该注解用于决定被标记的类是否要作为某个扩展点的包装类。
先说结论,下面再分析原因:默认可不加注解或加注解无配置,matches和mismatches属性为扩展点的名称(配置文件内容的key),决定哪些扩展点名称会被当作包装类。
下面看原理分析,获取扩展点实现的方法:getExtension(String),即上篇文章分析的根据名称获取扩展,默认有个参数wrap为true:
// org.apache.dubbo.common.extension.ExtensionLoader#getExtension(java.lang.String, boolean)
public T getExtension(String name, boolean wrap) {
	// ...
	instance = createExtension(name, wrap);
	// ...
}继续看创建扩展:
// org.apache.dubbo.common.extension.ExtensionLoader#createExtension
private T createExtension(String name, boolean wrap) {
    // 获取接口的全部扩展类实现class,然后根据name获取对应的class
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        // 从缓存中获取扩展实例,extensionInstances:ConcurrentMap<Class<?>, Object>
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            // 核心:调用createExtensionInstance方法创建扩展实例(构造器反射),并放入缓存
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
			instance = (T) extensionInstances.get(clazz);
			// ... 对instance进行一系列的处理
        }
        // 是否需要包装,默认为true
        if (wrap) {
            List<Class<?>> wrapperClassesList = new ArrayList<>();
            // 存在缓存的包装类,在getExtensionClasses()文件加载的时候会存入cachedWrapperClasses
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                // 排序 并 反转
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }
            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                // AOP思想的体现
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    // match两种情况为true:1)没有Wrapper注解,2)有注解,matches为空或者matches包含name,且mismatches不包含name
                    boolean match = (wrapper == null)
                            || ((ArrayUtils.isEmpty(wrapper.matches())
                                            || ArrayUtils.contains(wrapper.matches(), name))
                                    && !ArrayUtils.contains(wrapper.mismatches(), name));
                    if (match) {
                        // 包装扩展实例,实例化包装类,并完成"包装类"的DI,类似Spring的Autowired
                        instance = injectExtension(
                                (T) wrapperClass.getConstructor(type).newInstance(instance));
                        // 包装类的后置处理,类似Spring的BeanPostProcessor#postProcessAfterInitialization
                        // 默认会注入实现了ScopeModelAware接口的方法
                        instance = postProcessAfterInitialization(instance, name);
                    }
                }
            }
        }
		// ...
        // 返回扩展实例
        return instance;
    } catch (Throwable t) {
		// ... throw
    }
}代码省略了部分内容,核心看 if(wrap){},在进入包装扩展处理前,对应的instance已经创建完成了。
首先获取cachedWrapperClasses,这是一个关键成员变量,类型Set<Class<?>>,缓存wrapper的Class,它从何而来?下面再详细介绍。
对示例来说就是top.xudj.spi.dubbo.wrapper.DubboSimpleSpiWrapper对应的Class,如果存在多个,进行排序(正序),根据顺序进行多重包装。这就体现了AOP的思想。
最终会不会进行包装,还要看包装类上的注解@Wrapper,上面提到过,两种情况会进行包装:
- 类上没有@Wrapper注解; 
- 类上有@Wrapper注解,matches为空或者matches包含name,且mismatches不包含name。这个name是扩展点名称 
一旦进行包装,也会对包装类的Setter方法进行DI注入(Inject spi extension 和 scope bean),体现了IoC的思想;以及后置处理。
划重点,cachedWrapperClasses缓存从何而来?
回到createExtension方法的入口有个getExtensionClasses(),该方法会对配置文件进行解析,跟进getExtensionClasses()方法:
private Map<String, Class<?>> getExtensionClasses() {
    // cachedClasses扩展点实现的Class缓存,不包括标记@Adaptive注解和Wrapper扩展类
    Map<String, Class<?>> classes = cachedClasses.get();
	// ... 此处有cachedClasses双检锁
    // 获取当前接口的全部的扩展类Class
    classes = loadExtensionClasses();
    // ... 加入cachedClasses中
    return classes;
}继续看loadExtensionClasses();
private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
    checkDestroyed();
    // @SPI注解的value值,作为默认的扩展名,存入变量cachedDefaultName:String中
    cacheDefaultExtensionName();
    Map<String, Class<?>> extensionClasses = new HashMap<>();
    // 加载扩展类的策略,有三种:META-INF/dubbo/internal、META-INF/dubbo、META-INF/services
    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy, type.getName());
        // compatible with old ExtensionFactory
        if (this.type == ExtensionInjector.class) {
            loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
        }
    }
    return extensionClasses;
}一步步下去,按ClassLoader加载接口命名的配置文件(可能多个),最后调用到:
// org.apache.dubbo.common.extension.ExtensionLoader#loadClass
private void loadClass(
        ClassLoader classLoader,
        Map<String, Class<?>> extensionClasses,
        java.net.URL resourceURL,
        Class<?> clazz,
        String name,
        boolean overridden) {
	// 当前class实现了接口type
    if (!type.isAssignableFrom(clazz)) {
        // ... throw 
    }
    // clazz是否是激活的,不激活则直接返回,由@Activate的onClass属性决定
    boolean isActive = loadClassIfActive(classLoader, clazz);
    if (!isActive) {
        return;
    }
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        // clazz是标有Adaptive注解,将clazz指向缓存 cachedAdaptiveClass:Class<?>
        // 几种策略都是overridden=true,可覆盖
        cacheAdaptiveClass(clazz, overridden);
    } else if (isWrapperClass(clazz)) {
        // isWrapperClass:满足构造器的要求(扩展点作为构造器的入参),将class加入缓存 cachedWrapperClasses:Set<Class<?>>
        cacheWrapperClass(clazz);
    } else {
        // 此时,==》clazz不是Adaptive,也不是Wrapper,是普通的扩展类
        if (StringUtils.isEmpty(name)) {
            // 使用类名作为扩展名
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class " + clazz.getName()
                        + " in the config " + resourceURL);
            }
        }
        // 逗号分割name,正常就一个
        String[] names = NAME_SEPARATOR.split(name);
        if (ArrayUtils.isNotEmpty(names)) {
            // 存在@Activate注解时,缓存第一个name(一般也就一个名字)到cachedActivates:Map<String, Object>
            cacheActivateClass(clazz, names[0]);
            for (String n : names) {
                // 缓存name到cachedNames:ConcurrentMap<Class<?>, String>
                cacheName(clazz, n);
                // 重要,将class放入extensionClasses
                saveInExtensionClass(extensionClasses, clazz, n, overridden);
            }
        }
    }
}该方法是重点,处理的缓存信息关系着SPI的四大特性。
该方法核心分成2大步骤:
- 首先判断class是否是激活的,激活表示可以获取到该扩展。当使用@Activate且配置了onClass属性且配置的onClass存在className不能ClassLoader加载,则不是激活的,否则就是激活的。 
- 分三种情况依次处理: - 如果类是有@Adaptive注解,将Class存入缓存cachedAdaptiveClass:Class<?>中,多个会进行覆盖。 
- 如果有个仅接口类型入参的构造器,被认定为包装类,将Class缓存到cachedWrapperClasses:Set<Class<?>>中 
- 普通的扩展类处理,将扩展点名称到Class的映射放入缓存cachedClasses:Holder<Map<String, Class<?>>>中,名称相同会覆盖;同时标记@Activate的类会额外进行缓存,将扩展点名称到注解的信息缓存到cachedActivates:Map<String, Object>中, 
 
补充说明下,只有在cachedClasses缓存中的Class,才能通过getExtension(String name) 进行获取到扩展。具体可以看上面createExtension方法。
至此,分析了配置文件的加载与解析,得到了包装类信息,存入缓存cachedWrapperClasses中,在getExtension(String name) 获取扩展时,完成对原扩展的包装。
自动加载
上面的自动包装特性是通过构造函数完成的,还有一种是setter方法进行注入,获取类型是参数类型,名称通过方法名截取,Dubbo SPI会自动注入对应的实例,它是一种IoC思想。接下来先看一个示例。
一个示例
// 接口
@SPI
public interface DubboSpiInject {
    void echo(String msg);
    void setDubboAdaptiveSpi(DubboAdaptiveSpi dubboAdaptiveSpi);
}
// 实现
public class DubboSpiInjectImpl implements DubboSpiInject {
    public static final String NAME = "dubboSpiInject";
    @Override
    public void echo(String msg) {
        System.out.println("DubboSpiInjectImpl echo:" + msg);
    }
    /**
     * 自动注入:先查询ScopeBeanFactory,再查询ExtensionLoader,查询ExtensionLoader时,查找自适应扩展实例
	 * DubboAdaptiveSpi:可以通过SPI获取到的接口类型
     */
    @Override
    public void setDubboAdaptiveSpi(DubboAdaptiveSpi dubboAdaptiveSpi) {
        System.out.println("DubboSpiInjectImpl setDubboAdaptiveSpi:" + dubboAdaptiveSpi);
    }
}
// 演示
public class DubboSpiInjectDemo {
    public static void main(String[] args) {
        ExtensionLoader<DubboSpiInject> extensionLoader = ApplicationModel.defaultModel().getExtensionLoader(DubboSpiInject.class);
        DubboSpiInject dubboSpiInject = extensionLoader.getExtension(DubboSpiInjectImpl.NAME);
        dubboSpiInject.echo("hello");
    }
}配置文件,META-INF/dubbo/top.xudj.spi.dubbo.inject.DubboSpiInject,内容:
dubboSpiInject=top.xudj.spi.dubbo.inject.DubboSpiInjectImpl运行演示类,输出:
DubboSpiInjectImpl setDubboAdaptiveSpi:top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi$Adaptive@293a5bf6
DubboSpiInjectImpl echo:hello通过输出日志可以看出,setDubboAdaptiveSpi方法成功完成了对象注入。
原理分析
演示类中获取扩展点方法仍是extensionLoader.getExtension(String name);得到,在创建对应的扩展点时:
private T createExtension(String name, boolean wrap) {
    // 获取接口的全部扩展类实现class,然后根据name获取对应的class
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        // 从缓存中获取扩展实例,extensionInstances:ConcurrentMap<Class<?>, Object>
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            // 核心:调用createExtensionInstance方法创建扩展实例(构造器反射),并放入缓存
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
			// ...
            // DI,注入扩展实例的依赖,类似Spring的Autowired,只不过它是"setter注入"(Inject spi extension 和 scope bean)
            injectExtension(instance);
            // ...
        }
		// ...
        // 返回扩展实例
        return instance;
    } catch (Throwable t) {
        // ... throw 
    }
}重点在injectExtension(instance);方法:
private T injectExtension(T instance) {
    if (injector == null) {
        return instance;
    }
    try {
        for (Method method : instance.getClass().getMethods()) {
            // 1、查找方法
			if (!isSetter(method)) {
                continue;
            }
			// 2、过滤不需要注入的情况
            if (method.isAnnotationPresent(DisableInject.class)) {
                continue;
            }
            // When spiXXX implements ScopeModelAware, ExtensionAccessorAware,
            // the setXXX of ScopeModelAware and ExtensionAccessorAware does not need to be injected
            if (method.getDeclaringClass() == ScopeModelAware.class) {
                continue;
            }
            if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
                if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
                    continue;
                }
            }
            Class<?> pt = method.getParameterTypes()[0];
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }
            try {
                String property = getSetterProperty(method);
                // 3、进行注入
                Object object = injector.getInstance(pt, property);
                if (object != null) {
                    method.invoke(instance, object);
                }
            } catch (Exception e) {
                // ... log
            }
        }
    } catch (Exception e) {
        logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", e.getMessage(), e);
    }
    return instance;
}方法主要包括3步:
- 查找所有setter方法; 
- 对于部分情况不会进行注入,包括方法有@DisableInject注解、类实现了ScopeModelAware, ExtensionAccessorAware继承下来的方法、参数类型属于基础类型或String等都会跳过; 
- 进行注入,依赖于injector:ExtensionInjector对象,在构造ExtensionLoader时创建,最终会依次调用如下2个实现:ScopeBeanExtensionInjector、SpiExtensionInjector的实现方法:org.apache.dubbo.common.extension.ExtensionInjector#getInstance 
- 对于ScopeBeanExtensionInjector: 
依赖于beanFactory:ScopeBeanFactory变量,维护着当前作用域下的bean,通过ScopeModel作用域模型获取所得。在ScopeModel的initialize方法中初始化 beanFactory = new ScopeBeanFactory(parent != null ? parent.getBeanFactory() : null, extensionDirector);。类似于Spring的BeanFactory,也存在parent,有registerBean及getBean等api。
- 对于SpiExtensionInjector 
依赖于ExtensionLoader,如下,通过type得到ExtensionLoader,调用getAdaptiveExtension()获取到自适应扩展。
@Override
public <T> T getInstance(final Class<T> type, final String name) {
    if (!type.isInterface() || !type.isAnnotationPresent(SPI.class)) {
        return null;
    }
    ExtensionLoader<T> loader = extensionAccessor.getExtensionLoader(type);
    if (loader == null) {
        return null;
    }
    // 这里注意,即使getSupportedExtensions有,但可能自适应类扩展没有(一是没有@Adaptive注解的类,二是接口没有@Adaptive注解的方法)
    if (!loader.getSupportedExtensions().isEmpty()) {
        return loader.getAdaptiveExtension();
    }
    return null;
}至此,完成了扩展实例的setter方法依赖注入DI。最后提到的自适应扩展是什么呢?往下看自适应特性。
自适应
在SPI扩展中,有个注解@Adaptive,用来标记扩展点类或者接口方法,来实现动态的通过URL确定使用哪个具体的实现类。仍然先通过一个示例看看这个注解怎么用。
一个示例
// 接口
@SPI(DubboAdaptiveSpiImpl1.NAME)
public interface DubboAdaptiveSpi {
    @Adaptive(value = {"adaptive", "adaptiveBack"})
    String echo(URL url, String s);
    default void defaultPlay(String s) {
        System.out.println("DubboAdaptiveSpi play");
    }
    void play(String s);
}
// 实现1
public class DubboAdaptiveSpiImpl1 implements DubboAdaptiveSpi {
    public static final String NAME = "impl1";
    @Override
    public String echo(URL url, String s) {
        System.out.println("DubboAdaptiveSpiImpl1 echo url:" + url.toFullString() + ", s:" + s);
        return this.getClass().getSimpleName();
    }
    @Override
    public void play(String s) {
        System.out.println("DubboAdaptiveSpiImpl1 play s:" + s);
    }
}
// 实现2
public class DubboAdaptiveSpiImpl2 implements DubboAdaptiveSpi {
    public static final String NAME = "impl2";
    @Override
    public String echo(URL url, String s) {
        System.out.println("DubboAdaptiveSpiImpl2 echo url:" + url.toFullString() + ", s:" + s);
        return this.getClass().getSimpleName();
    }
    @Override
    public void play(String s) {
        System.out.println("DubboAdaptiveSpiImpl2 play s:" + s);
    }
}
// 演示类
public class DubboAdaptiveSpiDemo {
    public static void main(String[] args) {
        ExtensionLoader<DubboAdaptiveSpi> extensionLoader = ApplicationModel.defaultModel()
                .getExtensionLoader(DubboAdaptiveSpi.class);
        DubboAdaptiveSpi adaptiveExtension = extensionLoader.getAdaptiveExtension();
        // 构造参数
        Map<String, String> map = new HashMap<>();
		map.put("adaptive", "impl2"); // 使用DubboAdaptiveSpiImpl2,url:dubbo://1.2.3.4:1010/path1?adaptive=impl2
        URL url = new ServiceConfigURL("dubbo", "1.2.3.4", 1010, "path1", map);
        adaptiveExtension.echo(url, "hello");
    }
}配置文件:META-INF/dubbo/top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi,内容:
impl1=top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpiImpl1
impl2=top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpiImpl2运行演示类,输出:DubboAdaptiveSpiImpl2 echo url:dubbo://1.2.3.4:1010/path1?adaptive=impl2, s:hello
先说结论:@Adaptive注解方式:
- 方法上面,会动态生成并编译一个Class类,内部的逻辑往下看;extensionLoader.getAdaptiveExtension()得到的便是生成的类。 
- 类上面,该类作为自适应类,内部逻辑可以自行定义;extensionLoader.getAdaptiveExtension()得到的便是该类。 
对于注解在方法上的情况,生成的类的方法内部逻辑如下:通过url获取参数得到扩展的名称,通过扩展名称来决定使用哪个扩展实现类,所以叫自适应。
内部方法实现的逻辑分为2步:
1、获取扩展点name = url.getParameter(${key}, ${default})
${key}:首先是@Adaptive的value值作为key(多个依次为key),如果没配置则使用接口名转化(DubboAdaptiveSpi -> dubbo.adaptive.spi作为key),url通过key得到得value值作为对应的扩展点名称。如果key没有对应的value,则使用${default},${default}为@SPI注解的value值
2、根据扩展点name,调用getExtension(String name)得到扩展点实现,并调用对应的实现方法。
生成的自适应类是什么样子呢?示例生成的类如下:
package top.xudj.spi.dubbo.adaptive;
import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
public class DubboAdaptiveSpi$Adaptive implements top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi {
public void defaultPlay(java.lang.String arg0)  {
	throw new UnsupportedOperationException("The method public default void top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi.defaultPlay(java.lang.String) of interface top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi is not adaptive method!");
}
public void play(java.lang.String arg0)  {
	throw new UnsupportedOperationException("The method public abstract void top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi.play(java.lang.String) of interface top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi is not adaptive method!");
}
public java.lang.String echo(org.apache.dubbo.common.URL arg0, java.lang.String arg1)  {
	if (arg0 == null) 
		throw new IllegalArgumentException("url == null");
	org.apache.dubbo.common.URL url = arg0;
	String extName = url.getParameter("adaptive", url.getParameter("adaptiveBack", "impl1"));
	if(extName == null) 
		throw new IllegalStateException("Failed to get extension (top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi) name from url (" + url.toString() + ") use keys([adaptive, adaptiveBack])");
	ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi.class);
	
	top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi extension = (top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi)scopeModel.getExtensionLoade(top.xudj.spi.dubbo.adaptive.DubboAdaptiveSpi.class)
		.getExtension(extName);
	return extension.echo(arg0, arg1);
}可以看出:
- 该类实现了对应的接口,类名为:接口名 + $Adaptive 
- @Adaptive注解在echo方法,只会处理echo方法,其它方法均会抛出异常 
原理分析
先看下@Adaptive注解,比较简单就一个value数组,存放url用于匹配的参数key。
public @interface Adaptive {
    String[] value() default {};
}生成的类的情况上面分析差不多了,这里看下,extensionLoader.getAdaptiveExtension();具体做了什么?
public T getAdaptiveExtension() {
    checkDestroyed();
    // 先从缓存中取
    Object instance = cachedAdaptiveInstance.get();
    // ... 双重检查锁
	// 重要方法创建自适应扩展点实现
	instance = createAdaptiveExtension();
	// ... 设置到缓存中
	cachedAdaptiveInstance.set(instance);
    return (T) instance;
}这里可以看出,该方法无参,所以每个扩展点接口只有一个对应的自适应实现类。
继续看createAdaptiveExtension()创建:
private T createAdaptiveExtension() {
    try {
        // 创建自适应扩展类
        T instance = (T) getAdaptiveExtensionClass().newInstance();
        // 前置处理
        instance = postProcessBeforeInitialization(instance, null);
        // DI,setter方法注入
        injectExtension(instance);
        // 后置处理,默认处理ScopeModelAware接口的set方法
        instance = postProcessAfterInitialization(instance, null);
        // 如果是Lifecycle接口的实现类,调用初始化方法
        initExtension(instance);
        return instance;
    } catch (Exception e) {
        throw new IllegalStateException(
                "Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}主要看T instance = (T) getAdaptiveExtensionClass().newInstance();,其它都见过。
private Class<?> getAdaptiveExtensionClass() {
    // 加载配置文件,几种情况都一样
    getExtensionClasses();
    // 如果cachedAdaptiveClass不为空,直接返回,cachedAdaptiveClass是@Adaptive注解在了实现类上
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}调用getExtensionClasses加载配置文件,在自动包装特性原理分析时已经讲述过。SPI都离不开这个操作。
这里有个判断cachedAdaptiveClass也很重要,cachedAdaptiveClass(解析文件时处理的)如果不为null,说明存在类上有@Adaptive注解(多个的话默认进行覆盖),直接使用该Class即可。没有的话最后调用createAdaptiveExtensionClass()创建自适应Class:
private Class<?> createAdaptiveExtensionClass() {
	// ...
    // 生成自适应类的代码,然后通过Compiler编译成Class
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector
            .getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class)
            .getAdaptiveExtension();
    return compiler.compile(type, code, classLoader);
}通过AdaptiveClassCodeGenerator完成代码生成,再通过Compiler的扩展点实现完成compile编译成Class。
这里注意下:AdaptiveClassCodeGenerator的generate()里有个判断,需要当前的扩展点类type中有方法标记了@Adaptive注解,否则抛出异常。
至此,自适应的扩展实现完成。
自动激活
自适应特性是动态寻找实现类的方式,比较灵活,但只能激活一个具体的实现,上面三大特性都是激活一个具体的实现。如果像Filter一样,需要多个类同时激活呢,就需要用到SPI的最后一个特性:自动激活。
对于自动激活的注解@Activate,因为这个注解相对复杂,先看注解:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    String[] group() default {};
	// URL parameter keys
    String[] value() default {};
    @Deprecated
    String[] before() default {};
    @Deprecated
    String[] after() default {};
	// 正序排序,值越小越靠前
    int order() default 0;
    String[] onClass() default {};
}- group:可以通过group进行分组获取,具体看ExtensionLoader的入参 
- value:配置url参数,支持多个key或配置”key:value“形式 
- before和after在dubbo2.7.0版本之后废弃,用于排序 
- order:排序,默认0,值越小越靠前 
- onClass:决定Class是否是激活的,默认是,具体看上文有提到。 
一个示例
// 接口
@SPI
public interface DubboActivateSpi {
    void echo(URL url, String msg);
}
// 实现1
@Activate(group = "group1", value = {"key1:impl1", "key"})
public class DubboActivateSpiImpl1 implements DubboActivateSpi {
    public static final String NAME = "impl1";
    @Override
    public void echo(URL url, String msg) {
        System.out.println("DubboActivateSpiImpl1 echo url: " + url + ", msg: " + msg);
    }
}
// 实现2
@Activate(group = "group1", value = {"key2:impl2", "key"})
public class DubboActivateSpiImpl2 implements DubboActivateSpi {
    public static final String NAME = "impl2";
    @Override
    public void echo(URL url, String msg) {
        System.out.println("DubboActivateSpiImpl2 echo url: " + url + ", msg: " + msg);
    }
}
// 演示类
public class DubboActivateSpiDemo {
    public static void main(String[] args) {
        ExtensionLoader<DubboActivateSpi> extensionLoader = ApplicationModel.defaultModel().getExtensionLoader(DubboActivateSpi.class);
        // 激活扩展实例
        List<DubboActivateSpi> activateExtensions = new ArrayList<>();
        // activateExtensions = extensionLoader.getActivateExtensions(); // 获取全部
        // 获取特定的激活扩展,根据group和key1
        Map<String, String> map = new HashMap<>();
        // map.put("key1", "impl1");     // DubboActivateSpiImpl1作为非默认的激活扩展
        // map.put("key1", "impl2");       // DubboActivateSpiImpl2作为非默认的激活扩展
        // map.put("key1", "impl1,impl2");   // DubboActivateSpiImpl1和DubboActivateSpiImpl2作为非默认的激活扩展
        // map.put("key1", "xxx");         // 空,无非默认,无法匹配默认
        URL url = new ServiceConfigURL("dubbo", "1.2.3.4", 1010, "path1", map);
        activateExtensions = extensionLoader.getActivateExtension(url, "key1", "group1");
        // map.put("key", "xxx");   // DubboActivateSpiImpl1和DubboActivateSpiImpl2作为默认的激活扩展
        // map.put("key", "xxx,-default");   // 空,无非默认
        // map.put("key", "");     // 空,没有激活扩展,无法匹配默认,也无法匹配非默认
        // activateExtensions = extensionLoader.getActivateExtension(url, "key", "group1");
        activateExtensions.forEach(activateExtension -> activateExtension.echo(url, "hello"));
    }
}配置文件:META-INF/dubbo/top.xudj.spi.dubbo.activate.DubboActivateSpi,内容
impl1=top.xudj.spi.dubbo.activate.DubboActivateSpiImpl1
impl2=top.xudj.spi.dubbo.activate.DubboActivateSpiImpl2运行演示类,输出:DubboActivateSpiImpl1 echo url: dubbo://1.2.3.4:1010/path1?key1=impl1, msg: hello
原理分析
通过extensionLoader.getActivateExtension(url, "key1", "group1");看:
public List<T> getActivateExtension(URL url, String key, String group) {
    String value = url.getParameter(key);
    return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}调用重载方法,将key转成了url对应的value值,多个用逗号分隔:
public List<T> getActivateExtension(URL url, String[] values, String group) {
    checkDestroyed();
    // solve the bug of using @SPI's wrapper method to report a null pointer exception.
    // 用于排序的map,临时存储
    Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(activateComparator);
    List<String> names = values == null
            ? new ArrayList<>(0)
            : Arrays.stream(values).map(StringUtils::trim).collect(Collectors.toList());
    // value值集合,为啥要name命名,应该是因为这个值是扩展点的名字
    Set<String> namesSet = new HashSet<>(names);
    // 1、value中不包括 -default 的值情况,这一步拿到所有default的扩展点(不在namesSet中的其它的),后面用
    if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
        if (cachedActivateGroups.size() == 0) {
            synchronized (cachedActivateGroups) {
                // cache all extensions
                if (cachedActivateGroups.size() == 0) {
                    // 加载配置文件
                    getExtensionClasses();
                    // 遍历所有的标有@Activate注解的信息(每个类的@Activate注解)
                    for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                        // name为扩展点的名字,activate为扩展点的注解信息
                        String name = entry.getKey();
                        Object activate = entry.getValue();
                        // 注解配置的分组和值
                        String[] activateGroup, activateValue;
                        if (activate instanceof Activate) {
                            activateGroup = ((Activate) activate).group();
                            activateValue = ((Activate) activate).value();
                        } else if (Dubbo2CompactUtils.isEnabled()
                                && Dubbo2ActivateUtils.isActivateLoaded()
                                && Dubbo2ActivateUtils.getActivateClass().isAssignableFrom(activate.getClass())) {
                            activateGroup = Dubbo2ActivateUtils.getGroup((Annotation) activate);
                            activateValue = Dubbo2ActivateUtils.getValue((Annotation) activate);
                        } else {
                            continue;
                        }
                        // 扩展点的名字对应的group配置
                        cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
                        // 根据注解配置的value内容,创建不规则的(或称为“锯齿形”)二维数组
                        String[][] keyPairs = new String[activateValue.length][];
                        for (int i = 0; i < activateValue.length; i++) {
                            if (activateValue[i].contains(":")) {
                                keyPairs[i] = new String[2];
                                String[] arr = activateValue[i].split(":");
                                keyPairs[i][0] = arr[0];
                                keyPairs[i][1] = arr[1];
                            } else {
                                keyPairs[i] = new String[1];
                                keyPairs[i][0] = activateValue[i];
                            }
                        }
                        // 扩展点的名字对应的value配置
                        cachedActivateValues.put(name, keyPairs);
                    }
                }
            }
        }
        // traverse all cached extensions
        cachedActivateGroups.forEach((name, activateGroup) -> {
            // 如果入参group为空,或者注解group不为空且包含入参group 且 name 和 -name 不在入参value中 且 注解配置的value是否和url匹配(存在)
            // so,作为默认的扩展点的重要条件:1)、group匹配上、2)、扩展点的名称不在入参values中;3)、注解的value值(key或key-value)和url能匹配上
            if (isMatchGroup(group, activateGroup)
                    && !namesSet.contains(name)
                    && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
                    && isActive(cachedActivateValues.get(name), url)) {
                // 获取对应扩展点,和普通的获取方式一样。
                activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
            }
        });
    }
    // 2、包括 default 的值,注意顺序
    if (namesSet.contains(DEFAULT_KEY)) {
        // default的位置影响排序
        // ArrayList有序集合
        ArrayList<T> extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size());
        for (String name : names) {
            // 扩展name 以-开头。这里的namesSet判断有点冗余了。。。
            if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                continue;
            }
            // name为default,直接添加所有的扩展点
            if (DEFAULT_KEY.equals(name)) {
                extensionsResult.addAll(activateExtensionsMap.values());
                continue;
            }
            // 存在扩展点,添加
            if (containsExtension(name)) {
                extensionsResult.add(getExtension(name));
            }
        }
        return extensionsResult;
    } else {
        // 3、不包括 default 的值,使用activateExtensionsMap对应的顺序
        // add extensions, will be sorted by its order
        for (String name : names) {
            // 扩展name 以-开头,跳过
            if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                continue;
            }
            // 进不来
            if (DEFAULT_KEY.equals(name)) {
                continue;
            }
            // 存在扩展点,添加
            if (containsExtension(name)) {
                activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
            }
        }
        return new ArrayList<>(activateExtensionsMap.values());
    }
}该方法为获取激活扩展实现的核心且复杂方法,可以分成3大步骤看:
1、vlaues中没有“-default”,那么处理defalut的激活类,所谓default是排除values之外的扩展点名称;
2、values中有“default”,根据default的位置来排序所有激活点实例,例如ext1,default,ext2。而default的激活类来自步骤1;
3、values中没有“default”,则在default的基础上,添加values中所有的扩展点,通过getExtension(String name)获取。
关于第1步稍微复杂,哪些会作为defalut的激活扩展类呢?对应逻辑:
// ...	
cachedActivateGroups.forEach((name, activateGroup) -> {
    if (isMatchGroup(group, activateGroup)
            && !namesSet.contains(name)
            && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
            && isActive(cachedActivateValues.get(name), url)) {
        // 获取对应扩展点,和普通的获取方式一样。
        activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
    }
});这个if判断,决定作为默认的扩展点的重要逻辑包括:1)、group匹配上、2)、扩展点的名称不在入参values中;3)、注解的value值(key或key-value)和url能匹配上。
所以,@Activate注解的group和value决定着是否作为default自动激活扩展类的关键点。
关于上述代码中keyPairs变量,作为一个二维数组,它可能是一个不规则的(或称为“锯齿形”)二维数组,如对于注解@Activate(group = "group1", value = {"key1:impl1", "key"});keyPairs=[[key1][value2]],[[key]]
至此,获取自动激活的扩展类分析完,它允许通过getActivateExtension方法获取多个满足条件的扩展类。
四大特性小结
四大特性对比:
| 四大特性 | 对应获取api | 注解 | 是否得到多个 | 是否需要定义额外的扩展类 | 
|---|---|---|---|---|
| 自动包装 | getExtension | @SPI @Wrapper | 1 | 需要定义包装类,并加入配置文件中 | 
| 自动加载 | getExtension getAdaptiveExtension getActivateExtension | @SPI | - | - | 
| 自适应 | getAdaptiveExtension | @SPI @Adaptive | 1 | 如果注解类上,这个类就是定义的自适应类,并加入配置文件中 | 
| 自动激活 | getActivateExtension | @SPI @Activate | 多 | 不需要 | 
SPI四大特性底层都依赖于getExtension(String name)获取对应的扩展点实例,因为其实都是在获取扩展点实现。
ExtensionLoader的10大缓存:
| 变量名 | 类型 | 赋值时机 | 使用时机 | 说明 | 
|---|---|---|---|---|
| cachedNames | ConcurrentMap <Class<?>, String> | 解析配置文件时 | getExtensionName(Class) | 可以通过Class得到扩展点名称 | 
| cachedClasses | Holder<Map<String, Class<?>>> | 解析配置文件时 | getExtensionClasses(),加载接口的扩展类时,所有获取扩展点的方法都用得到 | 扩展点名称到Class的映射,注意:不包括标记@Adaptive注解和被判断为Wrapper扩展类 | 
| cachedActivates | Map<String, Object> | 解析配置文件时 | getActivateExtension | 标记@Activate注解的扩展缓存。扩展点名称到Activate注解信息的映射 | 
| cachedActivateGroups | Map<String, Set<String>> | getActivateExtension | getActivateExtension | 扩展点名称到@Activate注解的group的映射 | 
| cachedActivateValues | Map<String, String[][]> | getActivateExtension | getActivateExtension | 扩展点名称到@Activate注解的value的映射。二维数组的第一维长度对应value的大小 | 
| cachedInstances | ConcurrentMap <String, Holder<Object>> | getExtension | getExtension | 扩展点名称到扩展点实例的映射 | 
| cachedAdaptiveInstance | Holder<Object> | getAdaptiveExtension | getAdaptiveExtension | 接口的自适应扩展点实例的映射 | 
| cachedAdaptiveClass | Class<?> | 解析配置文件时 | getAdaptiveExtension | 如果实现类上为@Adaptive标记,那么会加入该缓存,该实现类会作为自适应类 | 
| cachedDefaultName | String | 进入解析配置文件之前,loadExtensionClasses 方法 | getDefaultExtension | 默认扩展点名称,@SPI注解的value值 | 
| cachedWrapperClasses | Set<Class<?>> | 解析配置文件时 | getExtension | 缓存包装类,就是扩展点实现了接口同时构造器入参也是该接口类型,装饰器模式 | 
总结
本文介绍了Dubbo3引入的作用域模型;然后介绍了SPI的四大特性;这两块内容在整个Dubbo框架中发挥着至关重要的作用。Dubbo是微内核+扩展的设计,框架内部的一些实现本身就是基于SPI扩展进行实现的。所以了解Dubbo的扩展对于理解和学习Dubbo都是前提。
 
                         
                         
             
             
            