這里指的Adaptive是dubbo中的一個注解:@Adaptive。從這個注解的定義上我們可以看到@Target({ElementType.TYPE, ElementType.METHOD}),它表明@Adaptive可以用在類、接口和方法上。
@Adaptive代表dubbo的SPI的動態(tài)適應(yīng)能力,如果@Adaptive注解在擴(kuò)展點實現(xiàn)類上那個該擴(kuò)展點就是一個包裝真實擴(kuò)展點實例的裝飾類;如果注解在方法上那么擴(kuò)展點的實例就是一個動態(tài)代理類,例如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為什么要設(shè)計adaptive?
adaptive設(shè)計的目的是為了識別固定已知類和擴(kuò)展未知類。
注解在類上和注解在方法上的區(qū)別?
注解在擴(kuò)展點實現(xiàn)類上:
代表人工實現(xiàn),實現(xiàn)一個裝飾類(設(shè)計模式中的裝飾模式),它主要作用于固定已知類,目前整個系統(tǒng)只有2個,AdaptiveCompiler、AdaptiveExtensionFactory。
- a. 為什么AdaptiveCompiler這個類是固定已知的?
因為整個框架僅支持Javassist和JdkCompiler; - b. 為什么AdaptiveExtensionFactory這個類是固定已知的?
因為整個框架僅支持2個objFactory,一個是spi,另一個是spring;
ExtensionLoader.getAdaptiveExtension方法會直接返回這個類的實例
注解在擴(kuò)展點接口方法上
代表自動生成和編譯一個動態(tài)的Adpative類,含有@Adaptive的方法中都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要真實的擴(kuò)展點。它主要是用于SPI,因為spi的類是不固定、未知的擴(kuò)展類,所以設(shè)計了動態(tài)$Adaptive類;
ExtensionLoader.getAdaptiveExtension方法會返回動態(tài)編譯生成的$Adaptive
例如: Protocol的spi類有injvm、dubbo、registry、filter、listener等很多未知擴(kuò)展類,ExtensionLoader.getAdaptiveExtension會動態(tài)編譯Protocol$Adaptive的類,再通過在動態(tài)累的方法中調(diào)用ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi類);來提取對象。
Adaptive加載流程
在dubbo中一般會首先通過ExtensionLoader.getAdaptiveExtension獲取Adaptive擴(kuò)展。這個方法會首先在擴(kuò)展點接口的所有實現(xiàn)類中查找類上是否有含有@Adaptive注解,如果有這樣的類直接返回該類的實例,如果沒有則會查找擴(kuò)展點接口的方法是否有@Adaptive注解并動態(tài)編譯一個類實現(xiàn)該接口并擴(kuò)展這些含有@Adaptive注解的方法。
- 代碼執(zhí)行流程
-----------------------getAdaptiveExtension()
-->getAdaptiveExtension()//為cachedAdaptiveInstance賦值
-->createAdaptiveExtension()
-->getAdaptiveExtensionClass()
-->getExtensionClasses()//為cachedClasses 賦值
-->loadExtensionClasses()
-->loadFile
-->createAdaptiveExtensionClass()//自動生成和編譯一個動態(tài)的adpative類,這個類是一個代理類
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
-->compiler.compile(code, classLoader)
-->injectExtension()//作用:進(jìn)入IOC的反轉(zhuǎn)控制模式,實現(xiàn)了動態(tài)入注
- 每次只需要從緩存中取出AdaptiveInstance,如果沒有命中則創(chuàng)建并加入緩存
/**
* @Author pengyunlong
* @Description 每次只需要從緩存中取出AdaptiveInstance,如果沒有命中則創(chuàng)建并加入緩存
* @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字節(jié)碼并創(chuàng)建實例,然后給該實例注入屬性(詳細(xì)過程參見http://www.itdecent.cn/p/764cec6ebb3d)。
/**
* @Author pengyunlong
* @Description 獲取AdaptiveExtensionClass字節(jié)碼并創(chuàng)建實例然后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);
}
}
- 加載實現(xiàn)該擴(kuò)展點接口的所有class并緩存,
getExtensionClasses()會將類上有@Adaptive注解的類緩存到cachedAdaptiveClass中,如果cachedAdaptiveClass沒有值則需要動態(tài)編譯一個AdaptiveExtension
/**
* @Author pengyunlong
* @Description 擴(kuò)展類上有@Adaptive注解的直接返回該類,如果沒有則動態(tài)創(chuàng)建
* @param
* @Date 2018/6/14 16:28
*/
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses加載含有@Adaptive的擴(kuò)展點實現(xiàn)類的過程
/**
* @Author pengyunlong
* @Description 從指定目錄讀取Spi配置文件,分析class并加入緩存
* 1.cachedAdaptiveClass 含有Adaptive注解的class
* 2.cachedWrapperClasses 含有指定參數(shù)的構(gòu)造方法的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);
//擴(kuò)展點實例類上有@Adaptive注解直接設(shè)置cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new
}
try {
clazz.getConstructor(type);
//如果擴(kuò)展點實例的是含有type類型參數(shù)的構(gòu)造方法則加入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);
}
//其余情況則將擴(kuò)展點類名加入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
}
}
}
}
}
關(guān)于loadfile的一些細(xì)節(jié)
cachedAdaptiveClass
如果這個class含有adative注解就賦值,例如ExtensionFactory,而例如Protocol在這個環(huán)節(jié)是沒有的。cachedWrapperClasses
只有當(dāng)該class無adative注解,并且構(gòu)造函數(shù)包含目標(biāo)接口(type)類型,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中cachedActivates
剩下的類,包含Activate注解cachedNames
剩下的類就存儲在這里。
動態(tài)編譯過程
- 沒有含有@Adaptive注解的擴(kuò)展點實現(xiàn)類則調(diào)用
createAdaptiveExtensionClassCode動態(tài)生成AdaptiveExtension的代碼然后編譯默認(rèn)采用Javassist編譯
/**
* @Author pengyunlong
* @Description 動態(tài)生成代碼然后編譯
* @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 <擴(kuò)展點接口所在包>;
public class <擴(kuò)展點接口名>$Adpative implements <擴(kuò)展點接口> {
public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
if(是否有URL類型方法參數(shù)?) 使用該URL參數(shù)
else if(是否有方法類型上有URL屬性) 使用該URL屬性
# <else 在加載擴(kuò)展點生成自適應(yīng)擴(kuò)展點類時拋異常,即加載擴(kuò)展點失??!>
if(獲取的URL == null) {
throw new IllegalArgumentException("url == null");
}
根據(jù)@Adaptive注解上聲明的Key的順序,從URL獲致Value,作為實際擴(kuò)展點名。
如URL沒有Value,則使用缺省擴(kuò)展點實現(xiàn)。如沒有擴(kuò)展點, throw new IllegalStateException("Fail to get extension");
在擴(kuò)展點實現(xiàn)調(diào)用該方法,并返回結(jié)果。
}
public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
throw new UnsupportedOperationException("is not adaptive method!");
}
}
-
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();因為Compiler.class的擴(kuò)展點實現(xiàn)類中AdaptiveCompiler又@Adaptive注解所以的返回值為AdaptiveCompiler,默認(rèn)使用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);
}
}
- 解析過程實際就是使用正則匹配轉(zhuǎn)化為Javassist的代碼;(Javassist動態(tài)編譯參見:http://www.itdecent.cn/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());
}
}
動態(tài)編譯結(jié)果
Protocol所有擴(kuò)展實現(xiàn)類上都沒有@Adaptive注解,且擴(kuò)展接口含有兩個 @Adaptive 注解的方法:exporter() refer(),所以dubbo會生成一個動態(tài)類Protocol$Adaptive,且它實現(xiàn)Protocol接口來擴(kuò)展這兩個Adaptive方法。擴(kuò)展點接口和最終動態(tài)生成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中解析出擴(kuò)展點名稱extName,extName的默認(rèn)值為@SPI的value。這是adaptive的精髓:每一個方法都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要的擴(kuò)展點。 -
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
根據(jù)extName重新獲取指定的Protocol.class擴(kuò)展點。如果所有擴(kuò)展點中含有Wrapper(listener,fiter)則ExtensionLoader.getExtension()會將真正的實現(xiàn)類通過Wrapper(listener,fiter)包裝后返回。 -
extension.export(arg0)
執(zhí)行目標(biāo)類的目標(biāo)方法
這樣就實現(xiàn)了每一個方法都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要真實的擴(kuò)展點。