REST 全称是 Representational State Transfer(表述性状态转移),它是 Roy Fielding 博士在 2000 年写的一篇关于软件架构风格的论文,此文一出,威震四方!许多知名互
联网公司开始采用这种轻量级 Web 服务,大家习惯将其称为 RESTful Web Services,或简称 REST 服务。
那么 REST 到底是什么呢?REST 本质上是使用 URL 来访问资源的一种方式。众所周知,URL 就是我们平常使用的请求地址了,其中包括两部分:请求方式 与 请求路径,比较常见的请求方式是 GET 与
POST,但在 REST 中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种,正好与 CRUD(增删改查)
四种操作相对应:GET(查)、POST(增)、PUT(改)、DELETE(删),这正是 REST 的奥妙所在!
实际上,REST 是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据。也就是说,服务端将内部资源发布 REST 服务,客
户端通过 URL 来访问这些资源,这不就是 SOA 所提倡的“面向服务”的思想吗?所以,REST 也被人们看做是一种轻量级的 SOA 实现技术,因此在企业级应用与互联网应用中
都得到了广泛使用。
在 Java 的世界里,有一个名为 JAX-RS 的规范,它就是用来实现 REST 服务的,目前已经发展到了 2.0 版本,也就是 JSR-339 规范,如果您想深入研究 REST,请深入阅读
此规范。
JAX-RS 规范目前有以下几种比较流行的实现技术:
Jersey:https://jersey.java.net/
Restlet:http://restlet.com/
RESTEasy:http://resteasy.jboss.org/
CXF:http://cxf.apache.org/
本文以 CXF 为例,我努力用最精炼的文字,让您快速学会如何使用 CXF 开发 REST 服务,此外还会将 Spring 与 CXF 做一个整合,让开发更加高效!
那么还等什么呢?咱们一起出发吧!
第一步:添加 Maven 依赖
4.0.0
demo.ws
rest_cxf
1.0-SNAPSHOT
UTF-8
3.0.0
2.4.1
org.apache.cxf
cxf-rt-frontend-jaxrs
${cxf.version}
org.apache.cxf
cxf-rt-transports-http-jetty
${cxf.version}
com.fasterxml.jackson.jaxrs
jackson-jaxrs-json-provider
${jackson.version}
以上添加了 CXF 关于 REST 的依赖包,并使用了 Jackson 来实现 JSON 数据的转换。
第二步:定义一个 REST 服务接口
package demo.cxf.rest_cxf;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import static javax.ws.rs.core.MediaType.*;
public interface ProductService {
@GET
@Path("/products")
@Produces(APPLICATION_JSON)
List retrieveAllProducts();
@GET
@Path("/product/{id}")
@Produces(APPLICATION_JSON)
Product retrieveProductById(@PathParam("id") long id);
@POST
@Path("/products")
@Consumes(APPLICATION_FORM_URLENCODED)
@Produces(APPLICATION_JSON)
List retrieveProductsByName(@FormParam("name") String name);
@POST
@Path("/product")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
Product createProduct(Product product);
@PUT
@Path("/product/{id}")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
Product updateProductById(@PathParam("id") long id, Map fieldMap);
@DELETE
@Path("/product/{id}")
@Produces(APPLICATION_JSON)
Product deleteProductById(@PathParam("id") long id);
}
以上 ProductService 接口中提供了一系列的方法,在每个方法上都使用了 JAX-RS 提供的注解,主要包括以下三类:
请求方式注解,包括:@GET、@POST、@PUT、@DELETE
请求路径注解,包括:@Path,其中包括一个路径参数
数据格式注解,包括:@Consumes(输入)、@Produces(输出),可使用 MediaType 常量
相关参数注解,包括:@PathParam(路径参数)、@FormParam(表单参数),此外还有 @QueryParam(请求参数)
针对 updateProductById 方法,简单解释一下:
该方法将被 PUT:/product/{id} 请求来调用,请求路径中的 id 参数将映射到 long id 参数上,请求体中的数据将自动转换为 JSON 格式并映射到 Map
注意:由于 Product 类与 ProductService 接口的实现类并不是本文的重点,因此省略了,本文结尾处会给出源码链接。
第三步:使用 CXF 发布 REST 服务
package demo.cxf.rest_cxf;
import java.util.ArrayList;
import java.util.List;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
public class Server {
public static void main(String[] args) {
// 添加 ResourceClass
List> resourceClassList = new ArrayList>();
resourceClassList.add(HelloServiceImpl.class);
resourceClassList.add(ProductServiceImpl.class);
// 添加 ResourceProvider
List resourceProviderList = new ArrayList();
resourceProviderList.add(new SingletonResourceProvider(new HelloServiceImpl()));
resourceProviderList.add(new SingletonResourceProvider(new ProductServiceImpl()));
// 添加 Provider
List
CXF 提供了一个名为 org.apache.cxf.jaxrs.JAXRSServerFactoryBean 的类,专用于发布 REST 服务,只需为该类的实例对象指定四个属性即可:
Address:REST 基础地址
ResourceClasses:一个或一组相关的资源类,即接口对应的实现类(注意:REST 规范允许资源类没有接口)
ResourceProviders:资源类对应的 Provider,此时使用 CXF 提供的 org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider 进行装饰
Providers:REST 服务所需的 Provider,此时使用了 Jackson 提供的 org.codehaus.jackson.jaxrs.JacksonJsonProvider,用于实现 JSON 数据的序列化与反序列化
运行以上 Server 类,将以 standalone 方式发布 REST 服务,下面我们通过客户端来调用以发布的 REST 服务。
第四步:使用 CXF 调用 REST 服务
首先添加如下 Maven 依赖:
org.apache.cxf
cxf-rt-rs-client
${cxf.version}
CXF 提供了三种 REST 客户端,下面将分别进行展示。
第一种:JAX-RS 1.0 时代的客户端
package demo.ws.rest_cxf;
import java.util.ArrayList;
import java.util.List;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
public class JAXRSClient {
public static void main(String[] args) {
String baseAddress = "http://localhost:8080/ws/rest";
List providerList = new ArrayList();
providerList.add(new JacksonJsonProvider());
ProductService productService = JAXRSClientFactory.create(baseAddress, ProductService.class, providerList);
List productList = productService.retrieveAllProducts();
for (Product product : productList) {
System.out.println(product);
}
}
}
本质是使用 CXF 提供的 org.apache.cxf.jaxrs.client.JAXRSClientFactory 工厂类来创建 ProductService 代理对象,通过代理对象调用目标对象上的方法。客户端同样也需要使
用 Provider,此时仍然使用了 Jackson 提供的 org.codehaus.jackson.jaxrs.JacksonJsonProvider。
第二种:JAX-RS 2.0 时代的客户端
package demo.ws.rest_cxf;
import java.util.List;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
public class JAXRS20Client {
public static void main(String[] args) {
String baseAddress = "http://localhost:8080/ws/rest";
JacksonJsonProvider jsonProvider = new JacksonJsonProvider();
List productList = ClientBuilder.newClient()
.register(jsonProvider)
.target(baseAddress)
.path("/products")
.request(MediaType.APPLICATION_JSON)
.get(List.class);
for (Object product : productList) {
System.out.println(product);
}
}
}
在 JAX-RS 2.0 中提供了一个名为 javax.ws.rs.client.ClientBuilder 的工具类,可用于创建客户端并调用 REST 服务,显然这种方式比前一种要先进,因为在代码中不再依赖 CXF API 了。
List productList = ClientBuilder.newClient()
.register(jsonProvider)
.target(baseAddress)
.path("/products")
.request(MediaType.APPLICATION_JSON)
.get(new GenericType>() {});
for (Product product : productList) {
System.out.println(product);
}
第三种:通用的 WebClient 客户端
package demo.ws.rest_cxf;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.jaxrs.client.WebClient;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
public class CXFWebClient {
public static void main(String[] args) {
String baseAddress = "http://localhost:8080/ws/rest";
List providerList = new ArrayList();
providerList.add(new JacksonJsonProvider());
List productList = WebClient.create(baseAddress, providerList)
.path("/products")
.accept(MediaType.APPLICATION_JSON)
.get(List.class);
for (Object product : productList) {
System.out.println(product);
}
}
}
CXF 还提供了一种更为简洁的方式,使用 org.apache.cxf.jaxrs.client.WebClient 来调用 REST 服务,这种方式在代码层面上还是相当简洁的。
如果想返回带有泛型的 List
List productList = WebClient.create(baseAddress, providerList)
.path("/products")
.accept(MediaType.APPLICATION_JSON)
.get(new GenericType>() {});
for (Product product : productList) {
System.out.println(product);
}
第一步:添加 Maven 依赖
4.0.0
demo.ws
rest_spring_cxf
1.0-SNAPSHOT
war
UTF-8
4.0.6.RELEASE
3.0.0
2.4.1
org.springframework
spring-web
${spring.version}
org.apache.cxf
cxf-rt-frontend-jaxrs
${cxf.version}
com.fasterxml.jackson.jaxrs
jackson-jaxrs-json-provider
${jackson.version}
这里仅依赖 Spring Web 模块(无需 MVC 模块),此外就是 CXF 与 Jackson 了。
第二步:配置 web.xml
contextConfigLocation
classpath:spring.xml
org.springframework.web.context.ContextLoaderListener
cxf
org.apache.cxf.transport.servlet.CXFServlet
cxf
/ws/*
使用 Spring 提供的 ContextLoaderListener 去加载 Spring 配置文件 spring.xml;使用 CXF 提供的 CXFServlet 去处理前缀为 /ws/ 的 REST 请求。
package demo.ws.rest_spring_cxf;
import org.springframework.stereotype.Component;
@Component
public class ProductServiceImpl implements ProductService {
...
}
使用 Spring 提供的 @Component 注解,将 ProductServiceImpl 发布为 Spring Bean,交给 Spring IOC 容器管理,无需再进行 Spring XML 配置。
在以上配置中扫描 demo.ws 这个基础包路径,Spring 可访问该包中的所有 Spring Bean,比如,上面使用 @Component 注解发布的 ProductServiceImpl。此外,加载了另一个配置文件 spring-cxf.xml,其中包括了关于 CXF 的相关配置。
使用 CXF 提供的 Spring 命名空间来配置 Service Bean(即上文提到的 Resource Class)与 Provider。注意,这里配置了一个 address 属性为“/rest”,表示 REST 请求的相对路径,与 web.xml 中配置的“/ws/*”结合起来,最终的 REST 请求根路径是“/ws/rest”,在 ProductService 接口方法上 @Path 注解所配置的路径只是一个相对路径。
Demo
Product
Product List
使用一个简单的 HTML 页面来调用 REST 服务,也就是说,前端发送 AJAX 请求来调用后端发布的 REST 服务。这里使用了 jQuery、Bootstrap、Handlebars.js 等技术。
{"id":2,"name":"ipad mini","price":2500},{"id":1,"name":"iphone 5s","price":5000}
那么对应的 JSONP 格式是:
callback([{"id":2,"name":"ipad mini","price":2500},{"id":1,"name":"iphone 5s","price":5000}]);
CXF 已经提供了对 JSONP 的支持,只需要通过简单的配置即可实现。
org.apache.cxf
cxf-rt-rs-extension-providers
${cxf.version}
然后,添加 CXF 配置:
注意:JsonpPreStreamInterceptor 一定要放在 最后,使用 jQuery 发送基于 JSONP 的 AJAX 请求:
$.ajax({
type: 'get',
url: 'http://localhost:8080/ws/rest/products',
dataType: 'jsonp',
jsonp: '_jsonp',
jsonpCallback: 'callback',
success: function(data) {
var template = $("#product_table_template").html();
var render = Handlebars.compile(template);
var html = render({
data: data
});
$('#product').html(html);
}
});
以上代码中有三个选项需要加以说明:
org.apache.cxf
cxf-rt-rs-security-cors
${cxf.version}
然后,添加 CXF 配置:
在 CrossOriginResourceSharingFilter 中配置 allowOrigins 属性,将其设置为客户端的域名,示例中为“http://localhost”,需根据实际情况进行设置。
源码地址:http://git.oschina.net/huangyong/cxf_demo
原文地址:https://my.oschina.net/huangyong/blog/294324