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代碼編譯感興趣可以細看。
dubbo源碼閱讀之自適應擴展