开篇

在Dubbo官方文档中关于泛化调用和泛化实现的说明,这里针对文档的案例做一些简单的说明和解释。

例子

// 引用远程服务
// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));// 弱类型接口名
reference.setInterface("org.apache.dubbo.demo.DemoService");
// 声明为泛化接口
reference.setGeneric("true");// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
// 基本类型以及Date,List,Map等不需要转换,直接调用
Object result = genericService.$invoke("sayHello",new String[] {"java.lang.String"}, new Object[] {"12345678"});System.out.println(result.toString());
  • 例子在注释中已经注明了泛化引用的一般步骤,这里不再赘述。
  • 核心的本质在于reference.get()的流程获取了consumer的对象,等同于xml通过reference初始化consumer的bean对象。
  • 本质上解析生成consumer的bean对象的过程就是泛化过程中reference.get()的过程。
  • 泛化调用唯一需要注意的是对参数的一些限制,基本上参数不需要转换,Class对象等参数需要把保存Class对象的完整类名。

序列化说明

public class Person {public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}代码例子-------------public class Demo {public static void main(String[] args) {Person person = new Person();person.setName("person");person.setAge(123);System.out.println("序列化后对象:");System.out.println(JSON.toJSONString(PojoUtils.generalize(person)));System.out.println("反序列化后对象:");System.out.println(JSON.toJSONString(PojoUtils.realize(PojoUtils.generalize(person), Person.class)));}
}运行结果-----------------序列化后对象:
{"name":"person","class":"org.apache.dubbo.demo.consumer.Person","age":123}反序列化后对象:
{"age":123,"name":"person"}
  • 一般的泛化调用都是普通的reference引用,在泛化调用过程中通过PojoUtils.generalize/realize进行序列化和反序列化。
  • 在序列化过程中Person对象通过PojoUtils.generalize()序列化的结果当中增加了class字段,然后对序列化的结果进行反序列才能正常反序列化。
  • 在dubbo官网有下列说明,指明在泛化调用参数为POJO对象的时候通过使用Map需要带上class类型。

泛化调用序列化补充

源码分析

  • Dubbo泛化调用实际是在filter过滤链上执行的序列化和反序列化操作。
  • genericimpl对应consumer侧的泛化调用操作。
  • generic对应的为provider侧的返回调用操作。
com.alibaba.dubbo.rpc.Filter文件内容generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter

泛化引用

@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000)
public class GenericImplFilter extends ListenableFilter {private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class};public GenericImplFilter() {super.listener = new GenericImplListener();}@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {String generic = invoker.getUrl().getParameter(GENERIC_KEY);//服务端是泛化暴露,客户端不是使用泛化调用场景if (ProtocolUtils.isGeneric(generic)&& (!$INVOKE.equals(invocation.getMethodName()) && !$INVOKE_ASYNC.equals(invocation.getMethodName()))&& invocation instanceof RpcInvocation) {RpcInvocation invocation2 = new RpcInvocation(invocation);String methodName = invocation2.getMethodName();Class<?>[] parameterTypes = invocation2.getParameterTypes();Object[] arguments = invocation2.getArguments();String[] types = new String[parameterTypes.length];for (int i = 0; i < parameterTypes.length; i++) {types[i] = ReflectUtils.getName(parameterTypes[i]);}Object[] args;// 客户端(非泛化)到服务端(泛化)根据不同协议进行序列化if (ProtocolUtils.isBeanGenericSerialization(generic)) {args = new Object[arguments.length];for (int i = 0; i < arguments.length; i++) {args[i] = JavaBeanSerializeUtil.serialize(arguments[i], JavaBeanAccessor.METHOD);}} else {args = PojoUtils.generalize(arguments);}if (RpcUtils.isReturnTypeFuture(invocation)) {invocation2.setMethodName($INVOKE_ASYNC);} else {invocation2.setMethodName($INVOKE);}invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);invocation2.setArguments(new Object[]{methodName, types, args});// 客户端调用转换为服务端的泛化调用return invoker.invoke(invocation2);// 服务端非泛化暴露,消费使用泛化调用  } else if ((invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC))&& invocation.getArguments() != null&& invocation.getArguments().length == 3&& ProtocolUtils.isGeneric(generic)) {Object[] args = (Object[]) invocation.getArguments()[2];// 校验不同的序列化格式是否正确if (ProtocolUtils.isJavaGenericSerialization(generic)) {for (Object arg : args) {if (!(byte[].class == arg.getClass())) {error(generic, byte[].class.getName(), arg.getClass().getName());}}} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {for (Object arg : args) {if (!(arg instanceof JavaBeanDescriptor)) {error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName());}}}invocation.setAttachment(GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY));}return invoker.invoke(invocation);}
}

