GenericService是Dubbo提供的泛化接口,用来进行泛化调用。
GenericService接口只有一个方法:
Object $invoke(String var1, String[] var2, Object[] var3) throws GenericException;
第一个参数是方法名。
第二个参数是一个字符串数组,这是接口方法每个参数类型的全路径。
第三个参数是Object数组,是传给方法的具体参数列表。
Dubbo服务的提供者和消费者都可以使用这个接口,场景略有不同,二者也未必同时存在,可以由提供者使用GenericService,消费者用具体接口类,或者消费者使用GenericService,提供者用具体接口类,或者提供者和消费者都用GenericService,只要指定的接口能对得上,调用都没问题。
下面分别看一下。
当服务提供者使用这个接口时,可以省略Interface的代码,省略方法和参数的声明,通过指定接口名称的方式向zookeeper发布Dubbo服务。
这时GenericService就像是一个网关。
举个例子:
@Override
public void afterSingletonsInstantiated() {
ServiceConfig service = new ServiceConfig();
ApplicationConfig application = new ApplicationConfig("test-provider");
service.setApplication(application);
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
service.setRegistry(registryConfig);
service.setInterface("com.suibian.WhateverService");
GenericService genericService = (method, parameterTypes, args) -> {
if ("method1".equals(method)) {
return "method1 result:" + args[0];
}
if ("method2".equals(method)) {
return 12345;
}
if (parameterTypes.length == 2
&& parameterTypes[0].equals("java.lang.String")
&& parameterTypes[1].equals("java.lang.Integer")
&& "method3".equals(method)) {
return "method3,param1:" + args[0] + ",param2:" + args[1];
}
return null;
};
service.setRef(genericService);
service.export();
}
大概说明一下:
1,registryConfig里需要写好注册中心地址。zookeeper注册中心格式是这样的:zookeeper://127.0.0.1:2181。
2,ServiceConfig.setInterface()用于指定要发布的接口名称,这个名称可以不对应任何的java接口类,甚至可以随便写。在上面的例子中com.suibian.WhateverService这个名字就是随便写的,实际上代码中并没有这个接口类。
3,因为这样发布的Dubbo服务没有具体的接口类,所以invoke()方法的第一个参数String var1(原本是方法名)和第二个参数String[] var2(原本是方法参数类型列表)就脱离了具体接口类的束缚,可以接收任意值,就像上面例子中所写的一样,按照不同的参数执行不同的逻辑,就像一个网关一样。
4,Dubbo服务的发布本身不需要Spring服务启动,但如果把上面的代码放在main()方法中,随着进程结束,发布的Dubbo服务也会立即被取消。
5,向zookeeper中注册好的GenericService在zookeeper中是这样的:
[zk: localhost:2181(CONNECTED) 4] ls /dubbo/com.suibian.WhateverService/providers
[dubbo%3A%2F%2F172.16.111.111%3A20880%2Fcom.suibian.WhateverService%3Fanyhost%3Dtrue%26application%3Dtest-provider%26dubbo%3D2.5.3%26generic%3Dtrue%26interface%3Dcom.suibian.WhateverService%26methods%3D*%26pid%3D3856%26side%3Dprovider%26timestamp%3D1592905521950]
可以看到里面没有方法列表,有generic标识。
当服务消费者使用这个接口时,有个好处是不依赖服务提供者的Interface接口类,而是通过指定具体接口类路径的方式,创建消费者,并去zookeeper查找对应的提供者,然后发起调用。
举个例子:
public static void test3() {
ApplicationConfig application = new ApplicationConfig("test-consumer"); //参数为dubbo消费方的名称
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
application.setRegistry(registryConfig);
ReferenceConfig reference = new ReferenceConfig();
reference.setApplication(application);
reference.setInterface("com.suibian.WhateverService");
reference.setTimeout(3000);
reference.setGeneric(true);
GenericService genericService = reference.get();
Object object1 = genericService.$invoke("method1", new String[]{"java.lang.String"}, new Object[]{"this is parameter"}); //调用泛化接口
System.out.println(object1);
Object object2 = genericService.$invoke("method2", new String[]{"java.lang.String"}, new Object[]{"this is parameter"}); //调用泛化接口
System.out.println(object2);
Object object3 = genericService.$invoke("method3", new String[]{"java.lang.String", "java.lang.Integer"}, new Object[]{"ABCD", 1234}); //调用泛化接口
System.out.println(object3);
}
public static void main(String[] args) {
test3();
}
根据上面提供者发布的接口,这次调用输出的结果是:
method1 result:this is parameter
12345
method3,param1:ABCD,param2:1234
可以看到,消费者使用GenericService时并不需要启动Spring服务,reference.get()方法会直接向zookeeper注册consumer,然后调用invoke()方法发起调用。
使用时记得加上reference.setGeneric(true);
因为不依赖提供者的接口类,只是指定了接口类名和参数,所以调用时无法知道方法具体的返回值类型。
虽然invoke()方法的返回类型统一都是Object,不过还是能分成以下几种情况:
1,方法的返回类型是简单类型,比如Integer和String,比如这样的方法:
Integer findOne (String code);
返回的类型就是这种简单类型本身。
2,方法的返回类型是单个非简单元素,比如这样的方法:
Student findOne (String code);
返回值类型实际上是HashMap。key是提供者的接口类中方法返回类型的属性名,value是属性值。
3,方法的返回类型是列表,比如这样的方法:
List findList (String code);
返回值类型实际上是ArrayList
本文结束