本文基于 Dubbo 2.6.1 版本,望知悉。
1. 概述
本文分享泛化实现。我们来看下 《用户指南 —— 泛化实现》 的定义:
泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。
请注意,消费提供者没有 API 接口 及 模型类元。那就是说,Dubbo 在泛化实现中,需要做两件事情:
泛化实现适用于服务提供者,和泛化引用适用于服务消费者,恰恰“相反”。
- 没有 API 接口,所以提供一个泛化服务接口,目前是
com.alibaba.dubbo.rpc.service.GenericService
。
- 一个泛化实现,只实现一个服务。
- 通过实现
$invoke(method, parameterTypes, args)
方法,处理所有该服务的请求。
- 具体的使用方式,我们在 「2. 示例」 中看。
- 没有 模型类元,所以方法参数和方法返回若是 POJO ( 例如 User 和 Order 等 ) ,需要转换处理:
- 服务消费者,将 POJO 转成 Map ,然后再调用服务提供者。(透明)
- 服务提供者,返回 Map 。
- 服务消费者,若收到返回值是 Map ,则转换成 POJO 再返回。
- 🙂 此处的 Map 只是举例子,实际在下文中,我们会看到还有一种转换方式。 (透明)
整体流程如下:
流程
2. 示例
服务提供者
在 dubbo-generic-service-demo-provider
,我们提供了例子。我们挑重点的地方说。
① 在 Java 代码中实现 GenericService 接口:
public class DemoServiceImpl implements GenericService {
@Override public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("sayHello".equals(method)) { return "Welcome " + args[0]; } return "unknown method"; }
}
|
② 在 Spring 配置申明服务的实现:
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" generic="true" />
|
interface
配置项,泛化实现的服务接口。通过该配置,服务消费者,可以从注册中心,获取到所有该服务的提供方的地址,包括泛化实现的该服务的地址。
generic
配置项,默认为 false
,不使用配置项。目前有两种配置项的值,开启泛化实现的功能:
服务消费者
在 dubbo-generic-service-demo-consumer
,我们提供了例子。我们挑重点的地方说。
调用代码如下:
DemoService demoService = (DemoService) context.getBean("demoService"); Object result = demoService.say01("NIHAO"); System.out.println("result: " + result);
|
- 和我们普通的服务消费者,调用服务提供者,一模一样,注意,是一模一样。
3. 服务消费者 GenericImplFilter
com.alibaba.dubbo.rpc.filter.GenericImplFilter
,实现 Filter 接口,服务消费者的泛化调用过滤器。代码如下:
1: @Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000) 2: public class GenericImplFilter implements Filter { 3: 4: private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class); 5: 6: private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class}; 7: 8: @Override 9: public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 10: 11: String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY); 12: 13: 14: if (ProtocolUtils.isGeneric(generic) 15: && !Constants.$INVOKE.equals(invocation.getMethodName()) 16: && invocation instanceof RpcInvocation) { 17: RpcInvocation invocation2 = (RpcInvocation) invocation; 18: String methodName = invocation2.getMethodName(); 19: Class<?>[] parameterTypes = invocation2.getParameterTypes(); 20: Object[] arguments = invocation2.getArguments(); 21: 22: 23: String[] types = new String[parameterTypes.length]; 24: for (int i = 0; i < parameterTypes.length; i++) { 25: types[i] = ReflectUtils.getName(parameterTypes[i]); 26: } 27: 28: Object[] args; 29: 30: if (ProtocolUtils.isBeanGenericSerialization(generic)) { 31: args = new Object[arguments.length]; 32: for (int i = 0; i < arguments.length; i++) { 33: args[i] = JavaBeanSerializeUtil.serialize(arguments[i], JavaBeanAccessor.METHOD); 34: } 35: 36: } else { 37: args = PojoUtils.generalize(arguments); 38: } 39: 40: 41: invocation2.setMethodName(Constants.$INVOKE); 42: 43: invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES); 44: 45: invocation2.setArguments(new Object[]{methodName, types, args}); 46: 47: 48: Result result = invoker.invoke(invocation2); 49: 50: 51: if (!result.hasException()) { 52: Object value = result.getValue(); 53: try { 54: 55: if (ProtocolUtils.isBeanGenericSerialization(generic)) { 56: if (value == null) { 57: return new RpcResult(null); 58: } else if (value instanceof JavaBeanDescriptor) { 59: return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value)); 60: } else { 61: throw new RpcException( 62: new StringBuilder(64) 63: .append("The type of result value is ") 64: .append(value.getClass().getName()) 65: .append(" other than ") 66: .append(JavaBeanDescriptor.class.getName()) 67: .append(", and the result is ") 68: .append(value).toString()); 69: } 70: } else { 71: 72: Method method = invoker.getInterface().getMethod(methodName, parameterTypes); 73: 74: return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType())); 75: } 76: } catch (NoSuchMethodException e) { 77: throw new RpcException(e.getMessage(), e); 78: } 79: 80: } else if (result.getException() instanceof GenericException) { 81: GenericException exception = (GenericException) result.getException(); 82: try { 83: String className = exception.getExceptionClass(); 84: Class<?> clazz = ReflectUtils.forName(className); 85: Throwable targetException = null; 86: Throwable lastException = null; 87: 88: try { 89: targetException = (Throwable) clazz.newInstance(); 90: } catch (Throwable e) { 91: lastException = e; 92: for (Constructor<?> constructor : clazz.getConstructors()) { 93: try { 94: targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]); 95: break; 96: } catch (Throwable e1) { 97: lastException = e1; 98: } 99: } 100: } 101: 102: if (targetException != null) { 103: try { 104: Field field = Throwable.class.getDeclaredField("detailMessage"); 105: if (!field.isAccessible()) { 106: field.setAccessible(true); 107: } 108: field.set(targetException, exception.getExceptionMessage()); 109: } catch (Throwable e) { 110: logger.warn(e.getMessage(), e); 111: } 112: 113: result = new RpcResult(targetException); 114: 115: } else if (lastException != null) { 116: throw lastException; 117: } 118: } catch (Throwable e) { 119: throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e); 120: } 121: } 122: 123: return result; 124: } 125: 126: 127: 128: 129: return invoker.invoke(invocation); 130: } 131: 132: 133: 134: }
|
- 整体,和服务提供者的 GenericFilter 有一些类似。
- 使用 Dubbo SPI Adaptive 机制,自动加载,仅限服务消费者,并且有
generic
配置项。
- 第 126 行:省略泛化引用的调用,在 《精尽 Dubbo 源码分析 —— 调用特性(二)之泛化引用》 中,详细解析。
- 第 129 行:若是普通调用( 非泛化引用的调用 ),调用
Invoker#invoke(invocation)
方法,继续过滤链的调用,最终调用 Service 服务。
- 👇👇👇 正戏 👇👇👇
- 第 14 至 16 行:判断是泛化实现的调用
- 第 22 至 26 行:获得参数类型数组。
- ========== 【第一步:序列化参数】 ==========
- 第 29 至 34 行:
generic = bean
,调用 JavaBeanSerializeUtil#serialize(JavaBeanDescriptor)
方法,序列化参数,即 方法参数 => JavaBeanDescriptor
。
- 第 35 至 38 行:
generic = true
,调用 PojoUtils#generalize(Object[] objs
方法,序列化参数,仅有 POJO => Map
。
- ========== 【第二步:RPC 调用】 ==========
- 第 41 行:设置 RpcInvocation 的方法名为
$invoke
。
- 第 42 行:设置 RpcInvocation 的方法参数类型为
GENERIC_PARAMETER_TYPES
。
- 第 43 行:设置 RpcInvocation 的参数数组为
methodName
types
types
。
- 第 48 行:通过如上的 RpcInvocation 的设置,我们调用
Invoker#invoke(invocation)
方法,就能 RPC 调用到泛化实现的服务。
- ========== 【第三步:反序列化正常结果】 ==========
- 第 55 至 69 行:
generic = bean
,调用 JavaBeanSerializeUtil#serialize(JavaBeanDescriptor)
方法,反序列化结果,即 JavaBeanDescriptor => POJO
。
- 第 71 至 74 行:
generic = true
,调用 PojoUtils#realize(pojo, type, genericType)
方法,反序列化结果,仅有 Map => POJO
。
- 注意,反序列完,是会创建新的 RpcResult 。
- ========== 【第三步:反序列化异常结果】 ==========
- 第 87 至 100 行:根据 GenericException 异常,创建原始异常
targetException
。
- 第 101 至 111 行:设置异常明细到
targetException
。
- 第 113 行:创建新的异常 RpcResult 对象。
- 第 114 至 117 行:创建原始异常失败,抛出异常
lastException
。
666. 彩蛋
知识星球
艿艿在本文,并未解析 POJO 的序列化和反序列化的相关代码,感兴趣的胖友,可以自己研究。