用JAX-RS开发微服务

概念阐述

ServiceComb支持开发者使用JAX-RS注解,使用JAX-RS模式开发服务。

开发示例

步骤 1定义服务接口。

根据开发之前定义好的契约,编写Java业务接口,代码如下。定义接口不是必须的,但是 一个好习惯,可以简化客户端使用RPC方式编写代码。

  1. public interface Hello {
  2. String sayHi(String name);
  3. String sayHello(Person person);
  4. }

步骤 2实现服务。

使用JAX-RS注解开发业务代码,Hello的服务实现如下:

  1. @RestSchema(schemaId = "jaxrsHello")
  2. @Path("/jaxrshello")
  3. @Produces(MediaType.APPLICATION_JSON)
  4. public class JaxrsHelloImpl implements Hello {
  5. @Path("/sayhi")
  6. @POST
  7. @Override
  8. public String sayHi(String name) {
  9. return "Hello " + name;
  10. }
  11. @Path("/sayhello")
  12. @POST
  13. @Override
  14. public String sayHello(Person person) {
  15. return "Hello person " + person.getName();
  16. }
  17. /**
  18. * 这个方法是实现类特有的,因此对它的远程调用会有所不同.
  19. * 具体可以参考 jaxrs-consumer
  20. */
  21. @Path("/saybye")
  22. @GET
  23. public String sayBye() {
  24. return "Bye !";
  25. }
  26. }

步骤 3发布服务。

在resources/META-INF/spring目录下创建jaxrsHello.bean.xml文件,配置spring进行服务扫描的base-package,文件内容如下。(注意修改package名称为正确名称)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns=" http://www.springframework.org/schema/beans " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
  3. xmlns:p=" http://www.springframework.org/schema/p " xmlns:util=" http://www.springframework.org/schema/util "
  4. xmlns:cse=" http://www.huawei.com/schema/paas/cse/rpc "
  5. xmlns:context=" http://www.springframework.org/schema/context "
  6. xsi:schemaLocation=" http://www.springframework.org/schema/beans classpath:org/springframework/beans/factory/xml/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.huawei.com/schema/paas/cse/rpc classpath:META-INF/spring/spring-paas-cse-rpc.xsd">
  7. <context:component-scan base-package="org.apache.servicecomb.samples.jaxrs.provider"/>
  8. </beans>

步骤 4启动服务。

  1. public class JaxrsProviderMain{
  2. public static void main(String[] args) throws Exception {
  3. Log4jUtils.init();
  4. BeanUtils.init();
  5. }
  6. }

涉及API

JAX-RS开发模式当前支持如下注解,所有注解的使用方法参考JAX-RS官方文档

表1-1JAX-RS注解支持汇总

注解位置描述
javax.ws.rs.Pathschema/operationURL路径
javax.ws.rs.Producesschema/operation方法支持的编解码能力
javax.ws.rs.DELETEoperationhttp method
javax.ws.rs.GEToperationhttp method
javax.ws.rs.POSToperationhttp method
javax.ws.rs.PUToperationhttp method
javax.ws.rs.QueryParamparameter从query string中获取参数
javax.ws.rs.PathParamparameter从path中获取参数,必须在path中定义该参数
javax.ws.rs.HeaderParamparameter从header中获取参数
javax.ws.rs.CookieParamparameter从cookie中获取参数
javax.ws.rs.FormParamparameter从form中获取参数
javax.ws.rs.BeanParamparameter用于参数聚合,允许在一个JavaBean的属性上打上参数标记以将多个参数聚合为一个JavaBean

说明:

  • 当方法参数没有注解,且不为HttpServletRequestInvocationContext类型参数时,默认为body类型参数,一个方法最多只支持一个body类型参数。

使用@BeanParam聚合参数

使用说明

用户可以使用@BeanParam注解将多个参数聚合到一个JavaBean中,通过将@QueryParam等参数注解打在此JavaBean的属性或setter方法上来声明参数,从而简化业务接口的参数表。可以参考JAX-RS的官方说明:https://docs.oracle.com/javaee/7/api/javax/ws/rs/BeanParam.html

ServiceComb现在也支持类似的用法,该用法的要求如下:

  1. 聚合参数所用的类型必须是标准的JavaBean,即类型的属性与getter、setter方法名称匹配,setter方法的返回类型为void
  2. 参数注解可以打在JavaBean的属性或setter方法上
  3. 允许通过@FormParam将多个上传文件参数聚合到JavaBean中
  4. 作为BeanParam的JavaBean内部如果有多余的属性,需要打上@JsonIgnore忽略掉
  5. body参数无法聚合进BeanParam
  6. Consumer端不支持将参数聚合为JavaBean发送,即仍然需要按照接口契约单独填写各个参数

代码示例

Provider端开发服务

  • Provider端业务接口代码:

    1. @RestSchema(schemaId = "helloService")
    2. @Path("/hello")
    3. public class HelloService {
    4. @Path("/sayHello/{name}")
    5. @GET
    6. public String sayHello(@BeanParam Person person) {
    7. System.out.println("sayHello is called, person = [" + person + "]");
    8. return "Hello, your name is " + person.getName() + ", and age is " + person.getAge();
    9. }
    10. }
  • BeanParam参数定义:

    1. public class Person {
    2. private String name;
    3. @QueryParam("age")
    4. private int age;
    5. @PathParam("name")
    6. public void setName(String name) {
    7. this.name = name;
    8. }
    9. @JsonIgnore // 忽略复杂属性
    10. private List<Person> children;
    11. // 其他方法忽略
    12. }
  • 接口契约:

    1. # 忽略契约的其他部分
    2. basePath: "/hello"
    3. paths:
    4. /sayHello/{name}:
    5. get:
    6. operationId: "sayHello"
    7. parameters:
    8. - name: "name"
    9. in: "path"
    10. required: true
    11. type: "string"
    12. - name: "age"
    13. in: "query"
    14. required: false
    15. type: "integer"
    16. format: "int32"
    17. responses:
    18. 200:
    19. description: "response of 200"
    20. schema:
    21. type: "string"

Consumer端调用服务

  • consumer端RPC开发模式:

    • Provider接口定义

      1. public interface HelloServiceIntf {
      2. String sayHello(String name, int age);
      3. }
    • 调用代码

      1. String result = helloService.sayHello("Bob", 22); // result的值为"Hello, your name is Bob, and age is 22"
  • consumer端RestTemplate开发模式:

    1. String result = restTemplate.getForObject(
    2. "cse://provider-service/hello/sayHello/Bob?age=22",
    3. String.class); // 调用效果与RPC方式相同