用JAX-RS开发微服务
概念阐述
ServiceComb支持开发者使用JAX-RS注解,使用JAX-RS模式开发服务。
开发示例
步骤 1定义服务接口。
根据开发之前定义好的契约,编写Java业务接口,代码如下。定义接口不是必须的,但是 一个好习惯,可以简化客户端使用RPC方式编写代码。
public interface Hello {
String sayHi(String name);
String sayHello(Person person);
}
步骤 2实现服务。
使用JAX-RS注解开发业务代码,Hello的服务实现如下:
@RestSchema(schemaId = "jaxrsHello")
@Path("/jaxrshello")
@Produces(MediaType.APPLICATION_JSON)
public class JaxrsHelloImpl implements Hello {
@Path("/sayhi")
@POST
@Override
public String sayHi(String name) {
return "Hello " + name;
}
@Path("/sayhello")
@POST
@Override
public String sayHello(Person person) {
return "Hello person " + person.getName();
}
/**
* 这个方法是实现类特有的,因此对它的远程调用会有所不同.
* 具体可以参考 jaxrs-consumer
*/
@Path("/saybye")
@GET
public String sayBye() {
return "Bye !";
}
}
步骤 3发布服务。
在resources/META-INF/spring目录下创建jaxrsHello.bean.xml文件,配置spring进行服务扫描的base-package,文件内容如下。(注意修改package名称为正确名称)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
xmlns:p=" http://www.springframework.org/schema/p " xmlns:util=" http://www.springframework.org/schema/util "
xmlns:cse=" http://www.huawei.com/schema/paas/cse/rpc "
xmlns:context=" http://www.springframework.org/schema/context "
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">
<context:component-scan base-package="org.apache.servicecomb.samples.jaxrs.provider"/>
</beans>
步骤 4启动服务。
public class JaxrsProviderMain{
public static void main(String[] args) throws Exception {
Log4jUtils.init();
BeanUtils.init();
}
}
涉及API
JAX-RS开发模式当前支持如下注解,所有注解的使用方法参考JAX-RS官方文档。
表1-1JAX-RS注解支持汇总
注解 | 位置 | 描述 |
---|---|---|
javax.ws.rs.Path | schema/operation | URL路径 |
javax.ws.rs.Produces | schema/operation | 方法支持的编解码能力 |
javax.ws.rs.DELETE | operation | http method |
javax.ws.rs.GET | operation | http method |
javax.ws.rs.POST | operation | http method |
javax.ws.rs.PUT | operation | http method |
javax.ws.rs.QueryParam | parameter | 从query string中获取参数 |
javax.ws.rs.PathParam | parameter | 从path中获取参数,必须在path中定义该参数 |
javax.ws.rs.HeaderParam | parameter | 从header中获取参数 |
javax.ws.rs.CookieParam | parameter | 从cookie中获取参数 |
javax.ws.rs.FormParam | parameter | 从form中获取参数 |
javax.ws.rs.BeanParam | parameter | 用于参数聚合,允许在一个JavaBean的属性上打上参数标记以将多个参数聚合为一个JavaBean |
说明:
- 当方法参数没有注解,且不为
HttpServletRequest
、InvocationContext
类型参数时,默认为body类型参数,一个方法最多只支持一个body类型参数。
使用@BeanParam聚合参数
使用说明
用户可以使用@BeanParam注解将多个参数聚合到一个JavaBean中,通过将@QueryParam等参数注解打在此JavaBean的属性或setter方法上来声明参数,从而简化业务接口的参数表。可以参考JAX-RS的官方说明:https://docs.oracle.com/javaee/7/api/javax/ws/rs/BeanParam.html
ServiceComb现在也支持类似的用法,该用法的要求如下:
- 聚合参数所用的类型必须是标准的JavaBean,即类型的属性与getter、setter方法名称匹配,setter方法的返回类型为
void
- 参数注解可以打在JavaBean的属性或setter方法上
- 允许通过@FormParam将多个上传文件参数聚合到JavaBean中
- 作为BeanParam的JavaBean内部如果有多余的属性,需要打上
@JsonIgnore
忽略掉 - body参数无法聚合进BeanParam
- Consumer端不支持将参数聚合为JavaBean发送,即仍然需要按照接口契约单独填写各个参数
代码示例
Provider端开发服务
- Provider端业务接口代码:
@RestSchema(schemaId = "helloService") @Path("/hello") public class HelloService { @Path("/sayHello/{name}") @GET public String sayHello(@BeanParam Person person) { System.out.println("sayHello is called, person = [" + person + "]"); return "Hello, your name is " + person.getName() + ", and age is " + person.getAge(); } }
BeanParam参数定义:
public class Person { private String name; @QueryParam("age") private int age; @PathParam("name") public void setName(String name) { this.name = name; } @JsonIgnore // 忽略复杂属性 private List<Person> children; // 其他方法忽略 }
- 接口契约:
# 忽略契约的其他部分 basePath: "/hello" paths: /sayHello/{name}: get: operationId: "sayHello" parameters: - name: "name" in: "path" required: true type: "string" - name: "age" in: "query" required: false type: "integer" format: "int32" responses: 200: description: "response of 200" schema: type: "string"
Consumer端调用服务
- consumer端RPC开发模式:
- Provider接口定义
public interface HelloServiceIntf { String sayHello(String name, int age); }
- 调用代码
String result = helloService.sayHello("Bob", 22); // result的值为"Hello, your name is Bob, and age is 22"
- Provider接口定义
- consumer端RestTemplate开发模式:
String result = restTemplate.getForObject( "cse://provider-service/hello/sayHello/Bob?age=22", String.class); // 调用效果与RPC方式相同