Dubbo-Adaptive实现解析
Dubbo-Adaptive实现解析介
daptive的主要功能是对所有的扩展点进行封装为一个类,通过URL传入参数的时动态选择需要使用的
扩展点。其底层的实现原理就是动态代理
ExtensionLoader-getAdaptiveExtension
public T getAdaptiveExtension() {//从缓存中获取 进行Holder和加锁的方式来保证只会被创建一次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 {//创建AdaptiveExtension 这里真实的进行创建操作instance = createAdaptiveExtension();//放入缓存cachedAdaptiveInstance.set(instance);} catch (Throwable t) {createAdaptiveInstanceError = t;throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);}}}}return (T) instance;}
createAdaptiveExtension
/*** 创建xxxAdaptive注解类的扩展* @return*/@SuppressWarnings("unchecked")private T createAdaptiveExtension() {try {//自动依赖注入injectExtension// 这里使用`getAdaptiveExtensionClass`方法进行构建类并且执行实例化// 然后和普通的其他class相同,依旧使用injectExtension进行扩展return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);}}
injectExtension
private T injectExtension(T instance) {//检测objectFactory字段if (objectFactory == null) {return instance;}try {//遍历其中的所有方法for (Method method : instance.getClass().getMethods()) {//方法遍历判断是否为setter方法 如果不是,忽略该方法继续下一个方法if (!isSetter(method)) {// 是否是set方法// 1. 以"set"开头// 2. 参数长度为1// 3. 是公开的方法continue;}/*** Check {@link DisableInject} to see if we need auto injection for this property*/if (method.getAnnotation(DisableInject.class) != null) {//判断是否禁止注入 如果方法上明确标注了@DisableInject注解,忽略该方法continue;}//根据setter方法的参数,确定扩展接口Class<?> pt = method.getParameterTypes()[0];//判断是否为基本类型 原始类型(boolean、char、byte、short、int、long、float、double)//如果参数为简单类型,忽略该setter方法(略)if (ReflectUtils.isPrimitives(pt)) {continue;}try {//setter方法注入 获取需要set的扩展点名称String property = getSetterProperty(method);//从扩展中获取对象 加载并实例化扩展实现类Object object = objectFactory.getExtension(pt, property);//从ExtensionLoader中加载指定的扩展点 比如有一个方法为setRandom(LoadBalance loadBalance),那么则以为着需要加载负载均衡中名为random的扩展点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;}
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {//确保已经加载了所有的扩展类信息getExtensionClasses();if (cachedAdaptiveClass != null) {//如果已经加载过了,则直接返回return cachedAdaptiveClass;}//否则进行构建操作return cachedAdaptiveClass = createAdaptiveExtensionClass();}
获取所有的扩展类信息
private Map<String, Class<?>> getExtensionClasses() {//从缓存中获取 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存Map<String, Class<?>> classes = cachedClasses.get();if (classes == null) {synchronized (cachedClasses) {classes = cachedClasses.get();if (classes == null) {//加载配置文件classes = loadExtensionClasses();cachedClasses.set(classes);}}}return classes;}
createAdaptiveExtensionClass
/*** 生成一个 xxxx$Adaptive 适配器类* @return*/private Class<?> createAdaptiveExtensionClass() {//实例化一个新的Adaptive的代码生成器,并且进行代码生成String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();//获取当前类的类加载器ClassLoader classLoader = findClassLoader();//通过扩展点,寻找编译器, 目前有Java自带的编译器和Javassist的编译器org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//编译并且生成classreturn compiler.compile(code, classLoader);}
具体通过 AdaptiveClassLoaderCodeGenerator.generate 方法来进行实现真正的代码生成
public String generate() {// no need to generate adaptive class since there's no adaptive method found.//如果没有任何方法标记为Adaptive,则不做处理if (!hasAdaptiveMethod()) {throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");}//行编写代码StringBuilder code = new StringBuilder();//生成包的信息code.append(generatePackageInfo());//生成 生成引用信息 importcode.append(generateImports());//生成类声明code.append(generateClassDeclaration());//生成每一个方法Method[] methods = type.getMethods();for (Method method : methods) {code.append(generateMethod(method));}//输出最后的一个"}"来结束当前类code.append("}");if (logger.isDebugEnabled()) {logger.debug(code.toString());}return code.toString();}
generateMethod
private String generateMethod(Method method) {//方法返回类型String methodReturnType = method.getReturnType().getCanonicalName();//方法名称String methodName = method.getName();//生成方法内容String methodContent = generateMethodContent(method);//生成方法参数String methodArgs = generateMethodArguments(method);//生成方法异常String methodThrows = generateMethodThrows(method);//格式化为一个字符串// public %s %s(%s) %s {// %s// }return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);}
generateMethodContent
private String generateMethodContent(Method method) {//获取Adaptive注解,只支持含有Adaptive注解方法处理Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {//没有该注解,直接抛出异常return generateUnsupported(method);} else {//获取URL参数的所在位置int urlTypeIndex = getUrlTypeIndex(method);// found parameter in URL type//增加判断url不为空的代码if (urlTypeIndex != -1) {// Null Point checkcode.append(generateUrlNullCheck(urlTypeIndex));} else {// did not find parameter in URL type//获取这个方法中的所有参数列表// 寻找每个参数中是否有"get"开头的方法,并且返回值是URL的// 如果有则同样认定为找到,否则抛出异常code.append(generateUrlAssignmentIndirectly(method));}//获取扩展点的适配名称String[] value = getMethodAdaptiveValue(adaptiveAnnotation);// 判断是否有参数是Invocation类// 这里判断的主要目的在于,拥有Invocation时,则获取扩展名称的方式发生改变// 存在Invocation时,通过getMethodParameter,否则通过getParameter来执行// getMethodParameter是dubboURL中特有的,用于将"test.a"转换为"testA"的形式boolean hasInvocation = hasInvocationArgument(method);//增加有Invocation类时的不为空判断code.append(generateInvocationArgumentNullCheck(method));//生成获取扩展点名称的方法code.append(generateExtNameAssignment(value, hasInvocation));// check extName == null?//检查扩展点不能为空code.append(generateExtNameNullCheck(value));//获取扩展点实现code.append(generateExtensionAssignment());//返回扩展点中的真实调用// return statementcode.append(generateReturnAndInvocation(method));}return code.toString();}
Dubbo-Adaptive实现解析相关推荐
- 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解
本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...
- Dubbo原码解析(version:2.5.3)
一.启动 dubbo借助spring的schemas来启动(dubbo.jar/META-INF/spring.schemas). 在dubbo.jar/META-INF/spring.handler ...
- dubbo源码解析(九)远程通信——Transport层
远程通讯--Transport层 目标:介绍Transport层的相关设计和逻辑.介绍dubbo-remoting-api中的transport包内的源码解析. 前言 先预警一下,该文篇幅会很长,做好 ...
- dubbo源码解析(十)远程通信——Exchange层
远程通讯--Exchange层 目标:介绍Exchange层的相关设计和逻辑.介绍dubbo-remoting-api中的exchange包内的源码解析. 前言 上一篇文章我讲的是dubbo框架设计中 ...
- Dubbo源码解析-Dubbo服务消费者_Dubbo协议(一)
前言: 在介绍完Dubbo 本地模式(Injvm协议)下的服务提供与消费后,上文我们又介绍了Dubbo远程模式(dubbo协议)下的服务暴露过程,本质上就是通过Netty将dubbo协议端口暴露出去, ...
- Dubbo源码解析 --- DIRECTORY和ROUTER
Dubbo源码解析 --- DIRECTORY和ROUTER 今天看一下Directory和Router. 我们直接从代码看起(一贯风格),先看后总结,对着总结再来看,相信会收获很多.我们先看com. ...
- dubbo源码解析-集群容错架构设计
前言 本来是想把整个dubbo源码解析一次性弄完,再做成一个系列来发布的,但是正巧最近有位好朋友要去杭州面试,就和我交流了一下.本着对dubbo源码略有心得的心态,在交流过程中也发表了个人的一些粗劣的 ...
- dubbo源码解析-zookeeper创建节点
前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...
- dubbo源码解析(二)
大家好,我是烤鸭: dubbo 源码解析: 1.服务导出 介绍: Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三 ...
- dubbo(5) Dubbo源码解析之服务调用过程
来源:https://juejin.im/post/5ca4a1286fb9a05e731fc042 Dubbo源码解析之服务调用过程 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与 ...
最新文章
- Spring Cloud Alibaba 极速运维:微服务与 DevOps
- 如何 判断 设备 是否 连接 上 了 wifi
- 卷积神经的这些坑你都踩过吗?
- 我应该如何道德地接近用户密码存储以便以后的明文检索?
- DoD模型与OSI模型的关系及其协议对应关系
- Tour West Australia by Motorcycle
- (34)SystemVerilog语言编写计数器
- Spring mvc 参数类型转换
- 经典算法题每日演练——第二十四题 梳排序
- 虚拟机、集群、数据中心虚拟化
- tf.train.Saver,和模型参数微调
- java 符号引用与直接引用
- java+junit百科_JUnit介绍
- VMware ESXi 6.7注入第三方RAID驱动
- 什么是量子加密(二)
- tk免费顶级域名注册及使用
- 【JZOJ4939】平均值 题解
- selenium模拟登录某宝
- “千山之首 大果榛品”2022年辽阳大果榛子地理标志标识推介会开幕
- 计算机底层:进程与线程。