原理概览
在dubbo中很多拓展都是通过SPI机制进行加载的,比如Protocol、Cluster、LoaderBalance等。同时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载,在dubbo中这一过程就是通过自适应拓展来完成的。 其实这个过程大概就是三个过程:
- 从URL中获取需要模块的名称
- 通过SPI加载该模块的实现类
- 调用目标方法
源码分析
1. Adaptive的注解
Adaptive注解可以注解在方法也可以注解在类上。Adaptive注解在方法上时会给该方法生成代理逻辑,并且一般不用做于注解在类上。
1.1 获取自适应拓展
createAdaptiveExtension方法中包含了三个逻辑:
- 调用getAdaptiveExtensionClass()获取自适应拓展类
- 通过反射进行实例化
- 调用injectExtension方法向自适应实例中注入依赖
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;
}
private T createAdaptiveExtension() {
try {
// 1. 首先获自适应拓展类
// 2. 实例话
// 3. 通过injectExtension方法对其进行属性注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
private T injectExtension(T instance) { // 这里的instance其实也是从ExtensionLoader<T>中传入的
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
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;
}
在getAdaptiveExtensionClass()方法中,虽然方法名称与一开始的提到的自适应拓展的方法的入口相类似,但是内容是完全不同的。方法的具体实现如下:
private Class<?> getAdaptiveExtensionClass() {
// 通过SPI获取所有的拓展类
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass(); // 创建拓展类
}
private Class<?> createAdaptiveExtensionClass() {
// gengerate 是产生代码的
// findClassLoader获取类加载器
// 进行编译
String code = new AdaptiveClassCodeGenerator(type/** 对象接口 */, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
下面看下自适应拓展累代码的生成:
/**
* test if given type has at least one method annotated with <code>Adaptive</code>
*/
private boolean hasAdaptiveMethod() {
return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
}
/**
* generate and return class code
*/
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
code.append(generatePackageInfo()); // 包申明
code.append(generateImports()); // import构造
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();
}
可以看到hasAdaptiveMethod()方法会首先对type【createAdaptiveExtensionClass()方法中传入的,其实上述方法都是在ExtensionLoader类中进行定义的,而type是该类中的一个属性,所以看起来没有明确的输入输出】进行反射,判断是否至少在一个方法上含有adaptive注解,否则抛出异常。generate()就是具体的生成代码的函数。其中相对来说比较复杂是generateMethod()方法。
/**
* 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);
}
/**
* generate method content
*/
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
return generateUnsupported(method); // 如果方法上没有Adaptive注解就抛出unsupported exception异常
} else {
int urlTypeIndex = getUrlTypeIndex(method); // 参数中含有URL类型,并返回URL在参数列表中的位置
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
code.append(generateUrlNullCheck(urlTypeIndex)); // 如果有URL参数,则进行判空
} else {
// did not find parameter in URL type
code.append(generateUrlAssignmentIndirectly(method)); // 不含有URL参数,
}
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
boolean hasInvocation = hasInvocationArgument(method);
code.append(generateInvocationArgumentNullCheck(method));
code.append(generateExtNameAssignment(value, hasInvocation)); // 获取拓展名
// check extName == null?
code.append(generateExtNameNullCheck(value));
code.append(generateExtensionAssignment());
// return statement
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
接下来就是生成拓展加载与目标方法调用逻辑
private String generateReturnAndInvocation(Method method) {
String returnStatement = method.getReturnType().equals(void.class) ? "" : "return ";
String args = IntStream.range(0, method.getParameters().length)
.mapToObj(i -> String.format(CODE_EXTENSION_METHOD_INVOKE_ARGUMENT, i))
.collect(Collectors.joining(", "));
return returnStatement + String.format("extension.%s(%s);\n", method.getName(), args);
}
以Protocol接口举例说明,上述代码生成的内容如下:
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0,arg1);
其他的就是方法的补全了,不再进行赘述。至此为止,dubbo的自适应拓展就算完成了。