实践Dubbo之泛化

泛化引用

提供者接口

1
2
3
public interface DemoService {
    public String helloService(String name);
}

xml配置方式

消费者不需要强依赖提供者的接口class,重点:generic="true"

1
<dubbo:reference id="demoService" interface="wang.ray.dubbo.demo.api.DemoService" generic="true"/>

消费者调用

1
2
GenericService demoService = (GenericService) context.getBean("demoService");
      Object result = demoService.$invoke("helloService", new String[] { "java.lang.String" }, new Object[] { "hello" });

$invoke方法有三个参数:

第一个参数是方法名

第二个参数是方法的参数类型的字符串数组。比如:

1
2
// 参数类型
new String[] {"java.lang.String","java.util.Map","java.util.List","java.util.Date","wang.ray.test.TestDTO"}

第三个参数是方法的参数值的对象数组,基本类型以及Date,List,Map等不需要转换,POJO则需要转换为Map,可以用class这个key指定Map对应的类型,比如泛型子属性等的类型。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 指定当前对象的类型(这种场景一般不需要指定,以为上面第二个参数已经指定了)
testDtoMap.put("class","wang.ray.test.TestDTO");
testDtoMap.put("username",123);

// 如果TestDTO有一个POJO的data属性,则可以指定子属性的类型
Map<String, Object> book = new HashMap<String, Object>();
book.put("class","wang.ray.test.Book");
book.put("name", "ray");

testDtoMap.put("data", book);

// 参数值
// map,list,date,testDtoMap都是参数值,testDtoMap是从DTO转换为Map来的
new Object[] { "hello",map,list,date,testDtoMap}

第二个参数和第三个参数都是数组,长度要保持一致。

Java代码方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 普通编码配置方式
ApplicationConfig application = new ApplicationConfig();
application.setName("demo-service-consumer");

// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");

ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setInterface("wang.ray.dubbo.demo.api.DemoService");
// 声明为泛化接口
reference.setGeneric(true); 

ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(reference);

Object result = genericService.$invoke("helloService", new String[] { "java.lang.String" },new Object[] { "world" });

注解方式

这种方式其实已经获得了接口,一般没必要使用泛化调用。

可以利用泛化的异步调用。

1
2
3
4
5
6
7
@Reference(generic = true,interfaceName = "wang.ray.dubbo.demo.api.DemoService")
private SampleRemoteService sampleRemoteService;

GenericService genericService = (GenericService) sampleRemoteService;
            CompletableFuture<Object> future = genericService.$invokeAsync("helloService", new String[] { "java.lang.String" }, new Object[] { "Wang" });
            String result = (String) future.get();
            logger.warn(result);

泛化实现

GenericService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package org.apache.dubbo.rpc.service;

import java.util.concurrent.CompletableFuture;

/**
 * Generic service interface
 *
 * @export
 */
public interface GenericService {

    /**
     * Generic invocation
     *
     * @param method         Method name, e.g. findPerson. If there are overridden methods, parameter info is
     *                       required, e.g. findPerson(java.lang.String)
     * @param parameterTypes Parameter types
     * @param args           Arguments
     * @return invocation return value
     * @throws GenericException potential exception thrown from the invocation
     */
    Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;

    default CompletableFuture<Object> $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException {
        Object object = $invoke(method, parameterTypes, args);
        if (object instanceof CompletableFuture) {
            return (CompletableFuture<Object>) object;
        }
        return CompletableFuture.completedFuture(object);
    }

}

参考

Dubbo的泛化调用

dubbo高级用法之泛化与接口自适应