两个用途一直是泛化服务,一种是泛化引用。前者是为了provider省事,后者是消费者省事。先介绍泛化服务怎么做到的:
比如提供者想省事,虽然提供了10个方法,但是不想在接口里面写十个方法,也就不用给这十个方法做函数声明、参数声明了。直接通过:
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>(); service.setApplication(new ApplicationConfig("generic-provider")); service.setRegistry(new RegistryConfig("N/A")); service.setProtocol(new ProtocolConfig("dubbo", 29581)); service.setInterface(DemoService.class.getName()); service.setRef(new GenericService() { public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("sayName".equals(method)) { return "Generic " + args[0]; } if ("throwDemoException".equals(method)) { throw new GenericException(DemoException.class.getName(), "Generic"); } if ("getUsers".equals(method)) { return args[0]; } return null; } }); service.export();
也就是通过method不同,直接返回不同处理结果,隐藏了方法和参数声明。上面虽然 service.setInterface(DemoService.class.getName()),这里传入的是接口的str名称。也就是说通过generic,provider已经完全不依赖interface了,只是用到这个接口的名称字符串用做servericekey用的。
之所以能这样操作,消费者在调用的时候,需要在url里面指定generic=true,那么在经过filter链的时候,在GenericImplFilter会做generic处理
把method-name设置成$invoke
parameter-types由于提供者不一定有,于是传入类型的string类型。
args按照pojo或者java-bean进行序列化成一个map,里面指定了参数的class类型、每个filed的value。
provider不会把这个map做逆序列化处理,直接原样返回这个map,所以可以看到这个generic的provider能够处理的东西比较有限。消费者在得到结果的时候,依然是一个map,那么在GenericImplFilter里面,把invoke得到的结果逆序列化成具体的对象。也就是说由GenericImplFilter序列化成一个map,你自己负责逆序列化。
对于泛化引用的话,例子如下:
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); reference.setApplication(new ApplicationConfig("generic-consumer")); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:29581?scope=remote"); reference.setGeneric(true); GenericService genericService = reference.get(); try { List<Map<String, Object>> users = new ArrayList<Map<String, Object>>(); Map<String, Object> user = new HashMap<String, Object>(); user.put("class", "com.alibaba.dubbo.config.api.User"); user.put("name", "actual.provider"); users.add(user); users = (List<Map<String, Object>>) genericService.$invoke("getUsers", new String[]{List.class.getName()}, new Object[]{users});
消费者直接使用$invoke方法,不用考虑provider到底提供了哪些方法,通过字符串getuser调用,参数类型也不用关心,直接用map描述参数的类型和value。
provider在被调用的时候,还是GenericImplFilter这个filter里面,由provider通过map进行逆序列化,毕竟provider提供的方法里面是按照这个参数类型进行处理的,所以必须从map转化成真正的类型,处理完成以后,还要逆序列化成map才能返回给consumer,因为consumer只认识map,不认识具体参数类型。