This article focuses on two important features of Dubbo: generalized calls and generalized implementations.

1. Generalization quote:



Usually, the service caller does not introduce the API package, and therefore does not contain the entity class in the interface. Therefore, the service caller can only provide the data in the form of Map, which is converted into the corresponding entity by the service provider according to the Map.

2. Generalization implementation



The generalized implementation means that the service provider does not introduce the API package, and therefore does not contain the entity class for the interface to transmit data. Therefore, the client needs to convert mode into Map before initiating the call.

From the above analysis, the so-called generalization is essentially the transformation of Map and Bean.

3. GenericImplFilter used by the source analysis client to generalize the call

public Result invoke(Invoker<? > invoker, Invocation invocation) throws RpcException { String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY); if (ProtocolUtils.isGeneric(generic) // @1 && ! 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; } if (invocation.getMethodName().equals(Constants.$INVOKE) // @2 && 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(byte[].class.getName(), arg.getClass().getName()); } } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { for (Object arg : args) { if (! (arg instanceof JavaBeanDescriptor)) { error(JavaBeanDescriptor.class.getName(), arg.getClass().getName()); } } } ((RpcInvocation) invocation).setAttachment( Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY)); } return invoker.invoke(invocation); }Copy the code

Code @ 1: This branch is a generalization implementation, and if it is a generalization implementation, the $invoke method is then called based on the generic values. Since the server implementation is a generalization implementation, all service providers implement the GenericeServer#$invoker method, which is implemented by converting the Bean to a Map. These details are explained in detail in the server GenericFilter sequence.

Code @ 2: GenericService#$invoke: GenericService#$invoke: GenericService#$invoke: GenericService#$invoke: GenericService#$invoke Why is the generic parameter retrieved in invoker.geturl ().getParameter(constants.generic_key) configured in < dubbo:service/> or in < dubbo:reference/>? It’s not hard to understand:

  1. If dubbo:servcie is not configured and dubbo:reference is configured, it represents the consumer end and must be a generalization reference.
  2. If dubbo:servcie is configured and dubbo:reference is not configured, it represents the server side and must be a generalization implementation.
  3. If both are configured, generic predominates on the consumer side. Combination of consumer and server Parameters When a service is discovered, the registry notifies the consumer of the URL of the service provider. Then the consumer merges the current configuration with that in the URL of the service provider. If the same parameter is encountered, the consumer overwrites the server.

Note: I won’t go into the implementation details here, because this section will cover the details of Map and Bean conversion, including whether or not it is serialized, in the source code analysis GenericFilter below, mainly because I looked at GenericFilter first. 4. GenericFilter (service provider)

@Activate(group = Constants.PROVIDER, order = -20000) public class GenericFilter implements Filter { @Override public Result invoke(Invoker<? > invoker, Invocation inv) throws RpcException { if (inv.getMethodName().equals(Constants.$INVOKE) && inv.getArguments() ! = null && inv.getArguments().length == 3 && ! ProtocolUtils.isGeneric(invoker.getUrl().getParameter(Constants.GENERIC_KEY))) { // @1 String name = ((String) inv.getArguments()[0]).trim(); String[] types = (String[]) inv.getArguments()[1]; Object[] args = (Object[]) inv.getArguments()[2]; try { Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); // @2 Class<? >[] params = method.getParameterTypes(); if (args == null) { args = new Object[params.length]; } String generic = inv.getAttachment(Constants.GENERIC_KEY); if (StringUtils.isEmpty(generic) || ProtocolUtils.isDefaultGenericSerialization(generic)) { // @3 args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { // @4 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(Constants.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 [" + Constants.GENERIC_SERIALIZATION_NATIVE_JAVA + "] only support message type " + byte[].class + " and your message type is " + args[i].getClass()); } } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { // @5 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 [" + Constants.GENERIC_SERIALIZATION_BEAN + "] only support message type " + JavaBeanDescriptor.class.getName() + " and your message type is " + args[i].getClass().getName()); } } } Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments())); // @6 if (result.hasException() && ! (result.getException() instanceof GenericException)) { return new RpcResult(new GenericException(result.getException())); } if (ProtocolUtils.isJavaGenericSerialization(generic)) { // @7 try { UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA) .serialize(null, os).writeObject(result.getValue()); return new RpcResult(os.toByteArray()); } catch (IOException e) { throw new RpcException("Serialize result failed.", e); } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD)); } else { return new RpcResult(PojoUtils.generalize(result.getValue())); } } catch (NoSuchMethodException e) { throw new RpcException(e.getMessage(), e); } catch (ClassNotFoundException e) { throw new RpcException(e.getMessage(), e); } } return invoker.invoke(inv); }}Copy the code

Code @1: If the method is named $invoker and has only 3 parameters, and the server implementation is non-return implementation, then the client generalization refers to the server, the client generalization calls, and the request parameters need to be deserialized into the real POJO object of the interface. Code @2: Based on the interface name (API class), method name, method parameter type list, according to the reflection mechanism to get the corresponding method. Code @3: Handles ordinary generic reference calls, i.e. handles
, just deserialize the list of parameters Object[] to pojo. The specific deserialization to PojoUtils#realize is as follows: In the JAVA world, POJOs are usually represented by maps. That is, a map can be used to represent the value of an object. What about serializing an object from a map? The key element is to keep the classpath name of the object in the Map. For example, we now have an object like this:

public class Student { private int id; private String name; private Team team; Public class Team {private int id; private String name; Student: {"class" :"somepackeage.Student", "id":1, "name":"dingw", "team": {"class" : {"team": {"team": {"team": {"team": {"team": {"team": {"team": { "somepackage.Team", "id":2, "name":"t" } }Copy the code

That is, the class identifies the POJO type that the Map needs to deserialize. Code @4: Handle < dubbo: Reference generic= “NativeJava” /> Enable the generalization reference, and use NativeJava to serialize the parameters, on the server side through NativeJava to deserialize the parameters into POJO objects. Code @5: Handle < dubbo: Reference generic= “bean” /> Enable the generic reference, and use Javabean to serialize the parameters, on the server side through the Javabean deserialize the parameters into POJO objects. Code @6: Serialize the type declared in the API method, build the new RpcInvocation(Method, Args, inv.getattachments ()) invocation environment, and continue to call subsequent filters. Code @7: Processes the execution result, serializing the return result if it is NativeJava or bean, or serializing pojoutils.Generalize if it is generic=true, also serializing the POJO to a Map. So much for generalization calls and generalization references.


Welcome to add the author micro signal (DINGwPMZ), add group discussion, the author quality column catalog: 1, source analysis RocketMQ column (40 +) 2, source analysis Sentinel column (12 +) 3, source analysis Dubbo column (28 +) 4, source analysis Mybatis column 5, source analysis Netty column (18 +) 6, source analysis JUC column Source code analysis (MyCat) for Elasticjob