Dubbo泛化引用过程 - consumer

泛化实现

@Activate(group = CommonConstants.PROVIDER, order = -20000)
public class GenericFilter extends ListenableFilter {public GenericFilter() {super.listener = new GenericListener();}@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {// 如果方法名为$invoker,并且只有3个参数,// 并且服务端暴露的invoker不是GenericService的相关类// 则认为本次服务调用时客户端泛化引用服务端,客户端的泛化调用,// 需要将请求参数反序列化为该接口真实的pojo对象。if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))&& inv.getArguments() != null&& inv.getArguments().length == 3&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {String name = ((String) inv.getArguments()[0]).trim();String[] types = (String[]) inv.getArguments()[1];Object[] args = (Object[]) inv.getArguments()[2];try {// 根据接口名(API类)、方法名、方法参数类型列表,根据反射机制获取对应的方法。Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);Class<?>[] params = method.getParameterTypes();if (args == null) {args = new Object[params.length];}String generic = inv.getAttachment(GENERIC_KEY);if (StringUtils.isBlank(generic)) {generic = RpcContext.getContext().getAttachment(GENERIC_KEY);}// 处理普通的泛化引用调用,即处理<dubbo:referecnce generic=“true” …/>,// 只需要将参数列表Object[]反序列化为pojo即可,// 具体的反序列化为PojoUtils#realize,// 其实现原理如下:在JAVA的世界中,pojo通常用map来表示,// 也就是一个Map可以用来表示一个对象的值,那从一个Map如果序列化一个对象呢?// 其关键的要素是要在Map中保留该对象的类路径名,// 也就是通过class来标识该Map需要反序列化的pojo类型。if (StringUtils.isEmpty(generic)|| ProtocolUtils.isDefaultGenericSerialization(generic)|| ProtocolUtils.isGenericReturnRawResult(generic)) {args = PojoUtils.realize(args, params, method.getGenericParameterTypes());// 处理< dubbo:reference generic=“nativejava” /> 启用泛化引用,// 并使用nativejava序列化参数,在服务端这边通过nativejava反序列化参数成pojo对象。} else if (ProtocolUtils.isJavaGenericSerialization(generic)) {for (int i = 0; i < args.length; i++) {if (byte[].class == args[i].getClass()) {try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) {args[i] = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA).deserialize(null, is).readObject();} catch (Exception e) {throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e);}} else {throw new RpcException("Generic serialization [" +GENERIC_SERIALIZATION_NATIVE_JAVA +"] only support message type " +byte[].class +" and your message type is " +args[i].getClass());}}// 处理< dubbo:reference generic=“bean” /> 启用泛化引用,// 并使用javabean序列化参数,在服务端这边通过javabean反序列化参数成pojo对象。} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {for (int i = 0; i < args.length; i++) {if (args[i] instanceof JavaBeanDescriptor) {args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]);} else {throw new RpcException("Generic serialization [" +GENERIC_SERIALIZATION_BEAN +"] only support message type " +JavaBeanDescriptor.class.getName() +" and your message type is " +args[i].getClass().getName());}}} else if (ProtocolUtils.isProtobufGenericSerialization(generic)) {// as proto3 only accept one protobuf parameterif (args.length == 1 && args[0] instanceof String) {try (UnsafeByteArrayInputStream is =new UnsafeByteArrayInputStream(((String) args[0]).getBytes())) {args[0] = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension("" + GENERIC_SERIALIZATION_PROTOBUF).deserialize(null, is).readObject(method.getParameterTypes()[0]);} catch (Exception e) {throw new RpcException("Deserialize argument failed.", e);}} else {throw new RpcException("Generic serialization [" +GENERIC_SERIALIZATION_PROTOBUF +"] only support one" + String.class.getName() +" argument and your message size is " +args.length + " and type is" +args[0].getClass().getName());}}// 序列化API方法中声明的类型,构建new RpcInvocation(method, args, inv.getAttachments())调用环境,继续调用后续过滤器。return invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));} catch (NoSuchMethodException e) {throw new RpcException(e.getMessage(), e);} catch (ClassNotFoundException e) {throw new RpcException(e.getMessage(), e);}}return invoker.invoke(inv);}
}

Dubbo泛化引用过程 - provider

Dubbo泛化引用和泛化实现相关推荐

