注:本文基于dubbo v2.6.1
1. @Adaptive 注解在方法上
咱们在dubbo框架中经常会看到类似下面获取自适应实现类的代码
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()
接着我们就跟着getAdaptiveExtension()方法看下dubbo怎样实现自适应的。
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
// 先从缓存获取
if (instance == null) {
if (createAdaptiveInstanceError == null) { // 判断之前有没有报错
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//创建adaptive
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
//抛出异常就记录下来
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
首先先从cachedAdaptiveInstance 成员中获取一下缓存着的自适应实现类对象,如果没有的话再判断createAdaptiveInstanceError是否是null,如果createAdaptiveInstanceError不是null,说明,之前再获取自适应实现类对象的时候报过错,然后被缓存下来了,接着就是执行createAdaptiveExtension方法来创建自适应实现类对象,如果抛出异常就会被记录下来,接着再看下createAdaptiveExtension方法
private T createAdaptiveExtension() {
try {
/**
* 首先 获取class
* 然后创建对象
*/
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
首先这里getAdaptiveExtensionClass()来获取自适应扩展实现对象的class,然后创建对象,再然后就是将对象传入injectExtension方法实现自动注入。
接着看下getAdaptiveExtensionClass方法看看是怎样获取class对象的。
//获取adaptiveExtension class
private Class<?> getAdaptiveExtensionClass() {
//获取extension classs
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
首先是获取所有的扩展实现类class,之前咱们分析过,会将实现类上带有@Adaptive注解的缓存到cachedAdaptiveClass 这个成员中。
那如果没有实现类上带有@Adaptive注解的时候,就要通过createAdaptiveExtensionClass来创建自适应扩展实现类了。
接着我们看下createAdaptiveExtensionClass是怎样创建自适应扩展实现类的
private Class<?> createAdaptiveExtensionClass() {
// 拼装代理类的java代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 进行编译
return compiler.compile(code, classLoader);
}
首先是通过createAdaptiveExtensionClassCode方法来拼装成自适应实现类的代码,接着就是使用spi获取编译器,然后进行编译,加载,返回自适应实现类的class对象。
接下来看下createAdaptiveExtensionClassCode方法是怎样拼装代码的。
private String createAdaptiveExtensionClassCode() {
StringBuilder codeBuilder = new StringBuilder();
Method[] methods = type.getMethods();
// 查找方法上面有没有@Adaptive 注解
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// no need to generate adaptive class since there's no adaptive method found. 没有一个自适用的方法没有必要 生成自适应类了
if (!hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");
for (Method method : methods) {
//返回值
Class<?> rt = method.getReturnType();
//参数类型
Class<?>[] pts = method.getParameterTypes();
//异常
Class<?>[] ets = method.getExceptionTypes();
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
code.append("throw new UnsupportedOperationException(\"method ")
.append(method.toString()).append(" of interface ")
.append(type.getName()).append(" is not adaptive method!\");");
} else {
int urlTypeIndex = -1;
// 查找参数列表中有没有 URL
for (int i = 0; i < pts.length; ++i) {
if (pts[i].equals(URL.class)) {
urlTypeIndex = i;
break;
}
}
// found parameter in URL type
if (urlTypeIndex != -1) { // 说明有
// Null Point check
String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
urlTypeIndex);
code.append(s);
s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
code.append(s);
}
// did not find parameter in URL type
else { // 没有url参数的
String attribMethod = null;
// find URL getter method
LBL_PTS:
for (int i = 0; i < pts.length; ++i) {
// 查找参数里面 有get方法并且返回值是URL的
Method[] ms = pts[i].getMethods();
for (Method m : ms) {
String name = m.getName();
if ((name.startsWith("get") || name.length() > 3)
&& Modifier.isPublic(m.getModifiers())
&& !Modifier.isStatic(m.getModifiers())
&& m.getParameterTypes().length == 0
&& m.getReturnType() == URL.class) {
urlTypeIndex = i;
attribMethod = name;
break LBL_PTS;
}
}
}
if (attribMethod == null) {
throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
+ ": not found url parameter or url attribute in parameters of method " + method.getName());
}
// Null point check 验证有get方法返回值是URL的参数 是否为null 然后get到的URL 是否为null
String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
urlTypeIndex, pts[urlTypeIndex].getName());
code.append(s);
s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
code.append(s);
// URL url = argX.getXXXX();
s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
code.append(s);
}
String[] value = adaptiveAnnotation.value();
// value is not set, use the value generated from class name as the key
if (value.length == 0) {// 没有值
char[] charArray = type.getSimpleName().toCharArray();
StringBuilder sb = new StringBuilder(128);
for (int i = 0; i < charArray.length; i++) {
if (Character.isUpperCase(charArray[i])) { // 是大写字母
if (i != 0) {
sb.append(".");
}
sb.append(Character.toLowerCase(charArray[i]));
} else {
sb.append(charArray[i]);
}
}
value = new String[]{sb.toString()};
}
boolean hasInvocation = false;
for (int i = 0; i < pts.length; ++i) {
if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
// Null Point check
String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
code.append(s);
s = String.format("\nString methodName = arg%d.getMethodName();", i);
code.append(s);
hasInvocation = true;
break;
}
}
String defaultExtName = cachedDefaultName;
String getNameCode = null;
for (int i = value.length - 1; i >= 0; --i) {
if (i == value.length - 1) {
if (null != defaultExtName) {
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], defaultExtName);
else
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()";
}
} 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);
}
}
code.append("\nString extName = ").append(getNameCode).append(";");
// check extName == null?
String s = String.format("\nif(extName == null) " +
"throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
type.getName(), Arrays.toString(value));
code.append(s);
// 获取扩展
s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
code.append(s);
// return statement
if (!rt.equals(void.class)) {
code.append("\nreturn ");
}
//调用 源方法
s = String.format("extension.%s(", method.getName());
code.append(s);
//拼装调用的参数
for (int i = 0; i < pts.length; i++) {
if (i != 0)
code.append(", ");
code.append("arg").append(i);
}
code.append(");");
}
// 拼装代理类
codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");
for (int i = 0; i < pts.length; i++) {
if (i > 0) {
codeBuilder.append(", ");
}
codeBuilder.append(pts[i].getCanonicalName());
codeBuilder.append(" ");
codeBuilder.append("arg").append(i);
}
codeBuilder.append(")");
if (ets.length > 0) { // 异常处理
codeBuilder.append(" throws ");
for (int i = 0; i < ets.length; i++) {
if (i > 0) {
codeBuilder.append(", ");
}
codeBuilder.append(ets[i].getCanonicalName());
}
}
codeBuilder.append(" {");
codeBuilder.append(code.toString());
codeBuilder.append("\n}");
}
codeBuilder.append("\n}");
if (logger.isDebugEnabled()) {
logger.debug(codeBuilder.toString());
}
return codeBuilder.toString();
}
首先,获取接口的所有方法,判断方法上面是否有@Adaptive注解,只要有一个方法有注解就继续执行,没有的话就要抛出异常了。
接着就是拼装package,import,然后自适应扩展类名就是 接口名+$Adaptive 然后实现一下接口,接着就是遍历method, 判断method的上面是否有@Adaptive注解,如果没有将抛出异常的代码追加到code上,如果有@Adaptive注解,找出URL参数在参数列表的位置,如果有URL参数就拼装成判断是否是null,如果是null就抛出异常,然后设置到本地变量,类似下面这样
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
如果没有URL,就遍历参数类型里面有get方法并且get方法不是静态没有参数返回值是URL类型的,没有找到这么个条件的参数的时候,就会抛出异常 ,如果找到判断这个参数不是null,参数获取的URL不是null,然后URL设置到本地变量url中。
接着获取@Adaptive 注解值,如果没有值,就那class 名字来
如果参数有Invocation类型的,然后生成判断null的代码与String methodName = arg0.getMethodName(),并设置标志hasInvocation为true
接着就是遍历@Adaptive 上面value数组,这块主要是实现通过url来获取配置参数的值,这个值就是对应扩展实现类的name,判断extName如果是null的话就要抛出异常,接着就是通过getExtensionLoader(当前扩展接口).getExtension(extName) 来获取扩展实现类对象,然后再通过这个实现类对象调用自己对应的这个方法。可以看下Transporter这个扩展接口的自适应实现类
public class Transporter$Adaptive implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
说白了,这个自适应就是根据接口自己实现一个实现类,然后这个实现类是能够根据@Adaptive 配置的参数值然后去URL中获取对应的值,然后再根据这个值使用spi获取扩展实现类,最后调用这个实现类的对应方法。
2.@Adaptive 注解在类上
当@Adaptive在具体实现类上时,在加载classs的时候,就会被cachedAdaptiveInstance这个成员缓存起来,我们可以在回顾下加载class的时候是怎么处理@Adaptive 注解在类上的情况的。
//缓存自适应拓展对象的类到 `cachedAdaptiveClass`
if (clazz.isAnnotationPresent(Adaptive.class)) {// Adaptive 注解是否在该类上面
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {// Adaptive 注解在类上面的时候,只允许一个实现类上面有@Adaptive注解
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
}
首先判断你这个实现类上有没有@Adaptive注解,如果有的话再判断cachedAdaptiveClass是不是null,如果是null,直接将咱们找出来的这个class 缓存到cachedAdaptiveClass成员中,如果不是null,说明被加载过了,然后判断咱们加载出来的这个class是不是缓存的那个,如果不是的话,就说明同一个扩展点接口有两个带有@Adaptive的扩展实现类,这个dubbo是不允许的,所以直接抛出异常。也就是说,当@Adaptive在扩展实现类时,只允许有一个。