dubbo源码阅读之自适应扩展
自适应扩展机制
刚开始看代码,其实并不能很好地理解dubbo的自适应扩展机制的作用,我们不妨先把代码的主要逻辑过一遍,梳理一下,在了解了代码细节之后,回过头再来思考自适应扩展的作用,dubbo为什么要设计这样一种扩展机制??它的优点在什么地方??
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.
* 该值决定了哪个扩展类会被自动注入。目标扩展名由传入的URL中的参数和Adaptive注解中的value值共同决定。
* value相当于一些key值,用这些key值从URL中获取相应的value值,最终决定扩展名。
* <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}).
* 如果传入的URL中没有符合条件的扩展名,那么使用默认扩展名(默认扩展名由SPI注解的value决定)
* <p>
* For examples, given <code>String[] {"key1", "key2"}</code>:
* 例如,value的值是:String[] {"key1", "key2"}
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* 首先在URL中查找key1,如果存在使用key1作为扩展名
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* 如果URL中找不到key1,查找key2,如果能找到使用key2作为扩展名
* <li>use default extension if 'key2' doesn't appear either</li>
* 如果key2也找不到,那么使用默认扩展名
* <li>otherwise, throw {@link IllegalStateException}</li>
* 如果默认扩展名为空,则抛异常
* </ol>
* If default extension's name is not give on interface's {@link SPI}, then a 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}, its default name is
* <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
* 如果SPI注解中没有给出默认扩展名,就会通过如下规则生成默认扩展名:将驼峰式风格的接口名转换成.号分隔的名称作为扩展名,例如
* 接口名org.apache.dubbo.xxx.YyyInvokerWrapper会被转换成yyy.invoker.wrapper作为扩展名。用这个名称在URL参数中查找。
*
*
* @return parameter key names in URL
*/
String[] value() default {};
}
getAdaptiveExtension
我们先从获取自适应扩展类的入口方法看起,
public T getAdaptiveExtension() {
//检查缓存
Object instance = cachedAdaptiveInstance.get();
//如果缓存为空,加载自适应扩展类,并设置缓存
if (instance == null) {
//如果之前已经加载过,并且抛出了异常,那么这里就不需要再试,直接抛异常
if (createAdaptiveInstanceError == null) {
//惯用法,双检查锁
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);
}
}
}
} else {
throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
主要逻辑就是检查缓存,如果缓存不错存在则加载自适应扩展类,使用双检查锁机制保证在多线程的情况下不会重复加载。都是惯用法,没什么可说的。
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
//1. 获取自适应扩展类并实例化
//2. 自动注入属性值
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
这里使用了dubbo实现的IOC功能,ExtensionLoader的IOC特性我们再上一节已经说过,通过ExtensionFactory接口的扩展类实现对属性的自动注入,不再赘述。
我们的重点还是放在如何生成自适应扩展类上,即getAdaptiveExtensionClass的逻辑。
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
//首先查找所有 资源文件,加载全部扩展类,但并未实例化
// 在加载扩展类的过程中,如果遇到带Adaptive注解的类,会把该类设到缓存cachedAdaptiveClass
//这种情况,由用户来实现自适应扩展的逻辑,比较简单
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//大部分情况下,自适应扩展的逻辑不是由用户实现,而是由框架自动生成的,生成的逻辑就在下面的方法中
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
没啥好说的,继续看下一个方法
createAdaptiveExtensionClass
//重点来了,这个方法定义了生成自适应扩展类代码的总体方案,
//这个方法引出了自适应扩展机制的核心类AdaptiveClassCodeGenerator
private Class<?> createAdaptiveExtensionClass() {
//AdaptiveClassCodeGenerator类是关键代码
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
//Compiler接口也是通过dubbo自己的spi机制加载的,
// Compiler接口的自适应扩展类是预先写好的代码,即AdaptiveCompiler
// 这里有一个有意思的问题,如果compiler的自适应扩展类也由框架自动生成,那么这里就会出现循环调用
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//返回编译后的Class对象
return compiler.compile(code, classLoader);
}
AdaptiveClassCodeGenerator.generate
生成自适应扩展类代码的核心逻辑被封装在AdaptiveClassCodeGenerator类中,AdaptiveClassCodeGenerator的构造方法仅仅设置了要扩展的接口类型以及默认扩展类名(可能为空),我们直接从generate方法看起,
/**
* generate and return class code
* 生成自适应扩展类代码
*/
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
// 如果接口中没有带Adaptive注解的方法,说名该类型不需要自适应扩展,直接抛异常
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
// 生成package信息,与接口的包名相同
code.append(generatePackageInfo());
//生成import信息, 生成了如下的import语句
//import org.apache.dubbo.common.extension.ExtensionLoader
code.append(generateImports());
//生成类定义信息
// public class <接口名>$Adaptive implements <接口全限定名> {
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();
}
总体来看,逻辑还是比较简单的,就是我们平时写代码的步骤,依次生成包信息,import内容,类的具体代码包括每个方法的实现代码。
其中比较重要的是生成方法的实现代码,我们重点看一下这段代码。
generateMethod
private String generateMethod(Method method) {
// 方法的返回类型
String methodReturnType = method.getReturnType().getCanonicalName();
// 方法名称
String methodName = method.getName();
// 方法体,即方法的实现代码,这一步最关键
String methodContent = generateMethodContent(method);
// 方法参数,
// (ParamType0 arg0, ParamType1 arg1, ParamType2 arg2)
String methodArgs = generateMethodArguments(method);
// 异常信息,throws内容
String methodThrows = generateMethodThrows(method);
//最后按照java代码的规范拼接成一个完整的方法代码
return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}
重点是生成方法具体实现代码的逻辑,generateMethodContent方法。
generateMethodContent
生成方法体的逻辑。这个方法比较长,我们分开来看。
/**
* generate method content
* 生成方法体
*/
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
// 这一句应该放到else分支内部
StringBuilder code = new StringBuilder(512);
// 如果方法不带Adaptive注解,就直接生成抛Unsupported异常的实现,
// 也就是说,自适应扩展机制必须要在需要自适应的方法上加Adaptive注解
if (adaptiveAnnotation == null) {
return generateUnsupported(method);
} else {
// 找到URL类型的参数的下标
int urlTypeIndex = getUrlTypeIndex(method);
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
// 如果方法参数列表中有URL类型的参数,那么添加对参数做非空检查的代码
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
// did not find parameter in URL type
// 如果方法参数中没有URL类型,那么查找能够简介获取到URL的参数,这个参数类型应该含有返回类型的是URL的方法
code.append(generateUrlAssignmentIndirectly(method));
}
// 获取Adaptive注解的value
// 如果Adaptive注解的value为空,获取会麻烦一些,将接口的驼峰式名称转换成.分隔的名称
// 如LoadBalance转换成load.balance
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
// 检查Invocation类型的参数
boolean hasInvocation = hasInvocationArgument(method);
// 生成检查Invocation类型参数非空的代码段,
// 只检查第一个参数??Invocation类型的参数只会有一个??
code.append(generateInvocationArgumentNullCheck(method));
// 生成获取扩展名的代码段,
// 扩展名就是资源文件中以k-v存储的key值
code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null?
// 生成扩展名非空检查语句
code.append(generateExtNameNullCheck(value));
// 获取到扩展名后,就可以生成扩展类的获取语句了
code.append(generateExtensionAssignment());
// return statement
// 最后加上函数调用和返回语句
code.append(generateReturnAndInovation(method));
}
return code.toString();
}
生成的方法体大致分为三块
- 获取URL的值。如果参数中有URL类型直接赋值;如果没有就间接获取,查找参数中包含返回URL类型的方法,调用这个方法。此外加上一些非空判断语句。
- 获取扩展名。这一步比较复杂,要考虑的情况比较多。如果Adaptive注解中有protocol就要调用URL的getProtocol方法;用以Adaptive的value数组中的key值依次去URL中获取参数,直到获取到不为空的参数。加上扩展名的非空检查语句。
- 通过ExtensionLoader获取扩展名对应的扩展类,赋值给变量extension
- 最后调用扩展类的相应方法,如果返回值不是void就加上return。
其中获取扩展名代码的生成逻辑较为复杂,我们看一下这段代码。
generateExtNameAssignment
private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
// TODO: refactor it
String getNameCode = null;
// 从后向前遍历可选的扩展名
for (int i = value.length - 1; i >= 0; --i) {
// 对于最后一个value值做如下处理
if (i == value.length - 1) {
if (null != defaultExtName) {
// 如果不是protocol
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
// 如果存在Invocation参数,生成获取方法参数的代码段
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
}
} else {
// 如果是protocol,生成扩区protocol的代码段
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
}
} else {
// 如果默认扩展名为空,在生成的代码中不使用带有默认扩展名的方法
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
}
} else {
getNameCode = "url.getProtocol()";
}
}
//对于不是最后一个的value值做如下处理
} else {
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
}
} else {
getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
}
}
}
// 拼接成赋值语句
// String extName = %s;
return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}
代码生成示例
这样光看代码很难对这块代码有直观的认识,理解也不够深刻,既然这段代码的作用主要是生成代码,那么我们就定义一个自己的接口,并实现一个扩展类,用AdaptiveClassCodeGenerator来生成一下,看生成的代码到底是什么样子。
-
首先是接口
package org.apache.dubbo.common.extension.adaptive.codeGen; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI("default") public interface MyExtension { @Adaptive String adaptiveMethod(String param1, int param2, URL url) throws Exception; @Adaptive("protocol") String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception; void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception; }
-
实现类
package org.apache.dubbo.common.extension.adaptive.codeGen.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension; import org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter; public class DefaultMyExtension implements MyExtension { @Override public String adaptiveMethod(String param1, int param2, URL url) { return String.format("Params passed in are: param1:%s, param2:%d, url:%s"); } @Override public String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception { return String.format("Params passed in are: param1:%s, param2:%d, urlGetter:%s"); } @Override public void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception { } }
-
URL获取类。为了测试简介获取URL
package org.apache.dubbo.common.extension.adaptive.codeGen; import org.apache.dubbo.common.URL; public class URLGetter { private URL url; public URLGetter(URL url){ this.url=url; } public URL getUrl() { return url; } public void setUrl(URL url) { this.url = url; } @Override public String toString() { return "URLGetter{" + "url=" + url + '}'; } }
-
测试类
package org.apache.dubbo.common.extension.adaptive.codeGen; import org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator; import org.junit.jupiter.api.Test; public class AdaptiveClassCodeGeneratorTest { @Test public void testProtocolCodeGen() { AdaptiveClassCodeGenerator generator= new AdaptiveClassCodeGenerator(MyExtension.class,"default"); String code=generator.generate(); System.out.println(code); } }
-
生成的代码。也就是自适应扩展类
package org.apache.dubbo.common.extension.adaptive.codeGen; import org.apache.dubbo.common.extension.ExtensionLoader; public class MyExtension$Adaptive implements org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension { public java.lang.String adaptiveMethod(java.lang.String arg0, int arg1, org.apache.dubbo.common.URL arg2) throws java.lang.Exception { if (arg2 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg2; // Adaptive注解中没有value,用接口名称自动生成的key值 String extName = url.getParameter("my.extension", "default"); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([my.extension])"); org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName); return extension.adaptiveMethod(arg0, arg1, arg2); } public java.lang.String adaptiveMethod2(java.lang.String arg0, int arg1, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg2) throws java.lang.Exception { if (arg2 == null) throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument == null"); if (arg2.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument getUrl() == null"); // 间接获取URL org.apache.dubbo.common.URL url = arg2.getUrl(); // 如果有protocol的key,那么调用URL.getProtocol方法获取协议名称 String extName = (url.getProtocol() == null ? "default" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName); return extension.adaptiveMethod2(arg0, arg1, arg2); } // 为标Adaptive注解的方法直接抛异常 public void noAdaptiveMethod(java.lang.String arg0, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg1) throws java.lang.Exception { throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.noAdaptiveMethod(java.lang.String,org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter) throws java.lang.Exception of interface org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension is not adaptive method!"); } }
这样看是不是直观多了。回过头再去看一遍代码生成逻辑,就好懂多了。
编译加载
代码生成之后,还需要将代码编译为字节码并通过类加载器加载到虚拟机中,获取Class对象,然后才能使用。
负责编译的是org.apache.dubbo.common.compiler.Compiler接口,这个接口的实现类也是通过ExtensionLoader进行加载的,见ExtensionLoader.createAdaptiveExtensionClass方法。
我们看一下Compiler接口
Compiler
// 默认扩展名是javassist
@SPI("javassist")
public interface Compiler {
/**
* Compile java source code.
*
* @param code Java source code
* @param classLoader classloader
* @return Compiled class
*/
Class<?> compile(String code, ClassLoader classLoader);
}
另外,我们再看一下dubbo-common模块中META-INF/dubbo/inetrnal/org.apache.dubbo.common.compiler.Compiler文件中的内容:
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
AdaptiveCompiler类上带有Adaptive注解,所以ExtensionLoader在加载时AdaptiveCompiler类会被设置为自适应扩展类。而在createAdaptiveExtensionClass方法中真是通过Compiler接口的自适应扩展类来进行编译。那么我们看一下AdaptiveCompiler中的逻辑。
AdaptiveCompiler
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
// 可以通过设置静态变量DEFAULT_COMPILER指定默认扩展类
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
// 如果未指定,则获取SPI注解指定的默认扩展类
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
由Compiler接口上的SPI注解,我们知道默认的实现类是JavassistCompiler,所以我们看一下这个类是怎么进行编译的。
JavassistCompiler
JavassistCompiler继承自AbstractCompiler,有一部分逻辑在AbstractCompiler类中,所以我们先看一下
AbstractCompiler.compile
private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");
private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");
public Class<?> compile(String code, ClassLoader classLoader) {
//去除两端空格
code = code.trim();
// 匹配包名
Matcher matcher = PACKAGE_PATTERN.matcher(code);
String pkg;
if (matcher.find()) {
pkg = matcher.group(1);
} else {
pkg = "";
}
// 匹配类名
matcher = CLASS_PATTERN.matcher(code);
String cls;
if (matcher.find()) {
cls = matcher.group(1);
} else {
throw new IllegalArgumentException("No such class name in " + code);
}
// 根据包名和类名拼接类的全限定名
String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;
try {
// 尝试直接加载???类的字节码已经存在??或者类已经被加载过了??
// 什么情况下会出现这种情况??
return Class.forName(className, true, ClassHelper.getCallerClassLoader(getClass()));
} catch (ClassNotFoundException e) {
// 感觉没必要多这一句吧??
// 代码合法性检查应该在生成代码的方法中做,这地方单独做一个花括号检查没什么意义吧?
if (!code.endsWith("}")) {
throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");
}
try {
// 由子类实现
return doCompile(className, code);
} catch (RuntimeException t) {
throw t;
} catch (Throwable t) {
throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));
}
}
}
主要就是拼接处类的全限定名,然后做一些检查,尝试直接加载类。真正的编译过程交由子类实现。
JavassistCompiler.doCompile
@Override
public Class<?> doCompile(String name, String source) throws Throwable {
// 实际执行编译的类
CtClassBuilder builder = new CtClassBuilder();
// 设置类名
builder.setClassName(name);
// process imported classes
// 匹配出所有的import语句,并设置到CtClassBuilder对象中
Matcher matcher = IMPORT_PATTERN.matcher(source);
while (matcher.find()) {
builder.addImports(matcher.group(1).trim());
}
// process extended super class
// 匹配出extends的类型,设置到CtClassBuilder对象中
matcher = EXTENDS_PATTERN.matcher(source);
if (matcher.find()) {
builder.setSuperClassName(matcher.group(1).trim());
}
// process implemented interfaces
// 匹配出implements的类型,设置到CtClassBuilder对象中
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\\,");
Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
}
// process constructors, fields, methods
// 添加方法和域
String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
String className = ClassUtils.getSimpleClassName(name);
Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method-> {
if (method.startsWith(className)) {
builder.addConstructor("public " + method);
} else if (FIELD_PATTERN.matcher(method).matches()) {
builder.addField("private " + method);
} else {
builder.addMethod("public " + method);
}
});
// compile
ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());
// 进行编译,实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去
CtClass cls = builder.build(classLoader);
return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}
实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去。
总结
自适应扩展模块主要分为两块,一是生成自适应扩展类的代码,二是将代码进行编译并加载编译后的字节码。
-
其中dubbo实现的主要逻辑就是代码生成。
自适应加载扩展类的主要思路就是从方法的参数中获取URL参数,可以是方法参数本省,也可以从参数的类型的方法中获取;
然后从URL参数中查找扩展名(查找的范围通过Adaptive注解的value值和SPI注解中的默认值组成),找到扩展名后再由ExtensionLoader根据扩展名加载对应的扩展类,
然后调用对应扩展类的对应方法并返回。 -
编译代码的逻辑主要有javassist库实现,dubbo在javassist库基础上进行了一些简单封装,大部分的逻辑都是直接调用javassist库,这部分不再深入下去,如果对java代码编译感兴趣可以细看。