  1. java 泛化_Dubbo 泛化引用和泛化实现

    开篇 在Dubbo官方文档中关于泛化调用和泛化实现的说明,这里针对文档的案例做一些简单的说明和解释. 例子 // 引用远程服务 // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 R ...

  2. Dubbo 泛化引用

    前言 大家好,今天开始给大家分享 - Dubbo 专题之 Dubbo 泛化引用.在前一个章节中我们介绍了 Dubbo 中的参数验证以及使用场景.我们在这个章节会继续介绍 Dubbo 泛化引用.那么什么 ...

  3. 理解 Dubbo 服务引用

    为什么80%的码农都做不了架构师?>>>    dubbo 服务引用过程 dubbo 的使用过程中消费者端会依赖服务端提供的 api 包(接口 jar 包) , 这些 api 包中只 ...

  4. Dubbo服务引用过程

    Dubbo服务引用 大致流程 Provider将服务暴露出来并且注册到注册中心,而Consumer通过注册中心获取Provider的信息,之后将自己封装成一个调用类去与Provider进行交互. 首先 ...

  5. 结构化泛化和面向对象泛化

    结构化泛化 结构化泛化(传统方法学.生命周期方法学),采用结构化技术(结构化分析.结构化设计和结构化实现)来完成软件开发的各项任务,并使用适当的软件工具或软件工程环境来支持结构化技术的运用. 特点: ...

  6. dubbo项目引用另一个项目的接口

    首先在主项目的pom.xml添加你所需要的jar包 如: <dependency><groupId>com.ssm.com</groupId> <versio ...

  7. Dubbo服务引用原理

    服务引用原理 配置文件 通过Spring容器加载 每一个标签,对应一个解析类 Reference 对应ReferenceBean 实现了FactoryBean FactoryBean 工厂Bean 引 ...

  8. Python机器学习:多项式回归与模型泛化008模型泛化与岭回归

    岭回归 数据 #数据 import numpy as np import matplotlib.pyplot as plt np.random.seed(42) x = np.random.unifo ...

  9. RPC框架Dubbo分析

    1,背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进 单一应用架构 当网站流量很小时,只需一 ...

最新文章

  1. PrestaShop 网站后台配置(六)
  2. 在线作图|2分钟绘制三维PCA图
  3. 用 Flask 来写个轻博客 (15) — M(V)C_实现博文页面评论表单
  4. 前端学习(3291):react hook 规范
  5. java+mysql校园学校宿舍管理系统源码
  6. 关于WinCE6.0补丁包的一点说明
  7. 18 File Duplication and Pipes
  8. iOS笔记之UIKit_UIButton
  9. 第二十二章 职业道德规范
  10. android动画哪些,Android Animation动画(很详细)
  11. Modelica资料整理
  12. 极速办公ppt里面如何插入表格
  13. 局域网下两台电脑ping不通的问题总结
  14. android win7共享文件夹,手把手教你win7系统怎么共享文件夹
  15. PageAdmin CMS网站制作教程:如何创建及管理栏目?
  16. 中断工作原理在现代计算机中的应用,中断、DMA、通道
  17. 载波聚合mac_Lte-a终端测试仪表在载波聚合下mac层数据调度方法
  18. uni-app锚点跳转及滚动Tab切换(非scroll-view)
  19. 微网站 源码 php,DIYWAP手机微网站内容管理系统 php版 v6.3
  20. MQ Reason code list

热门文章

  1. 用Python代码画一只喜羊羊
  2. IP地址192.168.1.1/24中的/24是什么意思
  3. 盘点 | 北京冬奥会上的那些 AI 黑科技,深延科技全都有
  4. 回忆快乐的童年 —— 写在六一儿童节时
  5. 酒吧想要吸引人,做好这三点就能事半功倍
  6. Java实现信息重复发送
  7. 一图带你看懂什么是智慧城市
  8. Bash漏洞分析溯源
  9. 解决 Closing root webapplication
  10. ”==”和和equals方法究竟有什么区别?