有关Dubbo SPI的源码分析请参考前篇博文:一篇短文就能搞定Dubbo SPI 源码及示例,本文介绍SPI自适应扩展相关实例及源码(源码版本:2.7.7)。
1. Dubbo SPI 自适应拓展简介
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。Dubbo 通过自适应拓展机制很好的解决了拓展加载及拓展方法调用的矛盾。自适应拓展机制的实现逻辑大致如:首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。
1.1 Dubbo Adaptive 注解
Adaptive 可注解在类或方法上。当 Adaptive 注解在类上时,Dubbo 不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo 则会为该方法生成代理逻辑。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Adaptive {
/**
*
* 确定要注入的目标扩展。目标扩展名的名称由传递的参数URL中获取。
* 如果在URL中找不到指定的参数,则将使用默认扩展名(即在SPI注解中设置的)依赖注入。
* 如果注解中参数也没有指定,则根据接口名称生成名称(如TestInvoker->test.invoker)
*/
String[] value() default {};
}
2. Dubbo SPI 自适应拓展源码
根据Dubbo SPI源码分析可知,实例化ExtensionLoader的时候,通过SPI获取了ExtensionFactory的拓展并使用getAdaptiveExtension方法获取了对应的自适应拓展,赋值给ExtensionLoader的属性,因此我们首先看下这个方法的源码.
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;
}
1.1. createAdaptiveExtension方法的源码
private T createAdaptiveExtension() {
try {
//1.getAdaptiveExtensionClass获取自适应拓展类
//2.反射newInstance创建实例
//3.使用setter依赖注入扩展injectExtension(主要是手工编码实现的拓展需要注入依赖)
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
1.2. getAdaptiveExtensionClass和createAdaptiveExtensionClass方法源码
private Class getAdaptiveExtensionClass() {
//通过SPI获取所有扩展类(具体逻辑请参考文首提到的SPI源码博客)
getExtensionClasses();
//检查自适应拓展类缓存是否存在,存在直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//创建自适应拓展实例
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class createAdaptiveExtensionClass() {
//生成自适应拓展类代码,生成逻辑下一步介绍
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
//优先使用线程上下文类加载器,不存在使用ExtensionLoader类的加载器
ClassLoader classLoader = findClassLoader();
//通过SPI获取Compiler的ExtensionLoader实例,再获取他的自适应拓展
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//调用Compiler编译生成的拓展类代码,Dubbo 默认使用 javassist 作为编译器
return compiler.compile(code, classLoader);
}
接着分析上一步提到的AdaptiveClassCodeGenerator的generate方法,查看生成拓展代理类的源码。
/**
* 判断你是否存在Adaptive注解的方法
*/
private boolean hasAdaptiveMethod() {
return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
}
/**
* generate and return class code
*/
public String generate() {
// 没用使用Adaptive注解的方法则直接抛出异常
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
//生成包信息的代码(和当前拓展type(接口)同一个包)
code.append(generatePackageInfo());
//生成import信息(导入ExtensionLoader类)
code.append(generateImports());
//生成类名和实现接口(public class type$Adaptive implements type)
code.append(generateClassDeclaration());
//生成接口所有的方法实现代码
Method[] methods = type.getMethods();
for (Method method : methods) {
code.append(generateMethod(method));
}
//添加类结束符
code.append("}");
if (logger.isDebugEnabled()) {
logger.debug(code.toString());
}
return code.toString();
}
/**
* generate method declaration
*/
private String generateMethod(Method method) {
//获取方法返回值类型
String methodReturnType = method.getReturnType().getCanonicalName();
//获取方法名称
String methodName = method.getName();
//生成方法主体,下面介绍(第三部分介绍)
String methodContent = generateMethodContent(method);
//生成方法参数部分
String methodArgs = generateMethodArguments(method);
//生成方法的异常部分
String methodThrows = generateMethodThrows(method);
return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}
generateMethodContent生成方法主体内容,根据有无Adaptive注解分两种逻辑
/**
* generate method content
*/
private String generateMethodContent(Method method) {
//获取方法上的Adaptive注解
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
//没有Adaptive注解的方法实现类直接抛出UnsupportedOperationException异常
return generateUnsupported(method);
} else {//有Adaptive注解生逻辑
//获取方法URL参数对应方法参数中的下标
int urlTypeIndex = getUrlTypeIndex(method);
if (urlTypeIndex != -1) {
//方法中存在URL参数,生成判空检查的代码,如果URL传空抛出IllegalArgumentException异常
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
// 如果没有URL参数,则遍历所有参数,找到存在返回URL的参数,生成判空代码
code.append(generateUrlAssignmentIndirectly(method));
}
// 获取Adaptive的属性值
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
// 是否存在org.apache.dubbo.rpc.Invocation类型参数
boolean hasInvocation = hasInvocationArgument(method);
// 生成Invocation参数判空逻辑
code.append(generateInvocationArgumentNullCheck(method));
//生成扩展名extName代码
code.append(generateExtNameAssignment(value, hasInvocation));
// 生成extName判空代码,空抛出IllegalStateException异常
code.append(generateExtNameNullCheck(value));
// 生成通过getExtensionLoader获取扩展代码
code.append(generateExtensionAssignment());
// 生成返回代码
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
上面生成代码中,还需要提一下的是没有URL参数时调用的generateUrlAssignmentIndirectly方法
private String generateUrlAssignmentIndirectly(Method method) {
//获取所有方法参数类型
Class[] pts = method.getParameterTypes();
Map getterReturnUrl = new HashMap();
// 遍历所有方法类型,找到返回 URL的方法(如getUrl)
for (int i = 0; i 3)
// 方法的访问权限为 public
&& Modifier.isPublic(m.getModifiers())
// 非静态方法
&& !Modifier.isStatic(m.getModifiers())
// 方法参数数量为0
&& m.getParameterTypes().length == 0
// 方法返回值类型为 URL
&& m.getReturnType() == URL.class) {
//保存方法名称和下标映射关系
getterReturnUrl.put(name, i);
}
}
}
// 如果没有找到抛出异常IllegalStateException
if (getterReturnUrl.size()