概述
写在前面
我们知道在jdk中提供了SPI 的机制,但是存在一个问题,就是加载的类会在加载时就进行初始化,而不是使用时再进行初始化,此时如果是初始化的类最终并没有使用,很明显这就是一种资源浪费了,对于dubbo,其提供了非常多的扩展点,并且每个扩展点都提供了很多不同的实现,而这些不同的实现实际运行过程中只有其中一个或者几个才会用到,比如Protocol,有DubboProtocol,InjvmProtocol,RestProtocol等,因此为了解决jdk SPI加载就是初始化的问题,dubbo再jdk SPI基础上进行了改进,自己实现提供了dubbo SPI,并在其基础上增加了一些其他的新功能,下面我们就一起来来看下吧!
1:项目对应的模块
dubbo SPI的功能是在dubbo-common
模块提供的,如下图:
其中的org.apache.dubbo.common.extension.ExtensionLoader 类是dubbo SPI的核心类,在使用时正是使用该类来动态加载接口实现类的,正常的使用过程如下,首先定义一个接口以及其实现类,假设如下:
package dongshi.daddy;
import com.alibaba.dubbo.common.extension.SPI;
@SPI
public interface Fallback {
Object invoker();
}
package dongshi.daddy;
public class FallbackImpl implements Fallback {
@Override
public Object invoker() {
return "dubbo调用被熔断";
}
}
然后在classpath下的META-INF/dubbo
(没有则创建),以接口全限定名为名称创建文件,这里是dongshi.daddy.Fallback
,然后添加如下内容在文件中:
fallbackImpl=com.workflow.form.hystrix.fallback.FallbackImpl
其中是获取对应实现类的key是fallbackImpl
,然后通过如下代码获取:
class FakeCls {
void fakeMethod() {
ExtensionLoader<Fallback> loader = ExtensionLoader.getExtensionLoader(Fallback.class);
Fallback fallback = loader.getExtension(fallbackName);
}
}
下面我们就从ExtensionLoader
类开会分析吧!
2:获得拓展配置
想要对拓展进行初始化必须首先获取拓展配置内容,首先看下如何获取拓展配置。
2.1:getExtensionClass
源码如下:
class FakeCls {
private Class<?> getExtensionClass(String name) {
// 2022-01-13 17:38:30
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (name == null)
throw new IllegalArgumentException("Extension name == null");
// 2022-01-13 17:41:26
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null)
throw new IllegalStateException("No such extension "" + name + "" for " + type.getName() + "!");
return clazz;
}
}
2022-01-13 17:38:30
处是判断name和type不为空,比如配置javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
,此时name值就是javassist
,type值就是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
。
2022-01-13 17:41:26
处getExtensionClasses()
是获取所有的扩展类,具体参考2.2:getExtensionClasses
,因为返回的结构是一个字典Map<String, Class<?>>
,所以直接调用get(name)
获取我们需要的扩展类对应的Class对象。
2.2:getExtensionClasses
源码如下:
class FakeCls {
private Map<String, Class<?>> getExtensionClasses() {
// 先从缓存中获取,使用缓存的目的是为了提高性能
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
// 2022-01-13 17:51:37
classes = cachedClasses.get();
if (classes == null) {
// 2022-01-13 18:17:31
classes = loadExtensionClasses();
// 2022-01-13 18:17:36
cachedClasses.set(classes);
}
}
}
return classes;
}
}
2022-01-13 17:51:37
处是DCL双重锁检查,因为如果一个线程被阻塞后的一段时间获取到锁,此时cachedClasses可能已经初始化完毕了,即不再为null,如果不再次进行检查的话就会造成重复的初始化问题,关于DCL的应用可以参考文章基于DCL的单例懒汉模式实现 。2022-01-13 18:17:31
处是加载扩展类,具体参考2.3:loadExtensionClasses
,2022-01-13 18:17:36
处是将加载的类放到缓存中,定义为private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
,Holder类定义如下:
public class Holder<T> {
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
可以看到只是进行了基于泛型的简单定义,来封装一个T类型的value。
2.3:loadExtensionClasses
源码如下:
class FakeCls {
private Map<String, Class<?>> loadExtensionClasses() {
// 获取SPI注解,获取默认的扩展类实现类名称,通过value配置
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
// 逗号分隔
String[] names = NAME_SEPARATOR.split(value);
// 只能配置一个默认实现类,否则抛出java.lang.IllegalStateException异常
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
// 只配置了一个,则取第一个作为默认实现类,设置到缓存private String cachedDefaultName;
// 注意这里是对象属性,并且每个扩展类实现都会使用一个ExtensionLoader封装,即二者是一对一的关系,所以可以这样子设置
if (names.length == 1) cachedDefaultName = names[0];
}
}
// 从服务配置目录加载扩展类们
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 2022-01-14 09:58:24
// 从指定目录中加载扩展类,注意优先级为
// DUBBO_INTERNAL_DIRECTORY(META-INF/dubbo/internal/)
// ->DUBBO_DIRECTORY(META-INF/dubbo/)
// ->SERVICES_DIRECTORY(META-INF/services/)
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
}
2022-01-14 09:58:24
分别从private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
,private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
,private static final String SERVICES_DIRECTORY = "META-INF/services/";
目录下加载扩展类们,这里需要注意加载顺序,优先级如下:
META-INF/dubbo/internal:dubbo内部使用的配置目录。
META-INF/dubbo:外部扩展使用的配置目录。
META-INF/services:jdkSPI配置目录,主要是为了兼容jdk SPI。
META-INF/dubbo/internal
如下图:
META-INF/dubbo
因为是给外部扩展使用的,所以再源码中并没有提供任何配置,META-INF/services
是兼容jdkSPI的,所以也没有提供任何配置。另外关于loadDirectory
方法具体参考2.4:loadDirectory
。
2.4:loadDirectory
从扩展目录中加载扩展实现类们,源码如下:
class FakeCls {
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
// 带有路径的完整文件名称,如META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
// 获取加载当前类的类加载器
ClassLoader classLoader = findClassLoader();
// 可以认为不为null
if (classLoader != null) {
// 从classpath下加载指定文件,也会从jar包中加载
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
// 依次处理每一个
while (urls.hasMoreElements()) {
// 如:jar:file:/D:/program_files/mvn_rep/com/alibaba/dubbo/2.6.6/dubbo-2.6.6.jar!/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
java.net.URL resourceURL = urls.nextElement();
// 从指定的路径加载资源
// 2022-01-14 10:32:12
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
}
2022-01-14 10:32:12
处是从指定的URL路径加载资源,具体参考2.4.1:loadResource
。
2.4.1:loadResource
源码如下:
class FakeCls {
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
// 通过java.net.URL#openStream获取流,并开始读取
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
// 以为#后代表注释内容,所以需要过滤掉,比如mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper # 模拟类
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('=');
// 分别获取名称和值,如cache=com.alibaba.dubbo.cache.filter.CacheFilter
// name:如cache line:com.alibaba.dubbo.cache.filter.CacheFilter
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
// 2022-01-14 13:29:10
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);
}
}
}
} finally {
// 关闭流,也可考虑try with resource语法糖方式来做
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
}
2022-01-14 13:29:10
处是加载类,具体参考2.4.2:loadResource
。
2.4.2:loadResource
源码如下:
class FakeCls {
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
// clazz必须是type的子类,不然抛出java.lang.IllegalStateException
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
// 2022-01-16 17:13:10
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 因为当前类是标注了@Adaptive注解的类,后续在生成动态子类时需要用到(通过getAdaptiveExtension方法可获取到)
// 为了不需要重复加载,所以这里直接设置到缓存变量中,后续便可直接使用,提高性能
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
// 如果时缓存的Adaptive注解clz已经有值,并且和当前的不相等,则说明有多个扩展类都使用了@Adaptive注解,这种情况
// 不被允许,会抛出java.lang.IllegalStateException
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
// 2022-01-17 10:40:07
} else if (isWrapperClass(clazz)) {
// 保存到集合中
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
// 就是普通的扩展类,即没有使用@Adaptive注解,也没有一个使用接口作为唯一参数的构造函数
} else {
// 这行代码是要保证有一个无参数的构造函数,即默认构造函数,因为后续需要用到创建对象
clazz.getConstructor();
// 2022-01-17 13:15:19
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
// 应该不会有这种情况吧!!!
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 扩展名可能是逗号分隔的多个,如concreteUseInSubClsInterface2,concreteUseInSubClsInterface21=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface2
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
// 2022-01-17 16:43:36
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
// private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
cachedActivates.put(names[0], activate);
}
for (String n : names) {
// private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
// 扩展类class->扩展类名字,只保存第一个name
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
// Map<String, Class<?>> extensionClasses,扩展类名称->扩展类class
// 如果是当前扩展类名称不包含在结果中,则put
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
// 不同的扩展类使用了相同的扩展类名称,则抛出java.lang.IllegalStateException
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
2022-01-16 17:13:10
处是处理@Adaptive ,该注解用来实现根据用户设置的参数动态选择具体的扩展类。2022-01-17 10:40:07
处是判断是否存在一个使用当前type作为唯一参数的构造函数,源码如下:
class FakeCls {
private boolean isWrapperClass(Class<?> clazz) {
// 存在一个使用当前type为唯一参数的构造函数的话则返回true,否则返回false
try {
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
}
2022-01-17 13:15:19
处是处理在SPI配置文件中没有设置name的情况时,通过findAnnotationName
方法获取名称,如下的第二行就是这种情况:
concreteUseInSubClsInterface2=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface2
dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface3
源码如下:
class FakeCls {
private String findAnnotationName(Class<?> clazz) {
// 获取类上使用的@Extension注解,并使用其value作为name结果
com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
// 如果是没有配置@Extension注解
if (extension == null) {
// 获取类的简单名称
String name = clazz.getSimpleName();
// 如果是类的简单名称是以接口的简单名称结尾,则去除接口的简单名称后缀,如接口简单名称是UseInSubClsInterface,扩展类名称是Concrete4UseInSubClsInterface
// 则之类结果就是Concrete4
if (name.endsWith(type.getSimpleName())) {
name = name.substring(0, name.length() - type.getSimpleName().length());
}
// 转小写
return name.toLowerCase();
}
return extension.value();
}
}
2022-01-17 16:43:36
处是处理类上标记了@Activate
注解的情况,具体参考5:@Activate
。到这里,在DUBBO SPI配置文件中配置的信息,已经成功加载并转换为程序的相关变量进行存储,接下来看下如何获取ExtensionLoader对象,因为在程序中获取扩展类一般就是通过该对象完成的,具体参考4:获取扩展类加载器
。
4:获取扩展类加载器
在dubbo的代码里,经常看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
就是通过扩展类加载器ExtensionLoader来获取目标扩展类,下面我们就从getExtensionLoader
方法开始分析。
4.1:getExtensionLoader
源码如下:
class FakeCls {
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 interface!");
}
// 必须有SPI注解
/*
private static <T> boolean withExtensionAnnotation(Class<T> type) {
return type.isAnnotationPresent(SPI.class);
}
*/
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// 从private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
// 中获取,获取不到,则new一个
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 2022-01-17 17:49:55
// 不存在才put
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
}
2022-01-17 17:49:55
处是在不存在指定类型的扩展类加载器时才put,关于ExtensionLoader构造函数具体参考4.1.1:ExtensionLoader构造函数
。
4.1.1:ExtensionLoader构造函数
源码如下:
class FakeCls {
private ExtensionLoader(Class<?> type) {
// 设置扩展类对应的接口class
this.type = type;
// 2022-01-18 10:59:55
// 设置扩展工厂,实现类似于Spring IOC的功能,注意这里的3目,如果是不加这个判断,将会造成死循环
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
}
2022-01-18 10:59:55
处是设置扩展工厂,ExtensionFactory有3个子类,一个是@Adaptive 子类,另外两个扩展类是通过SPI和spring容器获取对象,如下:
使用上是在调用方法injectExtension(instance)
注入实例的属性值,即通过ExtensionFactory获取对应属性的实例,并进行set。关于ExtensionFactory具体参考6:ExtensionFactory
。
4.2:getExtension
通过该方法获取扩展类的对应的对象实例,源码如下:
class FakeCls {
// 通过提供的名称name获取扩展类
@SuppressWarnings("unchecked")
public T getExtension(String name) {
// 检测name的合法性,不合法则抛出java.lang.IllegalArgumentException异常
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
// 2022-01-19 10:15:00
// name为true则获取默认扩展类
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从缓存中获取,可以认为为null
Holder<Object> holder = cachedInstances.get(name);
// 这个判断只是提前创建一个用于存放对应name的Holder
if (holder == null) {
// 执行putIfAbsent的原因是因为考虑并发的场景
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
// 2022-01-19 10:34:01
instance = holder.get();
if (instance == null) {
// 2022-01-19 10:39:50
instance = createExtension(name);
// 设置到holder
holder.set(instance);
}
}
}
return (T) instance;
}
}
2022-01-19 10:15:00
处是当name=true
时获取默认的扩展类,具体参考4.2.1:getDefaultExtension
。2022-01-19 10:34:01
DCL ,防止获取锁时进入同步代码块的条件已经不再满足。2022-01-19 10:39:50
处是创建扩展类,具体参考4.2.2:createExtension
。
4.2.1:getDefaultExtension
源码如下:
class FakeCls {
public T getDefaultExtension() {
// 加载扩展类
getExtensionClasses();
// 如果cachedDefaultName没有,则renturn null,其中cachedDefaultName值就是在接口的@SPI注解上配置的value
// 当且仅当配置了一个值时才会被设置到cachedDefaultName中,如@SPI("apple"),apple就是默认值,如@SPI("apple,banana"),则不会设置默认值
// 另外需要注意判断"true".equals(cachedDefaultName),即默认值不能为true,如果是没有这个判断会发生当设置@SPI("true"),又执行getExtension("true")时。就会发生死循环,因为会发生无限递归调用
if (null == cachedDefaultName || cachedDefaultName.length() == 0
|| "true".equals(cachedDefaultName)) {
return null;
}
// 通过默认值获取扩展类
return getExtension(cachedDefaultName);
}
}
4.2.2:createExtension
源码如下:
class FakeCls {
private T createExtension(String name) {
// 获取对应的Class对象
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 缓存获取,可以认为为null
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 直接反射创建实例
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 2022-01-19 10:55:16
// 注入属性,即有setter的类变量
injectExtension(instance);
// 存在wrapper class,即有一个使用当前type作为唯一参数的构造函数的类
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
// 如果有,则创建wrapper对应的实例
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
// 循环创建wrapper class实例,并调用方法injectExtension注入其属性值
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
}
2022-01-19 10:55:16
处是注入属性,具体参考4.2.3:injectExtension
。
4.2.3:injectExtension
源码如下:
class FakeCls {
private T injectExtension(T instance) {
try {
// 2022-01-19 12:29:04
if (objectFactory != null) {
// 获取当前实例的所有方法
for (Method method : instance.getClass().getMethods()) {
// 寻找方法签名如public void setXxxx(Xxx xxx) {} 的方法
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
// 如果是属性使用了@DisableInject注解,则忽略不注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获取参数类型
Class<?> pt = method.getParameterTypes()[0];
try {
// 获取属性名
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 通过getExtension方法获取要注入的对象,这里调用的是AdaptiveExtensionFactory#getExtension
Object object = objectFactory.getExtension(pt, property);
// 如果不为null,则反射调用
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
}
2022-01-19 12:29:04
处的objectFactory是ExtensionFactory的一个实例,具体参考6:ExtensionFactory
。到这里,获取普通的扩展对象已经解析完毕了,接下来看下如果获取使用@Adaptive 注解的自适应扩展类,具体参考4.3:获得自适应的拓展对象
。
4.3:获得自适应的拓展对象
获取自适应扩展对象有专门的API方法getAdaptiveExtension
,一般像ExtensionLoader.getExtensionLoader(UseInSubClsInterface.class).getAdaptiveExtension()
这样使用,接下来一起看下这部分内容。
4.3.1:getAdaptiveExtension
源码如下:
class FakeCls {
public T getAdaptiveExtension() {
// 缓存获取,可以认为为null
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 认为true
if (createAdaptiveInstanceError == null) {
// JVM级别同步
synchronized (cachedAdaptiveInstance) {
// 2022-01-19 14:20:55
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 2022-01-19 14:26:12
instance = createAdaptiveExtension();
// 添加到缓存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
}
2022-01-19 14:20:55
处是DCL 防止因为获取同步????后最初进入同步代码块的状态判断发生变更而导致程序错误。2022-01-19 14:26:12
处是创建自适应拓展类实例,具体参考4.3.2:createAdaptiveExtension
。
4.3.2:createAdaptiveExtension
源码如下:
class FakeCls {
private T createAdaptiveExtension() {
try {
// 2022-01-19 14:40:31
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
}
2022-01-19 14:40:31
处injectExtension是注入对象属性值,具体参考4.2.3:injectExtension
,getAdaptiveExtensionClass
是获取自适应扩展类的class对象,具体参考4.3.3:getAdaptiveExtensionClass
。
4.3.3:getAdaptiveExtensionClass
源码如下:
class FakeCls {
private Class<?> getAdaptiveExtensionClass() {
// 2022-01-19 15:03:32
// 获取扩展类
getExtensionClasses();
// 有一种场景这里会不为null,就是将@Adaptive注解使用在扩展接口子类上的情况
// 有两种场景这里会为null,@Adaptive使用在接口的方法上这里为null,根本就没有使用@Adaptive注解的时候为null
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 2022-01-19 15:07:19
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
}
2022-01-19 15:03:32
处参考2.2:getExtensionClasses
。2022-01-19 15:07:19
处是将@Adaptive注解使用在接口的方法上的场景不考虑没有使用的场景
,具体参考4.3.4:createAdaptiveExtensionClass
。
4.3.4:createAdaptiveExtensionClass
源码如下:
class FakeCls {
private Class<?> createAdaptiveExtensionClass() {
// 2022-01-19 15:49:11
String code = createAdaptiveExtensionClassCode();
// 以下是将动态生成的java代码,并最终生成对应的class,相当于经过了javac Xxx.java的过程
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
}
2022-01-19 15:49:11
处是生成接口的动态子类的类字符串,所作的事情就是根据url中的参数来动态获取扩展实现类并调用目标方法,如下是我本地的生成的结果:
package dongshi.daddy.adaptive;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
// 实现扩展类接口,即是扩展类接口的一个子类,FruitGranter接口定义如下:
/*
@SPI("apple")
public interface FruitGranter {
Fruit grant();
// @Adaptive
@Adaptive({ "find.fruit.extenstion" })
String watering(URL url);
}
*/
public class FruitGranter$Adaptive implements dongshi.daddy.adaptive.FruitGranter {
// 实现watering方法,该方法在接口我们标注了@Adaptive注解,因此会对该方法动态生成逻辑
public java.lang.String watering(com.alibaba.dubbo.common.URL arg0) {
// 必须有URL参数,强制要求,因为要从URL上提取参数,获取扩展类
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
// 通过参数find.fruit.extenstion获取扩展类名称,默认是apple
String extName = url.getParameter("find.fruit.extenstion", "apple");
// 没有获取到扩展类的名称,则抛出运行时异常java.lang.IllegalStateException
if (extName == null)
throw new IllegalStateException("Fail to get extension(dongshi.daddy.adaptive.FruitGranter) name from url(" + url.toString() + ") use keys([find.fruit.extenstion])");
// 根据扩展类名称获取扩展类实例
// getExtensionLoader参考:4.1:getExtensionLoader
// getExtension(extName)参考:4.2:getExtension
dongshi.daddy.adaptive.FruitGranter extension = (dongshi.daddy.adaptive.FruitGranter) ExtensionLoader.getExtensionLoader(dongshi.daddy.adaptive.FruitGranter.class).getExtension(extName);
// 调用目标扩展类的watering方法
return extension.watering(arg0);
}
// 实现grant方法,因为该方法在接口中没有标注@Adaptive注解,所以默认就是抛出java.lang.UnsupportedException
public dongshi.daddy.adaptive.Fruit grant() {
throw new UnsupportedOperationException("method public abstract dongshi.daddy.adaptive.Fruit dongshi.daddy.adaptive.FruitGranter.grant() of interface dongshi.daddy.adaptive.FruitGranter is not adaptive method!");
}
}
到这里自适应拓展类获取的相关源码就分析完毕了,还省最后一个,即使用了@Activate
注解的类,关于该注解参考5:@Activate
,获取激活扩展类是通过方法getActivateExtension
,具体参考7:获得激活的拓展对象数组
。
5:@Activate
该注解用来根据指定的条件激活扩展类,具体看下源码:
// 根据指定的条件来自动激活扩展类,这些条件可以是:
// 1:当前是服务提供者端,还是服务消费者端,对应的方法是String[] group() default {};
// 2:在URL上是否包含指定的key,对应的方法是String[] value() default {};
// 可以调用方法ExtensionLoader#getActivateExtension(URL, String, String)获取指定条件下可以被激活的扩展类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
// 激活的组,即服务提供者端,服务消费者端,对应的值是public static final String PROVIDER = "provider"; public static final String CONSUMER = "consumer";
// 在调用方法ExtensionLoader#getActivateExtension(URL, String, String)时会被用来进行匹配
String[] group() default {};
// 当在URL上有指定的参数key时激活当前的扩展类。例如,当前值配置的是@Activate("cache, validation"),只有在URL中包含key cache或者是validation时
// 该扩展类才会被激活
String[] value() default {};
// 需要放在当前扩展类之前的扩展类集合,可忽略
String[] before() default {};
// 需要放在当前扩展类之后的扩展类集合,可忽略
String[] after() default {};
// 排序号,多个扩展类,并且需要排序时使用
int order() default 0;
}
6:ExtensionFactory
这是一个用于获取扩展类的工厂,接口如下:
@SPI
public interface ExtensionFactory {
// 获取指定类型的指定名称的扩展类
<T> T getExtension(Class<T> type, String name);
}
该类有3个子类,类图如下:
6.1:AdaptiveExtensionFactory
这是使用了@Adaptive 注解的自适应拓展类实现类,源码如下:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
// 存储其他的非自适应扩展实现类,目前配置如下:
/*
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
*/
// 其中名称为adaptive的是本类,不会获取添加到集合中
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
// 注意这里调用的是getExtension方法,不会获取本类,想要获取本类的话需要调用getAdaptiveExtension
list.add(loader.getExtension(name));
}
// 调用方法Collections#unmodifiableList转换为不可修改集合
factories = Collections.unmodifiableList(list);
}
// 获取扩展类的方法
@Override
public <T> T getExtension(Class<T> type, String name) {
// 依次从接口的非自适应扩展类子类中获取,没有自己实现的话就是通过SPI,spring容器获取
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
6.2:SpiExtensionFactory
需要注意:仅支持@Adaptive自适应扩展子类的注入
源码如下:
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
// 返回对应type接口的adaptive自适应扩展类,因此仅仅支持存在adaptive子类的接口的自动注入
return loader.getAdaptiveExtension();
}
}
return null;
}
}
6.3:SpringExtensionFactory
该类支持依赖于spring的IOC容器ApplicationContext完成属性注入,源码如下:
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
// 查找目标对象的容器集合,通过调用方法addApplicationContext添加
private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();
private static final ApplicationListener shutdownHookListener = new ShutdownHookListener();
// 添加spring容器ApplicationContext
public static void addApplicationContext(ApplicationContext context) {
contexts.add(context);
BeanFactoryUtils.addApplicationListener(context, shutdownHookListener);
}
// 移除spring容器ApplicationContext
public static void removeApplicationContext(ApplicationContext context) {
contexts.remove(context);
}
// 获取当前的spring容器
public static Set<ApplicationContext> getContexts() {
return contexts;
}
// 清空spring容器集合
public static void clearContexts() {
contexts.clear();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
// 遍历所有的spring容器集合
for (ApplicationContext context : contexts) {
// 包含则调用getBean获取
if (context.containsBean(name)) {
Object bean = context.getBean(name);
// 如果是type类型的,即当前接口的子类,则强转返回
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
if (Object.class == type) {
return null;
}
// 执行到这里,说明是byName没有获取到,则通过byType的方式尝试获取
// 关于几种注入方式可以参考文章:https://blog.csdn.net/wang0907/article/details/115271264
for (ApplicationContext context : contexts) {
try {
return context.getBean(type);
} catch (NoUniqueBeanDefinitionException multiBeanExe) {
logger.warn("Find more than 1 spring extensions (beans) of type " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension of that type.");
} catch (NoSuchBeanDefinitionException noBeanExe) {
if (logger.isDebugEnabled()) {
logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
}
}
}
logger.warn("No spring extension (bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
return null;
}
private static class ShutdownHookListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
shutdownHook.destroyAll();
}
}
}
}
7:获得激活的拓展对象数组
在程序中想要获取激活的扩展对象数组,一般是执行代码ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group)
,下面我们就从getActivateExtension
方法开始分析。
7.1:getActivateExtension
源码如下:
class FakeCls {
public List<T> getActivateExtension(URL url, String key, String group) {
// 从url上获取参数值,即要激活的扩展类名称
String value = url.getParameter(key);
// 2022-01-19 16:31:26
// 获取符合自动激活条件的扩展类集合
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
}
2022-01-19 16:31:26
处源码如下:
class FakeCls {
public List<T> getActivateExtension(URL url, String[] values, String group) {
// 结果集合
List<T> exts = new ArrayList<T>();
// 获取要激活的扩展类的名称集合
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
// 如果是不包含"-default"
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 获取加载类classes
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
// 扩展类名称
String name = entry.getKey();
// 扩展类@Activate注解
Activate activate = entry.getValue();
// group:consumer/provider,
// activate.group():注解配置的激活的group
if (isMatchGroup(group, activate.group())) {
// 获取对应的扩展类,为什么先get,而不是在if内部get?
T ext = getExtension(name);
if (!names.contains(name) // names是自定义的,即过滤自定义的
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name) // 不包含"-xxx",即显示排除
&& isActive(activate, url)) { // 必须是激活的,即@Activate.value配置的值在URL中存在key且不为空("", false,null,0,N/A)
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
// 用户自定义的
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
// 不以“-”开头,并且不包含"-xxx",即排除当前name
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
// 如果时“name=default”
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
// 将urs添加到exts的最前面
exts.addAll(0, usrs);
// 因为已经添加所以clear
usrs.clear();
}
} else {
// 获取并添加
T ext = getExtension(name);
usrs.add(ext);
}
}
}
// 自定义的集合添加到系统的集合
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
}
最后
以上就是俊逸金针菇为你收集整理的dubbo之SPI分析的全部内容,希望文章能够帮你解决dubbo之SPI分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复