概述 Dubbo 中的泛化接口实现主要用于服务端没有 API 接口及模型类元的情况,泛化实现需要实现 GenericService 接口。调用端在引用某个服务时可以指定通用调用(generic配置项),进而可以调用该服务对应的泛化实现。服务消费端无需关注泛化实现的细节,只需要引用服务的时候指定通用调用即可。
应用场景 通常用于框架集成,比如:实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。
使用示例 服务端 服务端需要实现 GenericService 接口以实现泛化功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Service public class MyGenericService implements GenericService { @Override public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("sayHello" .equals(method)) { return "print " + args[0 ]; } return "not find method!" ; } }
服务暴露1 <dubbo:service interface ="com.code.dubbo.DemoService" ref ="myGenericService" />
说明: 泛化实现暴露出去需要依托已有的服务接口,将接口的实现设置成泛化实现即可。
消费端
服务引入1 <dubbo:reference id ="demoService" interface ="com.code.dubbo.DemoService" generic ="true" />
说明:
消费端需要引入 interface 配置项的依赖,并且设置 generic 属性,以指定引用的服务为通用调用。
generic 配置项支持设置 true、nativejava 以及 bean,在 Dubbo 2.7 中还支持了 protobuf-json 和 raw.return ,用于对参数进行序列化和反序列化。
消费端调用服务和普通的消费者一摸一样。
远程调用1 2 DemoService demoService = (DemoService) applicationContext.getBean("demoService" ); String result = demoService.sayHello("gentryhuang" );
GenericImplFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 @Activate (group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000 )public class GenericImplFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class ) ; private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class}; @Override public Result invoke (Invoker<?> invoker, Invocation invocation) throws RpcException { String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY); if (ProtocolUtils.isGeneric(generic) && !Constants.$INVOKE.equals(invocation.getMethodName()) && invocation instanceof RpcInvocation) { RpcInvocation invocation2 = (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); } invocation2.setMethodName(Constants.$INVOKE); invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES); invocation2.setArguments(new Object[]{methodName, types, args}); Result result = invoker.invoke(invocation2); if (!result.hasException()) { Object value = result.getValue(); try { Method method = invoker.getInterface().getMethod(methodName, parameterTypes); if (ProtocolUtils.isBeanGenericSerialization(generic)) { if (value == null ) { return new RpcResult(value); } else if (value instanceof JavaBeanDescriptor) { return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value)); } else { throw new RpcException( "The type of result value is " + value.getClass().getName() + " other than " + JavaBeanDescriptor.class .getName () + ", and the result is " + value); } } else { return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType())); } } catch (NoSuchMethodException e) { throw new RpcException(e.getMessage(), e); } } else if (result.getException() instanceof GenericException) { GenericException exception = (GenericException) result.getException(); try { String className = exception.getExceptionClass(); Class<?> clazz = ReflectUtils.forName(className); Throwable targetException = null ; Throwable lastException = null ; try { targetException = (Throwable) clazz.newInstance(); } catch (Throwable e) { lastException = e; for (Constructor<?> constructor : clazz.getConstructors()) { try { targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]); break ; } catch (Throwable e1) { lastException = e1; } } } if (targetException != null ) { try { Field field = Throwable.class.getDeclaredField("detailMessage"); if (!field.isAccessible()) { field.setAccessible(true ); } field.set(targetException, exception.getExceptionMessage()); } catch (Throwable e) { logger.warn(e.getMessage(), e); } result = new RpcResult(targetException); } else if (lastException != null ) { throw lastException; } } catch (Throwable e) { throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e); } } return result; } return invoker.invoke(invocation); }
GenericImplFilter 是服务消费端的过滤器,主要是对 泛化引用 和 泛化实现 在调用端的处理。关于泛化引用部分在泛化调用中已经详细说明,下面对泛化实现的逻辑进行说明:
获取 generic 配置项,判断是否开启泛化引用。
从调用信息对象 RpcInvocation 中获取调用的方法名、参数类型列表、参数值列表。
根据 generic 的值选择对应的序列化方式对参数进行序列化。
重置调用信息对象中的方法名、方法参数类型、方法参数,本质上将普通方法调用转为 $invoke 方法调用 ,重置完成后进行方法调用。
对正常结果进行反序列化,以及根据 GenericException 异常,创建原始异常 targetException 并返回给调用端。
这里需要说明的是上述的第 5 步,为什么执行 invoker.invoke(invocation2) 方法就会调用泛化实现 $invoke 方法? 原因如下:
服务暴露的过程确定了服务实现对象和服务接口1 2 3 4 5 6 7 8 9 10 +--- ServiceConfig#doExport if (ref instanceof GenericService) { interfaceClass = GenericService.class ; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } }
注意: 虽然接口设置为了 GenericService ,但是不影响服务暴露,服务暴露的 URL 中使用接口名表示服务接口信息,也就是 interface 的值。
将服务实例封装成 Invoker ,服务实例就是 GenericService 实例。1 2 3 4 5 6 7 8 9 10 11 12 13 @Adaptive ({Constants.PROXY_KEY})<T> Invoker<T> getInvoker (T proxy, Class<T> type, URL url) throws RpcException ;
GenericFilter GenericFilter 不会特别处理泛化实现的调用,将这种情况当作普通调用对待。
泛化实现流程图
小结 Dubbo 提供了 GenericService 接口用于实现泛化服务,由于泛化实现对于调用端来说是没有具体接口的,因此泛化实现必须依托现有的服务接口。