Dubbo源码分析DubboSPI获取自适应扩展点实例
Dubbo SPI 系列文章
- 浅谈 Java SPI
- Dubbo SPI 使用方法(一)- 扩展点自动包装
- Dubbo SPI 使用方法(二)- 扩展点自适应
- Dubbo SPI 使用方法(三)- 扩展点自动装配
- Dubbo 源码分析 - Dubbo SPI 在 Protocol 层 的应用
- Dubbo 源码分析 - Dubbo SPI 通过 getExtension 获取扩展点实现类对象
前言
上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 分析了 Dubbo SPI 通过 getExtension(String name)
如何获取扩展点实现类对象, 本文接着通过剖析 Dubbo 源码的方式看一下 getAdaptiveExtension()
方法是如何获取自适应扩展点实例的.
正文
1. ExtensionLoader 部分属性
// 1. 缓存的自适应扩展点实例 private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
// 2. 自适应扩展点实现类
private volatile Class<?> cachedAdaptiveClass = null;
2. ExtensionLoader#getAdaptiveExtension()
- 从自适应扩展点实例 缓存集合中获取, 赋值给
instance
变量 - 如果缓存中没有,就通过
createAdaptiveExtension
方法主动去创建, 并放入缓存 - 返回结果
return instance
public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 第一步
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
2. ExtensionLoader#createAdaptiveExtension()
- 先调用
getAdaptiveExtensionClass
方法, 获取自适应扩展实现类, 并且实例化,最终获得一个实例instance
- 再通过
injectExtension
方法, 判断instance
是否依赖其他扩展实现类对象, 如果有的话,通过setter
方法注入到instance
实例中
private T createAdaptiveExtension() {
try {
// 第二步: getAdaptiveExtensionClass
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can"t create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
2.1 getAdaptiveExtensionClass
getExtensionClasses
方法在上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 有讲到,- 先判断缓存的扩展实现类
cachedClasses
是否为空 - 如果为空,就去META-INF 目录下,解析对应子目录下的 SPI 配置文件, 最后放入缓存
cachedClasses
- 先判断缓存的扩展实现类
- 接着判断缓存的自适应扩展实现类是否为空
- 如果为空, 就进入第三步: 动态编译
private Class<?> getAdaptiveExtensionClass() {
// 这一步会 load 所有的扩展实现类
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 第三步: 动态编译
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
2.1.2 createAdaptiveExtensionClass
// 第三步: 动态编译 private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
// 动态生成 Adaptive 代码
package com.nimo.spi.adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class MyProtocol$Adaptive implements com.nimo.spi.adaptive.MyProtocol {
public void export(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("myKey");
if(extName == null)
throw new IllegalStateException("Failed to get extension (com.nimo.spi.adaptive.MyProtocol) name from url (" + url.toString() + ") use keys([myKey])");
com.nimo.spi.adaptive.MyProtocol extension = (com.nimo.spi.adaptive.MyProtocol)ExtensionLoader.getExtensionLoader(com.nimo.spi.adaptive.MyProtocol.class).getExtension(extName);
extension.export(arg0);
}
}
2.2 injectExtension
该方法就是对获取到的自适应扩展点实例, 注入其他实例
// 第四步: 注入其他实例 private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
总结
Dubbo SPI 中定义了很多缓存, 比如上一篇提到的
// 3. (缓存的)扩展实现类集合 private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
// 4. 缓存的扩展对象集合 key 为 dubbo, value 为 DubboProtocol
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 5. 扩展增强 Wrapper 实现类集合
private Set<Class<?>> cachedWrapperClasses;
还有本文中涉及的
// 1. 缓存的自适应扩展点实例 private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
// 2. 自适应扩展点实现类
private volatile Class<?> cachedAdaptiveClass = null;
因此在获取一个扩展点实例时,
- 都会先去缓存中获取,
- 如果获取不到就去重新加载并实例化,
- 最后把实例放入缓存,
这是一个很普遍的缓存思路, 上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 getExtension
方法也是如此, 只不过本文多了一个 动态编译过程, 也刚好迎合了前面讲到的 Dubbo SPI 使用方法(二)- 扩展点自适应 的内容.
以上是 Dubbo源码分析DubboSPI获取自适应扩展点实例 的全部内容, 来源链接: utcz.com/z/514873.html