泛化接口調用方式主要用於客戶端沒有 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);