一、开始
接着Dubbo源码剖析之SPI机制<一>[https://www.jianshu.com/p/35eb72ec564b]开始讲。我们知道当调用getAdaptiveExtensionClass
方法时,先去META-INF
目录下去查找,如果找不到就会调用createAdaptiveExtensionClass
创建一个,所以这里重点剖析下该方法,看Dubbo是怎么实现的。
private String createAdaptiveExtensionClassCode() {
StringBuilder codeBuidler = new StringBuilder();
//获取type的所有方法,如果方法上加了@Adaptive,则跳出循环
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for(Method m : methods) {
if(m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// 针对方法上没有@Adaptive的情况,直接抛出异常
if(! hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
//开始拼装.java
codeBuidler.append("package " + type.getPackage().getName() + ";");
codeBuidler.append("\nimport " + ExtensionLoader.class.getName() + ";");
codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adpative" + " implements " + type.getCanonicalName() + " {");
for (Method method : methods) {
Class<?> returnType = method.getReturnType();
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?>[] exceptionTypes = 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 {
//针对方法上存在@Adaptive,且方法参数中存在URL,则返回脚标
int urlTypeIndex = -1;
for (int i = 0; i < parameterTypes.length; ++i) {
if (parameterTypes[i].equals(URL.class)) {
urlTypeIndex = i;
break;
}
}
// 有类型为URL的参数
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);
}
// 如果参数没有URL类型,就通过反射获取参数的所有的方法。如果返回参数有URL,则保持该方法名和脚标
else {
String attribMethod = null;
LBL_PTS:
for (int i = 0; i < parameterTypes.length; ++i) {
Method[] ms = parameterTypes[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 adative class for interface " + type.getName()
+ ": not found url parameter or url attribute in parameters of method " + method.getName());
}
// Null point check
String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
urlTypeIndex, parameterTypes[urlTypeIndex].getName());
code.append(s);
s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
urlTypeIndex, attribMethod, parameterTypes[urlTypeIndex].getName(), attribMethod);
code.append(s);
s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod);
code.append(s);
}
//开始解析方法上@Adaptive中的value
String[] value = adaptiveAnnotation.value();
// 没有设置Key,则使用“扩展点接口名的点分隔 作为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 < parameterTypes.length; ++i) {
if (parameterTypes[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);
}
}
...省略部分代码
}
codeBuidler.append("\n}");
if (logger.isDebugEnabled()) {
logger.debug(codeBuidler.toString());
}
return codeBuidler.toString();
}
//获取type的所有方法,如果方法上加了@Adaptive,则跳出循环
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for(Method m : methods) {
if(m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// 针对方法上没有@Adaptive的情况,直接抛出异常
if(! hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
这段代码做的事情就是获取SPI接口中的所有方法,如果没有方法存在@Adaptive
注解,则直接抛出异常,不能为该SPI接口生成适配类。
后面紧接着又开始循环所有方法,之前的那次循环只是做了是否有@Adaptive
注解的判断,本次循环才是真正的处理逻辑。首先获取到方法的返回值、参数数组、异常类型,如果方法上不存在@Adaptive
注解,则给方法体拼装了一个 UnsupportedOperationException
异常,表示该方法不能调用。如果方法上加了@Adaptive
注解,则迭代方法的入参列表,看是否存在有URL
类型的参数,如果存在URL
这个类型的参数,则记录脚标,然后拼装java代码:
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
上面是存在URL类型参数的情况.如果不存在URL
类型的参数则通过反射获取参数类型所有的方法,例如:Invoker.class.getMethods()
.如果存在以get
方法开头,且是非静态、public,又没有入参、且返回值为URL
的方法,则记录脚标和方法名,类似下面这种方法:
public URL getUrl(){
}
如果找不到这种方法,则直接抛出异常fail to create adative class for interface
,创建适配类失败.
继续往下跟,首先通过String[] value = adaptiveAnnotation.value();
获取@Adaptive
注解上的value值,如果value为空,则使用扩展点接口作为value.假设这里的SPI接口为Protocol
,那么value就是protocol
,最后会通过该值是否等于protocol
来封装方法.
if(!"protocol".equals(value[i])){
//如果参数有Invocation
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);
}
接下来看:
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]))
//是否有Invocation参数
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);
}
}
...省略部分代码
如果默认的扩展名cachedDefaultName!=null
,然后判断@adaptive
中的value是否等于protocol
,如果不相等,则判断参数中是否存在Invocation
类型的参数,如果存在就拼接url.getMethodParameter()
方法,否则就拼接url.getParameter()
.如果等于protocol
,则直接拼接url.getProtocol()
.其它方法类似,我就不再赘述了。最后还生成了getExtension
的方法。
T extension = (T) ExtensionLoader.getExtensionLoader(T).getExtension(extName);
s = String.format("extension.%s(", method.getName());
code.append(s);
for (int i = 0; i < parameterTypes.length; i++) {
if (i != 0)
code.append(", ");
code.append("arg").append(i);
}
code.append(");");
上面方法主要是通过getExtension
获取到的实例来调用和SPI接口相同的目标方法。
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
...
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);
}
再回到最上层的createAdaptiveExtensionClass
方法:
private Class<?> createAdaptiveExtensionClass() {
//自己拼装的java类
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
//编译.java文件
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
利用Dubbo的Comiper来编译刚刚拼接出来的java文件,生成Class.其底层还是利用javassist来实现的。
总结
Adaptive机制的大体流程是,在初始化的时候一行一行的读取配置,然后通过反射生成Class,然后判断该Class是否加了@Adaptive
注解,然后缓存起来。我们在获取适配类的时候,判断缓存中是否有适配类,如果存在就直接返回,否则通过javassit编译出一个适配类。但是创建适配类也是有条件的,必须满足几个条件:
- SPI接口必须有加了
@Adaptive
注解的方法 - 方法中要有
URL
类型的参数 - 参数中必须要有getXXX()方法,且返回值为
URL
如果这三个条件都不满足,就会抛出异常。
网友评论