相对于需要依赖业务客户端 Jar 包的正常调用,泛化调用,不要不依赖二方包,使用其特定的 GenericService 接口,传入需要调用的方法名,方法签名和参数值进行调用服务。 泛化调用适用于一些网关应用(没办法依赖所有服务的二方包),其中 hsfops 服务测试也是依赖泛化调用功能

API 形式配置 HSF 服务

HSFConsumerBean 配置 generictrue,标识 HSF 客户端忽略加载不到接口的异常。

HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
hsfApiConsumerBean.setVersion("1.0.0");
hsfApiConsumerBean.setGroup("HSF");
// [设置] 泛化配置
hsfApiConsumerBean.setGeneric("true");
hsfApiConsumerBean.init(true);

// 使用泛化接口获取代理
GenericService genericOrderService = (GenericService) hsfApiConsumerBean.getObject();
// ---------------------- 调用 -----------------------//
// [调用] 发起 HSF 泛化调用, 返回 map 类型的 result。
Map orderModelMap = (Map) genericOrderService.$invoke("queryOrder",
                            // 方法入参类型数组(xxx.getClass().getName())
                            new String[] { Long.class.getName() },
                            // 参数,如果是 pojo,则需要转成 Map
                            new Object[] { 1L});

GenericService 提供的 $invoke 方法包含了真实调用的方法名、入参类型和参数,以便服务端找到改方法。由于没有依赖服务端的 API jar 包,传入的参数如果是自定义的 DTO,需要转成客户端可以序列化的 Map 类型。

调用传方法签名和参数说明
  • 方法没有入参,可以只传 methodName: service.$invoke("sayHello", null, null)
  • 方法类型有泛型的,列如 List<String>,只需要传 java.util.List,即 List.class.getName() 的值,不要传成 java.util.List<String> ,否则会出现方法找不到的错误。
  • 调用方在不确定格式的情况下,可以写个单元测试,测试时依赖需要泛化调用的二方包,使用 HSF 提供的工具类 com.taobao.hsf.util.PojoUtilsgeneralize() 方法来生成一个 POJO Bean 的 Map 描述格式。
    Map pojoMap = (Map) PojoUtils.generalize(new OrderModel());
  • 传递参数为 POJO 的 demo。
     class User {
        private String name;
        private int age;
        // 需要是标准的 pojo 格式,这里省略 getter setter
       }
    
       // 直接使用 map 去构造 pojo 对应的泛化参数
       Map param = new HashMap<String, Object>();
       param.put("age", 11);
       param.put("name","Miles");
       // 当传递的参数是声明参数类型的子类时,需要传入 class 字段,标明该 pojo 的真实类型(服务端需要有该类型)
       param.put("class", "com.taobao.User");

Spring 配置 HSF 服务

Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

<bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
    <!--[设置] 订阅服务的接口 -->
    <property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
    <!--[设置] 服务的版本 -->
    <property name="version" value="1.0.0"/>
    <!--[设置] 服务的归组 -->
    <property name="group" value="HSF"/>
    <property name="generic" value="true"/>
</bean>

注意事项

  • 泛化调用,如果客户端没有接口类,路由规则默认不生效。
  • 泛化调用性能会比正常调用差。
  • 配置抛出业务异常。-Dhsf.generic.throw.exception=true (默认是 false, 把异常泛化成 map 返回)

    本地存在异常类,由于 com.taobao.hsf.remoting.service.GenericService 上没有声明该异常,如果不是 RuntimeException 类型或其子类,则会抛出 UndeclaredThrowableException,可以通过 getCause 获取真实异。

    本地没有该异常类,则抛出 com.taobao.hsf.util.GenericInvocationException