dubbo AdaptiveExtension
AdaptiveExtension
自适应Extension,作者其实在使用Extension方和Extension之间插入AdaptiveExtension用来自适应,也可以说是适配。
所以,我们发现,这里其实有三种,
1,一种是自动生成的Adaptive根据url参数来选择具体实现;
2,一种是自己实现一个Adaptive,写自己的逻辑选择具体实现,比如AdaptiveExtensionFactory,Compiler。这种都是在ExtensionLoader中需要使用,一般是不会这么做的。
3,还有一种没有Adaptive,实现一个adapter,比如TelnetHandler。这种因为不能根据url参数来决定使用哪一个实现。
从结构上看AdaptiveExtension是很重要的角色,我们配置了一些插件,但是具体使用哪一个如果用硬编码自然是不可能的,而这种自适应的方式得益于dubbo的状态数据信息都可以通过URL来获取,被叫做以URL为总线的模式。而自适应即是根据这些信息来决定调用哪一个插件的实现。
ExtensionLoader是插件机制的核心类:
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); // 获得AdaptiveExtension入口 public T getAdaptiveExtension() { // cachedAdaptiveInstance缓存 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; } 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); } } private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 依然没有cachedAdaptiveClass 说明没有配置@Adaptive 那就自动生成一个 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<?> createAdaptiveExtensionClass() { // 组装java代码 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 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; } private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { String value = defaultAnnotation.value(); if(value != null && (value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if(names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if(names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
以下方法都会触发loadFile,这个方法就会读取文件,解析出各个插件的class。有@Adaptive注解的会放入赋值给cachedAdaptiveClass。
下面以Transporter接口为例进行说明自动生成的代码:
@SPI("netty") public interface Transporter { @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) Server bind(URL url, ChannelHandler handler) throws RemotingException; @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) Client connect(URL url, ChannelHandler handler) throws RemotingException; }
动态生成类:
public class Transporter$Adaptive implements Transporter { public Server bind(URL arg0, ChannelHandler arg1) throws RemotingException { URL url = arg0; String extName = url.getParameter("server", url.getParameter("transporter", "netty")); Transporter extension = (Transporter) ExtensionLoader .getExtensionLoader(Transporter.class).getExtension(extName); return extension.bind(arg0, arg1); } }
Adaptive机制是一个很好的设计,很好的解决多方案实现的适配问题,如果你遇到类似的代码架构的时候,多想一下更加有扩展性的设计。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述