dubbo泛化调用
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。
dubbo支持泛化调用
1、开发工程中 有时候想要测试dubbo接口 需要自定义用例,比较麻烦
2、无法调用服务器上的dubbo测试
3、无法随意调用多版本的服务
由于dubbo支持泛化调用 所以这些问题就迎刃而解了
/* * Copyright 1999-2011 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.dubbo.rpc.service; /** * 通用服务接口*/ public interface GenericService { /** * 泛化调用 * * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String) * @param parameterTypes 参数类型 * @param args 参数列表 * @return 返回值 * @throws Throwable 方法抛出的异常 */ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; }
// 当前应用的信息 private static ApplicationConfig application = new ApplicationConfig(); // 注册中心信息缓存 private static Map<String, RegistryConfig> registryConfigCache = new ConcurrentHashMap<>(); // 各个业务方的ReferenceConfig缓存 private static Map<String, ReferenceConfig> referenceCache = new ConcurrentHashMap<>(); static { application.setName("consumer-test"); } /** * dubbp接口泛化调用 * @param interfaceName 类名包含全路径:com.xx.xxService * @param methodName 方法名 qryAvailableBonusForPay * @param address zk地址 dubbo.registry.address=zookeeper://xx:xx?backup=xx:xx,xx:xx * @param group zk分组 dubbo.registry.group=GLOBAL_REGISTRY * @param paramTypes 参数类型 new String[]{"java.lang.String","com.xx.MyParam"} * @param params 参数值 new Object[]{"123", new HashMap<>()} * @return object */ public static Object invoke(String interfaceName, String methodName, String address, String group, String[] paramTypes, Object[] params) throws BaseAppException { ReferenceConfig reference = getReferenceConfig(interfaceName, address, group);// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用 // GenericService genericService = (GenericService) reference.get(); // ReferenceConfig的get方法创建代理对象是一个很重的操作,在泛化调用时如果每次都用新的ReferenceConfig对象的get方法创建代理对象, // 将浪费大量的cpu时间并可能导致内存泄露(内存泄露问题9.0.8已修复) ReferenceConfigCache cache = ReferenceConfigCache.getCache(); GenericService genericService = (GenericService) cache.get(reference); // 基本类型以及Date,List,Map等不需要转换,直接调用 Object object = genericService.$invoke(methodName, paramTypes, params);return object; } private static ReferenceConfig getReferenceConfig(String interfaceName, String address, String group) throws BaseAppException {// 引用远程服务 封装了所有与注册中心及服务提供方连接 ReferenceConfig referenceConfig = referenceCache.get(interfaceName); if (null == referenceConfig) { referenceConfig = new ReferenceConfig<>(); // dubbo consumer的application配置 referenceConfig.setApplication(application); // 注册中心信息 referenceConfig.setRegistry(getRegistryConfig(address, group)); // 弱类型接口名 referenceConfig.setInterface(interfaceName); // 声明为泛化接口 referenceConfig.setGeneric(true); referenceCache.put(interfaceName, referenceConfig); } return referenceConfig; } private static RegistryConfig getRegistryConfig(String address, String group) throws BaseAppException { String key = address + "-" + group; RegistryConfig registryConfig = registryConfigCache.get(key); if (null == registryConfig) { registryConfig = new RegistryConfig(); registryConfig.setAddress(address); registryConfig.setGroup(group); registryConfigCache.put(key, registryConfig); } return registryConfig; }
以上代码为本地测试工具类
在 Java 代码获取 barService 并开始泛化调用:
GenericService barService = (GenericService) applicationContext.getBean("barService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
通过API方式
import com.alibaba.dubbo.rpc.service.GenericService; ... // 引用远程服务 // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); // 弱类型接口名 reference.setInterface("com.xxx.XxxService"); reference.setVersion("1.0.0"); // 声明为泛化接口 reference.setGeneric(true); // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 GenericService genericService = reference.get(); // 基本类型以及Date,List,Map等不需要转换,直接调用 Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"}); // 用Map表示POJO参数,如果返回值为POJO也将自动转成Map Map<String, Object> person = new HashMap<String, Object>(); person.put("name", "xxx"); person.put("password", "yyy"); // 如果返回POJO将自动转成Map Object result = genericService.$invoke("findPerson", new String[] {"com.xxx.Person"}, new Object[]{person}); ...
如果参数中对象存在复杂结构(也就是参数类型中某个参数是复杂对象),这时,可以使用map来包入参,入参类型可以通过class属性保留下来,如果POJO 数据:
Person person = new PersonImpl(); person.setName("xxx"); person.setPassword("yyy");
可用下面 Map 表示:
Map<String, Object> map = new HashMap<String, Object>(); // 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。 map.put("class", "com.xxx.PersonImpl"); map.put("name", "xxx"); map.put("password", "yyy");
demo举例
import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.rpc.service.GenericService; import com.alibaba.fastjson.JSON; import java.util.HashMap; import java.util.Map; public class TestDubboGeneric { public static void main(String[] args) { ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); // 当前dubbo consumer的application配置,不设置会直接抛异常 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("xxx_test_service"); // 注册中心配置 RegistryConfig registryConfig = new RegistryConfig(); // 注册中心这里需要配置上注册中心协议,例如下面的zookeeper registryConfig.setAddress("zookeeper://192.168.0.1:2181"); registryConfig.setGroup("test_group"); reference.setApplication(applicationConfig); reference.setRegistry(registryConfig); // 设置调用的reference属性,下面只设置了协议、接口名、版本、超时时间 reference.setProtocol("dubbo"); reference.setInterface("com.xxx.test.TestService"); reference.setVersion("1.0.0"); reference.setTimeout(1000); // 声明为泛化接口 reference.setGeneric(true); // GenericService可以接住所有的实现 GenericService genericService = reference.get(); // 构造复杂参数,下面的示例中,头两个参数为string类型,后一个是一个复杂类型,但都可以通过map构造。 Map<String, Object> param = new HashMap<>(); param.put("test1", "a"); param.put("test2", "b"); Map<String,Object> thirdParam = new HashMap<>(); thirdParam.put("class","java.util.Map"); thirdParam.put("subParam1","c"); thirdParam.put("subParam2","d"); param.put("test3",thirdParam); Object result = genericService.$invoke("myMethod", new String[]{"java.lang.String", "java.lang.String", "com.xxxtest.MyParam"}, new Object[]{"123", "ddd",param}); System.out.println(JSON.toJSONString(result)); } }
优化:
// GenericService genericService = (GenericService) reference.get(); // ReferenceConfig的get方法创建代理对象是一个很重的操作,在泛化调用时如果每次都用新的ReferenceConfig对象的get方法创建代理对象, // 将浪费大量的cpu时间并可能导致内存泄露 ReferenceConfigCache cache = ReferenceConfigCache.getCache(); GenericService genericService = (GenericService) cache.get(reference);
不积跬步,无以至千里;不积小流,无以成江海
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!