泛化调用

在进行 RPC 调用时,应用无需依赖服务提供方的 JAR 包,只需要知道服务的接口名、方法名即可调用 RPC 服务。

泛化接口

public interface GenericService{

/**
   * 泛化调用仅支持方法参数为基本数据类型,或者方法参数类型在当前应用的 ClassLoader 中存在的情况。
   * 
   * @param methodName 调用方法名。
   * @param args 调用参数列表。
   * @return 调用结果。
   * @throws com.alipay.sofa.rpc.core.exception.GenericException 调用异常。
   */
Object $invoke(String methodName,String[] argTypes,Object[] args) throws GenericException;

/**
  * 支持参数类型无法在类加载器加载情况的泛化调用,对于非 JDK 类会序列化为 GenericObject。
  *
  * @param methodName  调用方法名。
  * @param argTypes  参数类型。
  * @param args 方法参数,参数类型支持 GenericObject。
  * @return result GenericObject 类型。
  * @throws com.alipay.sofa.rpc.core.exception.GenericException
  */
Object $genericInvoke(String methodName,String[] argTypes,Object[] args)
throws GenericException;

/**
     * 支持参数类型无法在类加载器加载情况的泛化调用。
     *
     * @param methodName 调用方法名。
     * @param argTypes   参数类型。
     * @param args       方法参数,参数类型支持 GenericObject。
     * @param context    GenericContext
     * @return result GenericObject 类型。
     * @throws com.alipay.sofa.rpc.core.exception.GenericException
     */
Object $genericInvoke(String methodName,String[] argTypes,Object[] args,
GenericContext context)throws GenericException;

/**
  * 支持参数类型无法在类加载器加载情况的泛化调用,返回结果类型为 T。
  *
  * @param methodName  调用方法名。
  * @param argTypes  参数类型。
  * @param args 方法参数,参数类型支持 GenericObject。
  * @return result T 类型。
  * @throws com.alipay.sofa.rpc.core.exception.GenericException
  */
<T> T $genericInvoke(String methodName,String[] argTypes,Object[] args,Class<T> clazz) throws GenericException;

/**
     * 支持参数类型无法在类加载器加载情况的泛化调用。
     *
     * @param methodName 调用方法名。
     * @param argTypes   参数类型。
     * @param args       方法参数,参数类型支持 GenericObject。
     * @param clazz      返回类型。
     * @param context    GenericContext
     * @return result T 类型。
     * @throws com.alipay.sofa.rpc.core.exception.GenericException
     */
<T> T $genericInvoke(String methodName,String[] argTypes,Object[] args,Class<T> clazz,GenericContext context)throwsGenericException;

}

$invoke 仅支持方法参数类型在当前应用的 ClassLoader 中存在的情况;$genericInvoke 支持方法参数类型在当前应用的 ClassLoader 中不存在的情况。

使用示例

  • 使用 Spring XML 创建泛化调用代理对象

    <!-- 引用 TR 服务 -->
    <sofa:reference interface="com.alipay.sofa.rpc.api.GenericService" id="genericService">
        <sofa:binding.tr>
            <sofa:global-attrs generic-interface="com.alipay.test.SampleService"/>
        </sofa:binding.tr>
    </sofa:reference>

    Java 代码:

     /**
         * Java Bean
         */
        public class People{
            private String name;
            private int    age;
    
            // getters and setters
        }
    
    
        /**
         * 服务方提供的接口。
         */
        interface SampleService{
            String hello(String arg);
            People hello(People people);
        }
    

     /**
         * 消费方测试类。
         */
        public class ConsumerClass{
            GenericService genericService;
    
            public void invokeTest(){
                // 1. $invoke 仅支持方法参数类型在当前应用的 ClassLoader 中存在的情况。
                genericService.$invoke("hello",new String[]{String.class.getName()},new Object[]{"I'm an arg"});
    
                // 2. $genericInvoke 支持方法参数类型在当前应用的 ClassLoader 中不存在的情况。
                // 2.1 构造参数
                GenericObject genericObject =new GenericObject("com.alipay.sofa.rpc.test.generic.bean.People");// 构造函数中指定全路径类名。
                genericObject.putField("name","Lilei");// 调用 putField,指定field值。
                genericObject.putField("age",15);
    
                // 2.2 进行调用,不指定返回类型,返回结果类型为 GenericObject。
                Object obj = genericService.$genericInvoke("hello",new String[]{"com.alipay.sofa.rpc.test.generic.bean.People"},new Object[]{ genericObject });
                Assert.assertTrue(obj.getClass()==GenericObject.class);
    
                // 2.3 进行调用,指定返回类型。
                People people = genericService.$genericInvoke("hello",new String[]{"com.alipay.sofa.rpc.test.generic.bean.People"},new Object[]{ genericObject },People.class);
    
                // 3. LDC 架构下的泛化调用使用。
                // 3.1 构造 GenericContext 对象。
                AlipayGenericContext genericContext =new AlipayGenericContext();
                genericContext.setUid("33");
    
                // 3.2 进行调用。
                People people = genericService.$genericInvoke("hello",new String[]{"com.alipay.sofa.rpc.test.generic.bean.People"},new Object[]{ genericObject },People.class, genericContext);
            }
        }
  • 使用 API 创建泛化调用代理对象

    在非必要情况下,并不推荐项目中使用 API 方式创建泛化调用代理对象,建议您优先通过 Spring XML 进行创建。如果您无法使用 Spring XML 方式进行初始化,可考虑使用 API 方式进行创建。

    使用 API 方式创建泛化调用代理时,需要注意以下事项:

    • 泛化调用代理是比较重要的对象,不可放在交易中反复创建。

      RPC 框架会检查是否有重复创建的情况,当相同配置的服务引用超过 3 次时,会产生异常,影响正常使用。

    • 在应用启动过程中进行对象创建,通过异常中止、健康检查等方式确保项目启动时,代理对象已创建成功。

    • 在应用退出前销毁泛化调用代理,实现优雅停止。

    Java 代码:

    @Component
    public class GenericServiceDemo implements InitializingBean, DisposableBean {
        private ConsumerConfig<GenericService> consumerConfig;
        private GenericService genericService;
    
        public GenericService getGenericService() {
            return genericService;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            this.consumerConfig = new ConsumerConfig<>();
            RegistryConfig registryConfig = new RegistryConfig()
                    .setProtocol("dsr");
            consumerConfig.setGeneric(true)
                    .setInterfaceId("com.alipay.test.SampleService")
                    .setRegistry(registryConfig)
                    .setProtocol("bolt")    // 引用 Bolt 服务。
                    .setTimeout(5000);      // 设置超时时间。
            this.genericService = consumerConfig.refer();
        }
    
        @Override
        public void destroy() throws Exception {
            if(this.consumerConfig != null) {
                consumerConfig.unRefer();   // 注销引用。
            }
        }
    }

特殊说明

调用 $genericInvoke(String methodName, String[] argTypes, Object[] args) 接口,会将除以下包以外的其他类序列化为 GenericObject

"com.sun",
"java",
"javax",
"org.ietf",
"org.ogm",
"org.w3c",
"org.xml",
"sunw.io",
"sunw.util"