1. SPI机制简介
SPI机制——即JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制,dubbo基于此机制作了增强版实现,同时该机制也是实现dubbo微内核+插件模式设计思想的实现基石。
首先明确什么是扩展点,扩展点实际上就是一个接口,该接口中定义了一个待实现的方法,SPI机制实现了可以在代码运行过程中自动装配该接口的实现类。
优化点如下:
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过?
getName() ?获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。(但这个其实不是重点)
首先,dubbo默认约定会自动加载该目录下的文件配置 ?META-INF/dubbo/接口全限定名 ,文件内容为:配置名=扩展实现类的全限定名 ,多个实现类用换行符分隔。
配置文件不一定都是?META-INF/dubbo/ ? 目录下,dubbo扩展类加载扫描的时候实际上有三种加载策略,对应会扫描三个文件目录,分别是
DUBBO_INTERNAL_STRATEGY 对应 META-INF/dubbo/internal/
DUBBO_STRATEGY 对应 META-INF/dubbo/
SERVICES_STRATEGY 对应 META-INF/services/
比如以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置配置文本文件为:
配置的文本内容为:
意思也就是说,加载一个name为 dubbo 的扩展实现类到内存中,具体类名为后面的全路径类名
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现。比如:
<dubbo:protocol name="dubbo" />
这样就可以指定我们到底使用扩展点的哪个具体实现类了。
2. dubbo中SPI实现细节
org.apache.dubbo.common.extension.ExtensionLoader_Adaptive_Test
org.apache.dubbo.common.extension.ExtensionLoaderTest
可以结合dubbo源码中这两个测试类的测试用例来理解dubbo中SPI的使用与原理。
2.1 关键注解
(1)@SPI:通过该注解来标识一个扩展点接口,只有在扩展点接口上标识该注解,扩展点才会生效,否则就会报错。
该注解中有个可选配置一个value值,该值代表的是默认情况下所使用的的具体扩展点实现类的名称,注意,该名称要与扩展点实现类配置在META-INF/dubbo/ ? 目录下的配置文件中的key名称相同,如果不存在则会报错。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* default extension name
*/
String value() default "";
}
@SPI("group")
public interface ActivateExt1 {
String echo(String msg);
}
?这个配置就表示会默认采用group对应的扩展点实现类
(2)@Adaptive:自适应注解,该注解一般会添加在扩展点接口中的方法之上,标识该方法的实现会在运行过程中依据URL(该对象中包含了一次RPC调用过程中的所有相关参数,包括方法名,调用参数等信息)中的参数(key-value)进行自动适配,所以每个扩展点接口中的方法参数都必须要有URL参数,哪怕是某个参数对象中包含有URL属性对象也可以,@Adaptive注解标识的方法直到运行时才知道具体执行的扩展点实现类。当然,该注解也可以添加在实现类之上,标识该类就是自适应扩展点实现类。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
* Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
* in the URL, and the parameter names are given by this method.
* <p>
* If the specified parameters are not found from {@link URL}, then the default extension will be used for
* dependency injection (specified in its interface's {@link SPI}).
* <p>
* For example, given <code>String[] {"key1", "key2"}</code>:
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* <li>use default extension if 'key2' doesn't exist either</li>
* <li>otherwise, throw {@link IllegalStateException}</li>
* </ol>
* If the parameter names are empty, then a default parameter name is generated from interface's
* class name with the rule: divide classname from capital char into several parts, and separate the parts with
* dot '.', for example, for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, the generated name is
* <code>String[] {"yyy.invoker.wrapper"}</code>.
*
* @return parameter names in URL
*/
String[] value() default {};
}
同样的,@Adaptive注解也有一个可选配置value项,该项配置标识该扩展点自动适配所依据URL对象中的key名称是什么,如果不配置的话默认是首字母小写的类名作为默认值。会依据该key字段的值来找到对应的扩展点实现类。具体如下代码所示
//扩展点接口
@SPI
public interface Ext2 {
// UrlHolder类型的参数对象中,持有一个URL类型的属性变量
@Adaptive
String echo(UrlHolder holder, String s);
String bang(URL url, int i);
}
//扩展点实现类
public class Ext2Impl1 implements Ext2 {
public String echo(UrlHolder holder, String s) {
return "Ext2Impl1-echo";
}
public String bang(URL url, int i) {
return "bang1";
}
}
然后将扩展点实现类配置到配置文件中
impl1=org.apache.dubbo.common.extension.ext2.impl.Ext2Impl1
此时就代表着,URL对象中包含的参数必须持有一个key为ext2,value为impl1的k-v参数,才能够获取到自适应的扩展点实现。
@Test
public void test_urlHolder_getAdaptiveExtension() throws Exception {
Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
Map<String, String> map = new HashMap<String, String>();
//此处就可以获取到自适应的扩展点实现。
map.put("ext2", "impl1");
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
UrlHolder holder = new UrlHolder();
holder.setUrl(url);
String echo = ext.echo(holder, "haha");
assertEquals("Ext2Impl1-echo", echo);
}
(3)@Activate:自动激活注解,该注解会放置在扩展点接口对应的实现类(或者方法)之上,标识该实现类会自动激活加载(当然,该类必须配置在配置文件中,也就是?META-INF/dubbo/ ? 目录下添加该扩展点接口配置的文件中),一般用于dubbo中的Filter模块加载。
同样的,该注解也有几个配置值,如果不做任何配置,就表示该扩展点实现无条件激活生效。
- group:取值范围是provider或consumer,表示只对服务提供方或服务消费方生效。
- value:作用和上面的几个注解一样,都是从URL参数中提取出参数的key值,如果URL中不存在该key,则该扩展点实现不生效。
- order:表示生效顺序。
- 其他两个自2.7版本以后就被删除了,而且本身很少使用,可以不用考虑这两个数值
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
String[] group() default {};
String[] value() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put before the current one
*/
@Deprecated
String[] before() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put after the current one
*/
@Deprecated
String[] after() default {};
int order() default 0;
}
使用范例(可以参考org.apache.dubbo.cache.filter.CacheFilter的实现案例)
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
@Activate // 无条件自动激活
public class XxxFilter implements Filter {
// ...
}
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
@Activate("xxx") // 当配置了xxx参数,并且参数为有效值时激活
public class XxxFilter implements Filter {
// ...
}
@Activate(group = "provider", value = "xxx") // 只对提供方激活
public class XxxFilter implements Filter {
// ...
}
?(4)扩展点自动包装类:
自动包装扩展点的 Wrapper 类。ExtensionLoader ?在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。
import org.apache.dubbo.rpc.Protocol;
public class XxxProtocolWrapper implements Protocol {
Protocol impl;
public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
// ...
}
Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从?ExtensionLoader ?返回扩展点时,包装在真正的扩展点实现外。即从?ExtensionLoader ?中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个,也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点实现类。
2.1 核心实现类ExtensionLoader源码阅读
ExtensionLoader的使用一般为:
//获取所有自动激活的扩展点实现,也就是配置的@Activate注解类
URL url = URL.valueOf("test://localhost/test");
List<ActivateExt1> list = getExtensionLoader(ActivateExt1.class)
.getActivateExtension(url, new String[]{}, "default_group");
//获取自适应的扩展点实现类
ExtensionLoader<AddExt2> loader = getExtensionLoader(AddExt2.class);
AddExt2 adaptive = loader.getAdaptiveExtension();
//依据配置的扩展点实现类对应的key,获取实现类
WrappedExt impl1 = getExtensionLoader(WrappedExt.class).getExtension("impl1");
//获取默认扩展点实现类
SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension();
(1)public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type):
依据扩展点接口类Class对象,来获取扩展点加载器,这是最初的入口方法,这里会有一层缓存,缓存每一个已经生成的Class对应的扩展点加载器ExtensionLoader对象。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
//type必须为接口类型
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
//判断接口上是否有@SPI注解
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
//先通过缓存获取该type对应的加载器
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//如果缓存中没有,则进行创建一个
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
//这里就是ExtensionLoader的构造方法,可以看到这里还调用了ExtensionFactory的扩展实现
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
(2)public T getAdaptiveExtension():获取自适应扩展点对象实例,这里是比较复杂的。
源码注释如下,按照调用栈编排
public T getAdaptiveExtension() {
//这里首先会尝试从缓存中获取自适应的扩展类实例对象,
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
//延迟双层检查锁,再做一次判断是否成功从缓存中获取到了扩展类实例
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//没有获取到,则进行创建,这里是核心代码
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
private T createAdaptiveExtension() {
try {
//三层代码,最外层的injectExtension负责对最后生成的实例对象进行属性注入
//getAdaptiveExtensionClass负责获取自适应的扩展类Class对象,核心方法
//然后依据这个Class对象调用newInstance方法获取实例对象
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
private Class<?> getAdaptiveExtensionClass() {
//核心方法,在这里扫描配置文件加载该扩展接口对应的所有扩展实现类,该方法是最核心的方法,后续详细解释
getExtensionClasses();
//当调用完 getExtensionClasses方法后,如果存在自适应的扩展点类,会被赋值给cachedAdaptiveClass,那么直接返回,
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//如果没有找到被@Adaptive标注的类,则在这里创建自适应类,通过字符串拼接生成一个类并加载到jvm中
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
//这里通过代码生成器生成目标扩展点的具体实现类
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
//然后通过类加载器和编译器编译并加载这个类字符串到jvm中
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
最后,通过代码生成器生成的自适应类源码如下示例所示:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter (com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
//这里的extName的获取是生成的代码,参考org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generateExtNameAssignment()方法中的逻辑
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
//在这里通过extName获取具体的扩展点实现类对象实例
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
//调用该对象实例对应的扩展点方法
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
通过以上代码分析可以看到,自适应扩展类的获取实现思路其实有些类似于代理模式加策略模式,通过字符串拼接生成当前扩展点自适应实现类的代码,其代理了扩展点接口方法,但其中的扩展点方法并不做具体实现,而是依据扩展点方法的URL参数中的的关键key对应的value值来决定到底使用哪个配置的扩展点实现类。
(3)private Map<String, Class<?>> getExtensionClasses():核心方法,获取并加载META-INF目录下的配置文件中配置的扩展点对应的具体实现类,上面几个方法内部都会调用该方法来获取扩展点实现类。
private Map<String, Class<?>> getExtensionClasses() {
//同样的,先从缓存中尝试获取缓存的所有扩展点实现类Class
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//如果没有获取到,则进行类加载操作
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
//这个作用不大,主要是获取扩展点上@SPI注解中设置的value值
cacheDefaultExtensionName();
//这里存储最后所有扫描加载到的扩展点实现类,key是配置文件中配置的=前面的字符串
Map<String, Class<?>> extensionClasses = new HashMap<>();
//这里的三个扫描加载策略实际上就是三种配置文件目录,分别是META-INF/dubbo/、META-INF/services/、META-INF/dubbo/internal/
for (LoadingStrategy strategy : strategies) {
//核心方法loadDirectory,负责将指定目录下的配置文件中指定的扩展类进行加载
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
}
return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, String... excludedPackages) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if(urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
//上面一大段其实就是为了拿到所有包下META-INF/中扩展点对应的配置文件的路径(dubbo规定配置文件必须是扩展点接口全路径命名)
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
//遍历解析每个文件路径,读取其中配置的文本数据,并加载类,这里是核心方法
loadResource(extensionClasses, classLoader, resourceURL, excludedPackages);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, String... excludedPackages) {
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;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
//然后拿到类的全路径名进行加载类
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
//带有自适应Adaptive注解的类添加到自适应缓存中
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
} else if (isWrapperClass(clazz)) {
//wrapper扩展类缓存放在这里
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
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);
}
}
//其他扩展点实现类放在其他缓存里
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
//带有Activate注解的扩展类缓存处理
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
(4)public T getExtension(String name):依据URL中传递的参数值,获取对应的扩展点实现类对象。
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
//如果值为true,则直接返回默认扩展点实现类对象实例即可
if ("true".equals(name)) {
return getDefaultExtension();
}
//从缓存中获取
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//获取失败则创建,核心方法,里面没什么特殊逻辑,也是通过getExtensionClasses方法获取加载配置的所有扩展点实现类,然后实例化
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
(5)public T getDefaultExtension():获取默认实现类
public T getDefaultExtension() {
//先尝试加载扩展点对应的所有实现类
getExtensionClasses();
//然后看一下有没有设置默认扩展点实现类,没有就直接退出
if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
return null;
}
//有的话,就通过cachedDefaultName获取
return getExtension(cachedDefaultName);
}
(6)public List<T> getActivateExtension(URL url, String[] values, String group):
依据@Activate注解中配置的group和value中配置两个匹配条件,对当前扩展点的所有实现类进行过滤排序,然后返回满足条件的扩展类对象实例集合。
其实说白了,SPI就是通过读取指定目录下的配置文件,从配置文件中读取扩展点接口的具体实现类并加载,然后依据注解上的配置以及URL参数中的属性值来决定到底要是用哪个实现类执行相应操作。
|