目录
- 原理概述
- 为什么设计@Adaptive注解
- dubbo源码中哪些地方什么时候使用到了,具体如何体现
- @Adaptive扩展加载过程源码分析
- 总结
原理概述
首先看以下官网的关于自适应扩展的描述:
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。
好像还是有点抽象,首先回顾下dubbo的SPI是什么:spi是一种扩展机制,目的就是为了获取一个对象。通过ExtensionLoader.getExtension(String name)获取,通过ExtensionLoader.getAdaptiveExtension()获取扩展类,这个扩展类的自适应具体时如何适应的呢,继续往下看
看下@Adaptive注解的源码定义,可以看出这个注解可以用在方法上和类上
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
为什么设计@Adaptive注解
设计Adaptive注解是为了区分人工编码的和动态生成的类
注解在类上时,表示扩展的逻辑有人工编码完成,不需要生成动态代理类(对应的java文件已经存在项目源码中);在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory;AdaptiveCompiler 只有两个实现类JavassistCompiler和JdkCompiler;AdaptiveExtensionFactory只有两个实现类,SpiExtensionFactory和SpringExtensionFactory.
注解在接口方法上,表示表示扩展的逻辑有框架自动生成(对应的java文件在启动后按需要动态拼接字符串生成,编译加载);例如Protocol接口,实现类有DubboProtocol,InjvmProtocol, HttpProtcol 等等
后面会以AdaptiveCompiler和Protocol为例来分析扩展的加载过程
dubbo源码中哪些地方什么时候使用到了,具体如何体现
下面从最基本的xml配置文件开始分析:
从spring.handlers得知解析DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
}
从 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));得知解析service标
签对应的时ServiceBean对象,ServiceBean继承了ServiceConfig
public class ServiceConfig extends AbstractServiceConfig {
private static final long serialVersionUID = 3033787999037024738L;
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private static final Map RANDOM_PORT_MAP = new HashMap();
private final List urls = new ArrayList();
private final List> exporters = new ArrayList>();
ServiceConfig中有两个静态变量初始化
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
这里就使用到了,接下来分析Protocol的自适应扩展加载过程
@Adaptive扩展加载过程源码分析
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
把断点打在这一行上,首先进入getAdaptiveExtension()方法
代码块1.1
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()
代码块2.1
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
injectExtension()时dubbo的IOC实现,上一篇文章已经说过;
接下来看 getAdaptiveExtensionClass()方法:
代码块3.1
private Class> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses();这个方法很重要,主要时读取MET-INF/dubbo/internal目录下的配置获取扩展的别名和全类名,并同过判断是否类上有@Adaptive注解,并加入缓存变量cachedAdaptiveClass 中,部分代码如下,注意:这个缓存中放的就是前面说的在类上加@Adaptive注解的类
代码块4.1
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
返回getAdaptiveExtensionClass()方法,此cachedAdaptiveClass为空,所以会执行createAdaptiveExtensionClass()方法,如下:
代码块4.2
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);
}
一行一行分析:
String code = createAdaptiveExtensionClassCode();
这行代码就是通过拼接字符串生产一个Protocol$Adaptice对象,这里就是前面说注解在方法上的情况,自适应扩展就体现在这个对象中,代码如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative 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.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);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.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);
}
}
真正的自适应就体现在这里了,按需加载某一个实现,extName ,如果url中没有传值就使用默认值dubbo,如果传递了就是传的值。
回到代码块4.2这一行,
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
发现这里又调用getAdaptiveExtension()方法了,循环调用?这里就是上面说的@Adaptive注解加载类上的情况,这个时候就不需要去生成动态类了
private Class> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
当再次调用到这个方法时cachedAdaptiveClass不为空,所以直接返回退出循环了。
注意:每次加载不同的扩展都是使用新的ExtensionLoader实例
总结
本文讲了什么时自适应扩展机制,以及@Adaptive设计的目的,以及注解在方法和类上的区别,并分析了源码如何实现。
到这里自适应@Adaptive原理也讲完了,如果要看源码dubbo SPI和Adaptive原理必须要了解,源码中随处可见ExtensionLoader的使用。