ExtensionLoader源码解析
上一片文章写了dubbo基础架构和SPI的原理,这一篇将着重讲述Dubbo核心工具类ExtensionLoader的工作原理
不积跬步,无以至千里;不积小流,无以成江河。
ExtensionLoader顾名思义,扩展装载器。在dubbo框架中,每一个SPI接口都对应着自己的ExtensionLoader实例。基于这个,我们先看一下基本上使用:
假如我想获取一个SPI接口的扩展可以使用如下代码:
//1 根据接口类取获取它对应的ExtensionLoader实例对象
//2 然后再通过ExtensionLoader实例的getExtension(扩展点名称)获取对应的扩展点
BaseCommand command = ExtensionLoader.getExtensionLoader(BaseCommand.class).getExtension(commandName);
我先整理一下整个调用流程,如果看不懂可以参考这个流程去查看:
从以上流程中可以看到:dubbo获取对象实例或者class都是从缓存中获取,如果缓存中没有,就重新加载,然后存入缓存,然后再从缓存中获取。getExtension方法只能获取非@Adaptive和非Wrapper的扩展点实例。对于Adaptive扩展点的加载底层还是使用getExtension,而Wrapper扩展点,目前版本还没从ExtensionLoader直接获取的方法。这个在下面会详细讨论。
接下来分析一下ExtensionLoader#getExtensionLoader(Class type)的相关源码:
/**
* ExtensionLoader扩展类加载器缓存
*/
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
//必须是接口
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() + "!");
}
//从ExtensionLoader缓存中查询是否已经存在对应类型的ExtensionLoader实例
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//没有就new一个ExtensionLoader实例,并存入本地缓存
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
//返回接口对应的ExtensionLoader实例
return loader;
}
/**
* 判断给定的类是否标有@SPI注解
*/
private static <T> boolean withExtensionAnnotation(Class<T> type) {
return type.isAnnotationPresent(SPI.class);
}
再来分析一下ExtensionLoader#getExtension(String 实例名)获取扩展实例的相关源码:
/**
* 扩展点实例缓存 key=扩展点名称,value=扩展实例的Holder实例
*/
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
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);
//典型的DCL 单例模式
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//如果holder中没有响应的扩展点实例就新建一个
instance = createExtension(name);
//然后把新建的扩展点实例存入holder中,此时holder已经存入缓存,相当于把扩展点实例存入了缓存
holder.set(instance);
}
}
}
//返回实例对象
return (T) instance;
}
/**
* Helper Class for hold a value.
*/
public class Holder<T> {
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
/**
*获取或者创建一个Holder对象
*/
private Holder<Object> getOrCreateHolder(String name) {
// 首先通过扩展名从扩展实例缓存中获取Holder对象
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
//如果没有获取到就new一个空的Holder实例存入缓存
cachedInstances.putIfAbsent(name, new Holder<>());
//返回空的Holder实例
holder = cachedInstances.get(name);
}
return holder;
}
private T createExtension(String name) {
//根据扩展名加载扩展类的class对象,
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
/*
* 从扩展点缓存中获取对应实例对象
*/
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//如果缓存中不存在此类的扩展点,就new一个扩展点实例,并存入缓存
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
//然后从缓存中获取对应实例
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//实现依赖注入,通过setter方法自动注入对应的属性实例
injectExtension(instance);
//自动注入缓存中所有的包装类。
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
//初始化实例并返回
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
以上详细分析了一下获取一个SPI接口的扩展点的过程,其中设计到两个核心方法:
- ExtensionLoader#getExtensionClasses()
获取扩展点默认实现的class , 扫描SPI默认三个扩展点配置文件夹下名为:接口的全限定名 的配置文件,根据文件配置加载配置的扩展点实现类到缓存。
配置文件的格式如下:
key(扩展点实现类的名称)=value(扩展点实现类的class的全限定名)
开始撸源码:
/**
* 扩展点Class缓存 key=扩展名 ,value=对应的class对象
*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
/**
* 加载扩展点的类
* 从缓存中获取所有的扩展点的类,如果没有就重新扫描一次,并缓存起来
*/
private Map<String, Class<?>> getExtensionClasses() {
//首先从缓存中获取已经缓存的扩展点的class
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
//如果没有缓存过,就开始根据配置扫描
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//扫描配置文件,加载配置的扩展点的class
classes = loadExtensionClasses();
//将扫描到的class存入缓存
cachedClasses.set(classes);
}
}
}
return classes;
}
/**
* 三个dubbo SPI默认扫描的路径
*/
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
/**
* SPI 接口类
*/
private final Class<?> type;
/**
* synchronized in getExtensionClasses
* */
private Map<String, Class<?>> loadExtensionClasses() {
//设置扩展点的默认名称,值为@SPI("value")中的value
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// internal extension load from ExtensionLoader's ClassLoader first
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
//兼容历史版本
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
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;
}
/**
* extract and cache default extension name if exists
* 获取注解了SPI的接口中指定的默认的实现类的名称,
* 存在就存入内存缓存中,
* 接口没有标注@SPI注解或者标注了@SPI注解,但是注解中没有指定默认实现类的名称时不做任何操作
*/
private void cacheDefaultExtensionName() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation == null) {
return;
}
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 on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) {
cachedDefaultName = names[0];
}
}
}
要点:
当在接口的@SPI(value)注解上指定value,表示指定这个SPI接口默认实现类的名称。这个值存入cachedDefaultName缓存中
举个栗子:
Dubbo中很重要的一个组件是序列化,而序列化有avro,fastjson,gson,hession2,jdk,kryo,protobuf等等,dubbo默认是的使用hessian2.
看Dubbo在序列化SPI接口的定义:
可以看到就是在SPI注解中指定默认实现的名称为hessian2,也就是在获取默认的序列化器时在Serialization接口对应的ExtensionLoader实例中cachedDefaultName=hessian2 ;再看一下配置文件:
以上讲述了加载的大致流程,这里的ExtensionLoader#loadDirectory方法设计到读取配置文件,从配置文件中解析出扩展名,扩展实现类的完整路径,然后加载实现类的过程,再看一下源码:
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
loadDirectory(extensionClasses, dir, type, false);
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
//配置文件的全路径:默认路径前缀+接口的全限定名
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
//获取当前线程中获取类加载器
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
//获取加载ExtensionLoader.class这个类的类加载器
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
//如果extensionLoaderClassLoaderFirst=true时,且这两个类加载器不同,就默认使用 extensionLoaderClassLoader
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if(urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
if (urls != null) {
//解析并加载配置文件中配置的实现类到extensionClasses中去
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: " +
type + ", description file: " + fileName + ").", t);
}
}
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;
int i = line.indexOf('=');
if (i > 0) {
//配置的接口的实现的名称
name = line.substring(0, i).trim();
//配置的接口的实现类的全限定名
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//加载配置的class到extensionClasses ,,使用Class.forName的方式加载class
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
...
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
/**
* 加载扩展点的实现的class ,这个类的主要作用是对解析到的class进行分类缓存
* @param extensionClasses 装载容器
* @param resourceURL 配置文件资源URL
* @param clazz 扩展点实现类的class
* @param name 扩展点实现类的名称
* @throws NoSuchMethodException 加载异常
*/
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判断配置的实现类是否是实现了type接口
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.");
}
//根据配置中实现类的类型来分类缓存起来
if (clazz.isAnnotationPresent(Adaptive.class)) {
//如果配置中的实现类使用了@Adaptive注解,表示这个类就是一个自适应实现类,缓存到全局缓存cachedAdaptiveClass中
cacheAdaptiveClass(clazz);
} else if (isWrapperClass(clazz)) {
//如果配置的实现类是一个Wrapper类,就缓存到缓存到全局缓存cachedWrapperClasses中
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
//如果配置文件中名称为空,配置文件的内容格式是 com.foo.XxxProtocol 这样,没有指定名称
// com.foo.YyyProtocol
//这种格式的配置现在版本已经弃用,这种格式的配置,实现类需要添加注解@Extension ;由于已经弃用,就不在讨论这种方式
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注解,就将对应的注解信息缓存起来
cacheActivateClass(clazz, names[0]);
for (String n : names) {
//缓存扩展点实现类class和扩展点名称的对应关系
cacheName(clazz, n);
//最后将class存入extensionClasses
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
以上就讲述了使用ExtensionLoader加载一个SPI接口的扩展点实现的完整流程。不知道你们有没有注意到ExtensionLoader#loadClass方法,这个里面就实现了扩展点的分类缓存功能。
- ExtensionLoader#injectExtension(T instance)
直接上源码,说明都在注解里面:
/**
* 实现IOC和AOP的机制,获取到类中所有的setter方法。
* 1 如果setter方法不是私有的和基础数据类型,
* 并且没有注解{@link DisableInject}注解,就自动给相关属性注入默认的实现类
* 2 如果setter方法属性是一个接口,并且次接口有多个实现类,则会更加{@link Adaptive}注解
* 自适应加载配置的默认实现
*/
private T injectExtension(T instance) {
//objectFactory为空就不执行属性注入,这个对象我将会在下面详细介绍,重点!!!
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
//如果不是setter方法就不注入,判断条件:1 public ,2 set开头 ,3 只有一个参数
if (!isSetter(method)) {
continue;
}
/*
*如果扩展点的成员属性的setter方法上使用@DisableInject,在加载扩展点实例时就不会对这个属性进行依赖注入
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
//如果是Java的基本数据类型就直接跳过
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
//获取setter方法中的属性值,例如:setVersion -> version
String property = getSetterProperty(method);
//objectFactory是ExtensionFactory实例;从dubbo容器和spring容器中获取setter属性的默认扩展实例。
//这个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 " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
至此使用ExtensionLoader#getExtension(String name) 获取一个SPI的普通的扩展点实例流程全部理清楚了,那么怎么获取一个自适应或者wrapper接口的扩展点呢?
下面我们来看一下获取自适应扩展点的实例;这就是ExtensionLoader#createAdaptiveExtensionClass()中的获取Compiler接口自适应实现的代码:
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
可以看到:
- 第一步还是通过Cluster.class获取它对应的ExtensionLoader,这个逻辑在上面已经分析过,就不在赘述。
- 第二步是通过它的ExtensionLoader#getAdaptiveExtension()方法获取它的默认的自适应的扩展点实例。既然是默认的,淡然这个方法不需要参数。
我还是先给出整个流程的流程图,不懂的可以结合这个流程图来理解:
使用前提: 要求这个SPI接口中至少有一个接口方法使用了@Adaptive注解 或者 SPI接口的扩展点实现类上标注了@Adaptive注解
我们再来分析ExtensionLoader#getAdaptiveExtension()的详细实现
/**
* 获取SPI接口的自适应扩展点
* 接口的实现类上使用了@Adaptive注解的扩展点实现类
* @return 自适应扩展点实例
*/
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
//首先从自适应扩展点实例缓存中获取自适应的扩展点
Object instance = cachedAdaptiveInstance.get();
//又是经典的DCL模式
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;
}
这里面设计的比较重要的一个方法是ExtensionLoader#createAdaptiveExtension(),我们再来分析一下这个方法的源码:
/**
* 创建一个自适应扩展点实例
*/
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
//1获取到自适应扩展点实例的class
//2新建一个class的实例
//3使用injectExtension方法对这个实例的属性进行依赖注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
/**
* 获取自适应扩展点的class对象
*/
private Class<?> getAdaptiveExtensionClass() {
//加载扩展点的class到cachedAdaptiveClass缓存。 缓存中没有的话就解析配置文件,这个方法上面给过详细介绍这里不在赘述
getExtensionClasses();
//如果自适应扩展点class缓存不为空,就直接使用缓存中的class
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//当这个SPI接口没有带有@Adaptive的实现类时。就使用字节码技术自己构建一个自适应的class
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
/**
* 创建自适应扩展点class文件的Java代码(Java String 形式)
* 然后使用默认的Javassist编译器来编译为对应的扩展点的class
*/
private Class<?> createAdaptiveExtensionClass() {
//根据SPI接口和缓存中扩展点默认的名称来构建一个自适应扩展点的class的代码生产器,生产String类型的java文件
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
//ExtensionLoader#getAdaptiveExtension()加载Compiler接口默认实现类
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//将字符串形式的java文件编译为class
return compiler.compile(code, classLoader);
}
到此ExtensionLoader#getAdaptiveExtension()的基本流程就理清楚了。
前文说到,SPI扩展的还有个特性是自动激活,一个SPI接口可能需要同时激活多个实现,例如Filter接口。那么这个在ExtensionLoader中如何体现呢?
经过仔细阅读发现ExtensionLoader中有4个获取自动激活扩展点的方法:
这里使用了Java的特性多态 - 方法的重载。其实具体的实现逻辑在getActivateExtension(URL url, String[] values, String group)方法中 。
细心朋友可能已经发现getActivateExtension方法返回的结果是一个List ,这就刚好印证了一个接口,自动激活多个实现这个原理!
首先还是列举一个此方法的使用实例;这个就是获取Filter接口自动激活实现类激活的方法
源码如下:
//例如:key=reference.filter , group=consumer
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
这是实际项目中调试的界面,方便看清这些参数的详细信息。
在consumer的配置中dubbo.consumer.filter= rpcFilter,default
这是运行时的截图:
下面直接撸代码:
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
public List<T> getActivateExtension(URL url, String key, String group) {
//通过key值到URL中查找对应key的值作为扩展点的名称的集合
String value = url.getParameter(key);
//然后将value使用逗号分割为字符串数组
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
/**
* Get activate extensions.
* 获取所有自动激活的扩展点,从实现细节可以发现,这个方法只的主要逻辑在于自动激活规则的实现
* 而获取扩展点实例还通过调用getExtension(String name)
* @param url url
* @param values extension point names 扩展点名称 数组
* @param group group
* @return extension list which are activated
* @see org.apache.dubbo.common.extension.Activate
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
//exts存放系统里面自带的并且配置中没有排除的自动激活的扩展点实例
List<T> exts = new ArrayList<>();
/* 要激活的扩展点的名称集合,这个是获取dubbo的配置信息
例如:Filter的配置dubbo.consumer.filter= rpcFilter,default
此时names=["rpcFilter","default"]
*/
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
//在dubbo的扩展点的配置里面,如果配置的扩展点的名称以"-"开头,就不会被激活
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
//刷新缓存,如果没有装载过这个接口,就会读取配置将配置中的扩展点的信息刷到cachedActivates
getExtensionClasses();
//遍历缓存中的激活的扩展点信息cachedActivates中的key=扩展点名称 ,value=扩展点实现类上的@Activate注解对象
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
//扩展点名称
String name = entry.getKey();
//扩展点上的@Activate注解对象
Object activate = entry.getValue();
//获取扩展点的@Activate注解对象上的参数的值
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
//获取注解@Activate的group参数
activateGroup = ((Activate) activate).group();
//获取注解@Activate的value参数
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
//这个主要是为了兼容Dubbo前期的版本,此处不做讨论
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
//匹配group ,value ,来决定是否激活这个扩展点
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
//排除单个扩展
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
//如果信息匹配成功,就调用getExtension(name)方法获取自动激活的扩展点实例
exts.add(getExtension(name));
}
}
//排序,通过扩展点的名称的长度来进行排序
exts.sort(ActivateComparator.COMPARATOR);
}
//定义usrs存放用户自定义的扩展点实现
List<T> usrs = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
//调用getExtension(name)方法获取用户自定义配置中的扩展点实现 ,这个不需要添加@Activate注解。默认要激活
usrs.add(getExtension(name));
}
}
}
//合并所有好激活的扩展点
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
/**
* 匹配group
* @param group 服务的group
* @param groups 注解@Activate中的group字符串数组
* @return true 匹配成功,false失败
*/
private boolean isMatchGroup(String group, String[] groups) {
//如果group是空,默认为要激活
if (StringUtils.isEmpty(group)) {
return true;
}
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
/**
* 匹配key
* @param keys {@link @Active}注解中配置的参数,一个或者多个
* @param url SPI实现的核心对象URL
* @return true 表示是自动激活扩展点,false不是
*/
private boolean isActive(String[] keys, URL url) {
//如果keys没有指定,默认表示要激活
if (keys.length == 0) {
return true;
}
for (String key : keys) {
// @Active(value="key1:value1, key2:value2")
String keyValue = null;
if (key.contains(":")) {
String[] arr = key.split(":");
key = arr[0];
keyValue = arr[1];
}
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
return true;
}
}
}
return false;
}
至此ExtensionLoader的核心功能就已经解析完毕。
在解读ExtensionLoader时,出现了几个重要知识点:扩展工厂ExtensionFactory,字节码编译器org.apache.dubbo.common.compiler.Compiler ,还有高并发中经常使用到的DCL模式。这个将再下次进行详细介绍