【Dubbo】Adaptive
这里指的Adaptive是dubbo中的一个注解:@Adaptive
。从这个注解的定义上我们可以看到@Target({ElementType.TYPE, ElementType.METHOD})
,它表明@Adaptive
可以用在类、接口和方法上。@Adaptive
代表dubbo的SPI的动态适应能力,如果@Adaptive注解在扩展点实现类上那个该扩展点就是一个包装真实扩展点实例的装饰类;如果注解在方法上那么扩展点的实例就是一个动态代理类,例如Protocol$Adaptive对象。
package com.alibaba.dubbo.common.extension;
/**
* Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance.
*
* @see ExtensionLoader
* @see URL
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
* Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
* in the URL, and the parameter names are given by this method.
* <p>
* If the specified parameters are not found from {@link URL}, then the default extension will be used for
* dependency injection (specified in its interface's {@link SPI}).
* <p>
* For examples, given <code>String[] {"key1", "key2"}</code>:
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* <li>use default extension if 'key2' doesn't appear either</li>
* <li>otherwise, throw {@link IllegalStateException}</li>
* </ol>
* If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
* class name with the rule: divide classname from capital char into several parts, and separate the parts with
* dot '.', for example: for {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is
* <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
*
* @return parameter key names in URL
*/
String[] value() default {};
}
dubbo为什么要设计adaptive?
adaptive设计的目的是为了识别固定已知类和扩展未知类。
注解在类上和注解在方法上的区别?
注解在扩展点实现类上:
代表人工实现,实现一个装饰类(设计模式中的装饰模式),它主要作用于固定已知类,目前整个系统只有2个,AdaptiveCompiler、AdaptiveExtensionFactory。
- a. 为什么AdaptiveCompiler这个类是固定已知的?
因为整个框架仅支持Javassist和JdkCompiler; - b. 为什么AdaptiveExtensionFactory这个类是固定已知的?
因为整个框架仅支持2个objFactory,一个是spi,另一个是spring;ExtensionLoader.getAdaptiveExtension
方法会直接返回这个类的实例
注解在扩展点接口方法上
代表自动生成和编译一个动态的Adpative类,含有@Adaptive的方法中都可以根据方法参数动态获取各自需要真实的扩展点。
它主要是用于SPI,因为spi的类是不固定、未知的扩展类,所以设计了动态$Adaptive
类;ExtensionLoader.getAdaptiveExtension
方法会返回动态编译生成的$Adaptive
例如: Protocol的spi类有injvm、dubbo、registry、filter、listener等很多未知扩展类,ExtensionLoader.getAdaptiveExtension
会动态编译Protocol$Adaptive的类,再通过在动态累的方法中调用ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi类);
来提取对象。
Adaptive加载流程
在dubbo中一般会首先通过ExtensionLoader.getAdaptiveExtension
获取Adaptive扩展。这个方法会首先在扩展点接口的所有实现类中查找类上是否有含有@Adaptive
注解,如果有这样的类直接返回该类的实例,如果没有则会查找扩展点接口的方法是否有@Adaptive
注解并动态编译一个类实现该接口并扩展这些含有@Adaptive
注解的方法。
- 代码执行流程
-----------------------getAdaptiveExtension()
-->getAdaptiveExtension()//为cachedAdaptiveInstance赋值
-->createAdaptiveExtension()
-->getAdaptiveExtensionClass()
-->getExtensionClasses()//为cachedClasses 赋值
-->loadExtensionClasses()
-->loadFile
-->createAdaptiveExtensionClass()//自动生成和编译一个动态的adpative类,这个类是一个代理类
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
-->compiler.compile(code, classLoader)
-->injectExtension()//作用:进入IOC的反转控制模式,实现了动态入注
- 每次只需要从缓存中取出AdaptiveInstance,如果没有命中则创建并加入缓存
/**
* @Author pengyunlong
* @Description 每次只需要从缓存中取出AdaptiveInstance,如果没有命中则创建并加入缓存
* @param
* @Date 2018/6/14 17:03
*/
@SuppressWarnings("unchecked")
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;
}
- 获取AdaptiveExtensionClass字节码并创建实例,然后给该实例注入属性(详细过程参见https://www.jianshu.com/p/764cec6ebb3d)。
/**
* @Author pengyunlong
* @Description 获取AdaptiveExtensionClass字节码并创建实例然后IOC注入
* @param
* @Date 2018/6/14 17:04
*/
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
- 加载实现该扩展点接口的所有class并缓存,
getExtensionClasses()
会将类上有@Adaptive
注解的类缓存到cachedAdaptiveClass
中,如果cachedAdaptiveClass
没有值则需要动态编译一个AdaptiveExtension
/**
* @Author pengyunlong
* @Description 扩展类上有@Adaptive注解的直接返回该类,如果没有则动态创建
* @param
* @Date 2018/6/14 16:28
*/
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses加载含有@Adaptive的扩展点实现类的过程
/**
* @Author pengyunlong
* @Description 从指定目录读取Spi配置文件,分析class并加入缓存
* 1.cachedAdaptiveClass 含有Adaptive注解的class
* 2.cachedWrapperClasses 含有指定参数的构造方法的class
* 3.cachedActivates 含有Activate注解的class
* 4.cachedNames 其余的class
* @param
* @Date 2018/6/7 17:13
*/
private void loadFile(Map<String, Class<?>> extensionClasses, String dir){
String fileName = dir + type.getName();
//遍历SPI文件的每一行配置
while ((line = reader.readLine()) != null) {
Class<?> clazz = Class.forName(line, true, classLoader);
//扩展点实例类上有@Adaptive注解直接设置cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new
}
try {
clazz.getConstructor(type);
//如果扩展点实例的是含有type类型参数的构造方法则加入cachedWrapperClasses集合中
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
//其余情况则将扩展点类名加入cachedNames,key为class
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new
}
}
}
}
}
关于loadfile的一些细节
cachedAdaptiveClass
如果这个class含有adative注解就赋值,例如ExtensionFactory,而例如Protocol在这个环节是没有的。cachedWrapperClasses
只有当该class无adative注解,并且构造函数包含目标接口(type)类型,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper
能命中cachedActivates
剩下的类,包含Activate注解cachedNames
剩下的类就存储在这里。
动态编译过程
- 没有含有@Adaptive注解的扩展点实现类则调用
createAdaptiveExtensionClassCode
动态生成AdaptiveExtension的代码然后编译默认采用Javassist编译
/**
* @Author pengyunlong
* @Description 动态生成代码然后编译
* @param
* @Date 2018/6/14 16:38
*/
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);
}
-
createAdaptiveExtensionClassCode
使用的模板文件adaptive-code-teamplet
代码模板
package <扩展点接口所在包>;
public class <扩展点接口名>$Adpative implements <扩展点接口> {
public <有@Adaptive注解的接口方法>(<方法参数>) {
if(是否有URL类型方法参数?) 使用该URL参数
else if(是否有方法类型上有URL属性) 使用该URL属性
# <else 在加载扩展点生成自适应扩展点类时抛异常,即加载扩展点失败!>
if(获取的URL == null) {
throw new IllegalArgumentException("url == null");
}
根据@Adaptive注解上声明的Key的顺序,从URL获致Value,作为实际扩展点名。
如URL没有Value,则使用缺省扩展点实现。如没有扩展点, throw new IllegalStateException("Fail to get extension");
在扩展点实现调用该方法,并返回结果。
}
public <有@Adaptive注解的接口方法>(<方法参数>) {
throw new UnsupportedOperationException("is not adaptive method!");
}
}
-
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
因为Compiler.class的扩展点实现类中AdaptiveCompiler又@Adaptive注解所以的返回值为AdaptiveCompiler,默认使用JavassistCompiler来解析并编译上一步生成好的代码;
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
}
- 解析过程实际就是使用正则匹配转化为Javassist的代码;(Javassist动态编译参见:https://www.jianshu.com/p/c34cdf5eaf9a)
package com.alibaba.dubbo.common.compiler.support;
/**
* JavassistCompiler. (SPI, Singleton, ThreadSafe)
*/
public class JavassistCompiler extends AbstractCompiler {
......
private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");
@Override
public Class<?> doCompile(String name, String source) throws Throwable {
......
String[] packages = importPackages.toArray(new String[0]);
matcher = EXTENDS_PATTERN.matcher(source);
CtClass cls;
if (matcher.find()) {
String extend = matcher.group(1).trim();
String extendClass;
if (extend.contains(".")) {
extendClass = extend;
} else if (fullNames.containsKey(extend)) {
extendClass = fullNames.get(extend);
} else {
extendClass = ClassUtils.forName(packages, extend).getName();
}
cls = pool.makeClass(name, pool.get(extendClass));
} else {
cls = pool.makeClass(name);
}
......
return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
}
}
动态编译结果
Protocol
所有扩展实现类上都没有@Adaptive
注解,且扩展接口含有两个 @Adaptive
注解的方法:exporter() refer()
,所以dubbo会生成一个动态类Protocol$Adaptive
,且它实现Protocol
接口来扩展这两个Adaptive方法。扩展点接口和最终动态生成Protocol$Adaptive类如下:
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([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);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
解释一下export(com.alibaba.dubbo.rpc.Invoker arg0)
方法:
-
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
从arg0中解析出扩展点名称extName,extName的默认值为@SPI
的value。这是adaptive的精髓:每一个方法都可以根据方法参数动态获取各自需要的扩展点。 -
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
根据extName重新获取指定的Protocol.class
扩展点。如果所有扩展点中含有Wrapper(listener,fiter)则ExtensionLoader.getExtension()
会将真正的实现类通过Wrapper(listener,fiter)包装后返回。 -
extension.export(arg0)
执行目标类的目标方法
这样就实现了每一个方法都可以根据方法参数动态获取各自需要真实的扩展点。