注解@Adaptive
在dubbo中,为了灵活的适配一个接口的多种实现,而不是通过硬编码来指定用哪个实现,提供了@Adaptive注解。这个注解看的出,在类以及方法上使用的。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
源码分析
getAdaptiveExtension
从缓存中获取,没有则创建
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;
}
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
// 获取扩展对象的实例,再注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 获取SPI信息
getExtensionClasses();
// 缓存有,则返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 没有则创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 拼接代码
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
// 获取类加载器
ClassLoader classLoader = findClassLoader();
// 获取编译器,javassist用于动态编程
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 把代码编译成class
return compiler.compile(code, classLoader);
}
为了方便后面源码参考,先贴出拼接好的代码(我这边以Protocol为例)
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract " +
"void org.apache.dubbo.rpc.Protocol.destroy() of " +
"interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int " +
"org.apache.dubbo.rpc.Protocol.getDefaultPort() of " +
"interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension " +
"(org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.
getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension " +
"(org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.
getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
AdaptiveClassCodeGenerator.generate
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());
// 拼接Imports信息
code.append(generateImports());
// 拼接类名以及实现的接口
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();
}
AdaptiveClassCodeGenerator.generateMethod
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
AdaptiveClassCodeGenerator.generateMethodContent
主要是方法体的代码拼接
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
// 不是Adaptive注解的,方法体内抛异常,详情见拼接好的代码
if (adaptiveAnnotation == null) {
return generateUnsupported(method);
} else {
// 获取url的位置
int urlTypeIndex = getUrlTypeIndex(method);
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
// 拼接判断参数是否为空,参考拼接好的refer方法
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
// did not find parameter in URL type
// 不存在URL,方法体中判断是否有某个参数的某个方法有URL返回,
// 有的话,则返回的字符串参考拼接好的代码,没有的话,雷同上面抛异常的拼接字符串,参考拼接好的export方法
// 这个方法,必须是方法名以 get 开头,或方法名大于3个字符,方法的访问权限为 public,
// 非静态方法,方法参数数量为0,方法返回值类型为 URL,不符合抛异常
code.append(generateUrlAssignmentIndirectly(method));
}
// 如果没有值,获取类名的小写
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
// 是否有Invocation 类型的参数
boolean hasInvocation = hasInvocationArgument(method);
// 有的话,则拼接判断是否为空的异常代码
code.append(generateInvocationArgumentNullCheck(method));
// String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null?
// 拼接extName == null这行
code.append(generateExtNameNullCheck(value));
// 拼接getExtension这行
code.append(generateExtensionAssignment());
// return statement
// 拼接返回值的最后一行
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。