SPI机制和Adaptive适配机制
Adaptive适配机制
我们可以使用dubbo的SPI机制, 将dubbo中的一些扩展点通过注解改变原有的实现SPI(“dubbo”), 除此之外Adaptive适配机制则可以帮助我们从参数级别对dubbo的扩展点做出改变。
接口
@SPI("dubbo")
public interface AdaptiveExt2 {
@Adaptive()
String echo(String msg, URL url);
}
实现类
public class DubboAdaptiveExt2 implements AdaptiveExt2 {
@Override
public String echo(String msg, URL url) {
return "DubboAdaptiveExt2";
}
}
public class SpringCloudAdaptiveExt2 implements AdaptiveExt2 {
@Override
public String echo(String msg, URL url) {
return "spring cloud";
}
}
测试
public class Test {
public static void main(String[] args) {
ExtensionLoader<AdaptiveExt2> loader = ExtensionLoader.getExtensionLoader(AdaptiveExt2.class);
AdaptiveExt2 adaptiveExtension = loader.getAdaptiveExtension();
URL url = URL.valueOf("test://localhost/test");
System.out.println(adaptiveExtension.echo("d", url));
}
}
探究
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("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
// 返回实例
return (T) instance;
}
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
// 主要是 getAdaptiveExtensionClass()
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 得到扩展点的类, 此方法和SPI中加载扩展点信息相同
getExtensionClasses();
// cachedAdaptiveClass 在 SPI的分析中介绍过了,在加载扩展点信息时候,如果发现有类上包含Adaptiva注解,则使用 cachedAdaptiveClass 缓存。
if (cachedAdaptiveClass != null) {
// 直接返回, 由此可见如果在类上添加Adaptiva注解,则优先使用
return cachedAdaptiveClass;
}
// 扩展点类上都没 Adaptiva注解, 则去使用代理方式生成
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses
加载dubbo的扩展信息
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
createAdaptiveExtensionClass
创建适配扩展点类, 主要三步
- 生成类信息,使用String
- 得到类加载器
- 编译类
private Class<?> createAdaptiveExtensionClass() {
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);
}
生成类信息
在这里方法中,我们主要观察它生成的类信息
private String createAdaptiveExtensionClassCode() { StringBuilder codeBuilder = new StringBuilder(); // 得到类信息 Method[] methods = type.getMethods(); // 是否有适配注解 boolean hasAdaptiveAnnotation = false; for (Method m : methods) { if (m.isAnnotationPresent(Adaptive.class)) { hasAdaptiveAnnotation = true; break; } } // 没有需要适配注解 直接报错 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注解信息 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 注解 // 省略多余代码 String[] value = adaptiveAnnotation.value(); // 没有值, 使用类名作为默认值 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()}; } // 省略代码 // cachedDefaultName 是SPI注解中的 value, 下面的循环主要是生成了一段代码 // String extName = url.getParameter( key, value ); - key 是方法上 Adaptive 注解的内容,如果没有的话, 使用类名 - value 使用的 defaultExtName (SPI注解的value)值 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); } } // 省略代码 return codeBuilder.toString(); }
createAdaptiveExtensionClassCode 生成的类信息
package com.weimob.spi;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class AdaptiveExt2$Adaptive implements com.weimob.spi.AdaptiveExt2 { public java.lang.String echo(java.lang.String arg0, com.alibaba.dubbo.common.URL arg1) { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = url.getParameter("adaptive.ext2", "dubbo"); if(extName == null) throw new IllegalStateException("Fail to get extension(com.weimob.spi.AdaptiveExt2) name from url(" + url.toString() + ") use keys([adaptive.ext2])"); // 可以看到这里使用的是SPI的内容,按照扩展类的名称获取扩展点信息 // extName 的来源和SPI注解、Adaptive注解相关 com.weimob.spi.AdaptiveExt2 extension = (com.weimob.spi.AdaptiveExt2)ExtensionLoader.getExtensionLoader(com.weimob.spi.AdaptiveExt2.class).getExtension(extName); return extension.echo(arg0, arg1); }} public String getParameter(String key, String defaultValue) { // 去获取key对应的value (URL参数是key?value格式) String value = getParameter(key); if (value == null || value.length() == 0) { // value 为空,使用默认值 return defaultValue; } return value; }
得到信息
- Adaptive适配机制主要功能是得到扩展点的名称
- 使用 Adaptive 注解的value作为 扩展点名称
- value为空时候,选择类名为参数,从URL入参中获取
- 使用 SPI 注解的value作为 扩展点名称的默认名称
- 无 Adaptive 注解时,或者按照Adaptive 注解获取不到信息时候
- 使用 Adaptive 注解的value作为 扩展点名称
- 扩展点实现类添加Adaptive注解时候在所有扩展点中优先级最高