Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

Introduction to the

The generalized interface invocation method is mainly used when the client has no API interface and model class element. All POJOs in parameters and return values are represented by Map. The service provider application has no special operations when using the generalized invocation, and the service consumer application no longer needs to import the service provider’s SDK package.

It applies to SCENARIOS such as API gateway service and framework integration, and provides a unified management platform for Dubbo services, enabling each consumer to invoke the services registered on the unified management platform without introducing SDK libraries.

Usage examples

public interface GreetingService { String sayHello(String name); } @test public void Test () {// Apply ApplicationConfig ApplicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-consumer"); RegistryConfig = new RegistryConfig(); RegistryConfig. SetAddress (" zookeeper: / / 127.0.0.1:2181 "); ReferenceConfig<GenericService> reference = new ReferenceConfig<>(); reference.setApplication(applicationConfig); reference.setRegistry(registryConfig); reference.setInterface("com.xxx.GreetingService"); reference.setGeneric(true); / / get GenericService instead of ReferenceConfigCache cache = ReferenceConfigCache. GetCache (); GenericService genericService = cache.get(reference); ParameterTypes = new String[] {"java.lang.String"}; Object[] args = Stream.of("xiaoming").toArray(); Object result = genericService.$invoke("sayHello", parameterTypes, args); System.out.println("result = " + result); }Copy the code

Source code analysis

Service consumer

In dubbo’s chain of responsibility, GenericImplFilter intercepts the generalization call, validates the parameters, and makes an RPC call.

org.apache.dubbo.rpc.filter.GenericImplFilter#invoke

@Override public Result invoke(Invoker<? > invoker, Invocation invocation) throws RpcException { String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY); // Determine if it is a generalization call if (Invocation.getMethodName().equals(Constants.$INVOKE) && Invocation. GetArguments ()! = NULL && Mechanize.Getarguments ().length == 3 && protocolutils.isGeneric (generic)) {// Call 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()); }}} // Pass the generalization call method argument, (RpcInvocation).setattachment (Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY)); } // Invocation invocation return invoker. Invoke (Invocation); }Copy the code

Service Provider

The service provider intercepts the request using GenericFilter, deserializes the generalization parameters, and forwards the request to the specific service provider implementation object for business execution.

org.apache.dubbo.rpc.filter.GenericFilter#invoke

@Override public Result invoke(Invoker<? > invoker, Invocation inv) throws RpcException {// Generalization call if (Inv.getMethodName ().equals(Constants.$INVOKE) && Inv.getarguments ()! = null && inv.getArguments().length == 3 && ! GenericService. Class. IsAssignableFrom (invoker getInterface ())) {/ / parameter information String name = ((String) inv.getArguments()[0]).trim(); String[] types = (String[]) inv.getArguments()[1]; Object[] args = (Object[]) inv.getArguments()[2]; Try {/ / access to call Method Method. = ReflectUtils findMethodByMethodSignature (invoker. GetInterface (), the name, types). Class<? >[] params = method.getParameterTypes(); if (args == null) { args = new Object[params.length]; } String generic = inv.getAttachment(Constants.GENERIC_KEY); if (StringUtils.isBlank(generic)) { generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY); } // The generalization type is empty, If use the default deserialization way (StringUtils. IsEmpty (generic) | | ProtocolUtils. IsDefaultGenericSerialization (generic)) {args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); / / nativejava deserialization way} 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(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(...) ; }}} / / bean deserialization way 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(...) ); Invocation = invocation (Method, Args, inv.getattachments ()); if (result.hasException() && ! (result.getException() instanceof GenericException)) { return new RpcResult(new GenericException(result.getException())); } / / serialized processing and returns the result if (ProtocolUtils. IsJavaGenericSerialization (generic)) {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