CXF系列之JAX-RS:CXF与spring集成发布REST服务

来源:互联网 发布:js控制滚动条位置 编辑:程序博客网 时间:2024/06/05 15:35

第一步、添加maven依赖

<dependencies>        <!-- Spring -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-web</artifactId>            <version>${spring.version}</version>        </dependency>        <!-- CXF -->        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-rt-frontend-jaxrs</artifactId>            <version>${cxf.version}</version>        </dependency>        <!-- Jackson -->        <dependency>            <groupId>com.fasterxml.jackson.jaxrs</groupId>            <artifactId>jackson-jaxrs-json-provider</artifactId>            <version>${jackson.version}</version>        </dependency>    </dependencies>
这里仅依赖 Spring Web 模块(无需 MVC 模块),此外就是 CXF 与 Jackson 了。

第二步、配置web.xml文件

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"         version="3.0">    <!-- Spring -->    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/spring.xml</param-value>    </context-param>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <!-- CXF -->    <servlet>        <servlet-name>cxf</servlet-name>        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>cxf</servlet-name>        <url-pattern>/ws/*</url-pattern>    </servlet-mapping></web-app>
使用 Spring 提供的 ContextLoaderListener 去加载 Spring 配置文件 spring.xml;使用 CXF 提供的 CXFServlet 去处理前缀为 /ws/ 的 REST 请求。

第三步、REST服务接口以及实现类

REST服务接口
package com.test.rest.service.inter;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 javax.ws.rs.QueryParam;import javax.ws.rs.core.MediaType;import com.test.rest.bean.Product;public interface ProductService {@GET@Path("/products")@Produces(MediaType.APPLICATION_JSON)List<Product> retrieveAllProducts();@GET@Path("/product/{id}")@Produces(MediaType.APPLICATION_JSON)Product retrieveProductById(@PathParam("id")long id);/** * 参数查询 * @param name * @return */@GET@Path("/products/name")@Produces(MediaType.APPLICATION_JSON)List<Product> retrieveProductsByName_param(@QueryParam("name")String name);/** * 提交表单查询 * @param name * @return */@POST@Path("/products/form/name")@Consumes(MediaType.APPLICATION_FORM_URLENCODED)@Produces(MediaType.APPLICATION_JSON)List<Product> retrieveProductsByName_form(@FormParam("name")String name);@POST@Path("/product")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)Product createProduct(Product product);@POST@Path("/product/create")@Consumes(MediaType.APPLICATION_FORM_URLENCODED)@Produces(MediaType.APPLICATION_JSON)Product createProduct(@FormParam("name")String name,@FormParam("price")int price);@PUT@Path("/product/{id}")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)Product updateProductById(@PathParam("id")long id,Map<String,Object> fieldMap);@PUT@Path("/product")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)Product updateProduct(Product product);@PUT@Path("/product/update")@Consumes(MediaType.APPLICATION_FORM_URLENCODED)@Produces(MediaType.APPLICATION_JSON)Product updateProduct(@FormParam("id")long id,@FormParam("name")String name,@FormParam("price")int price);@DELETE@Path("/product/{id}")@Produces(MediaType.APPLICATION_JSON)Product deleteProductById(@PathParam("id")long id);@DELETE@Path("/product")@Produces(MediaType.APPLICATION_JSON)Product deleteProductById_param(@QueryParam("id")long id);}

接口所对应的实现类:
package com.test.rest.service.impl;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.Date;import java.util.Iterator;import java.util.List;import java.util.Map;import com.test.rest.bean.Product;import com.test.rest.service.inter.ProductService;public class ProductServiceImpl implements ProductService {private static final List<Product> productList = new ArrayList<Product>();static {productList.add(new Product(1, "iphone63", 5000));productList.add(new Product(2, "ipad mini", 2500));}@Overridepublic List<Product> retrieveAllProducts() {Collections.sort(productList, new Comparator<Product>() {@Overridepublic int compare(Product p1, Product p2) {return (p1.getId() > p2.getId()) ? -1 : 1;}});return productList;}@Overridepublic Product retrieveProductById(long id) {Product targetProduct = null;for (Product product : productList) {if (product.getId() == id) {targetProduct = product;break;}}return targetProduct;}@Overridepublic List<Product> retrieveProductsByName_param(String name){List<Product> targetList = new ArrayList<Product>();for (Product product : productList) {if (product.getName().equals(name)) {targetList.add(product);}}return targetList;}@Overridepublic List<Product> retrieveProductsByName_form(String name) {List<Product> targetList = new ArrayList<Product>();for (Product product : productList) {if (product.getName().equals(name)) {targetList.add(product);}}return targetList;}@Overridepublic Product createProduct(Product product) {product.setId(new Date().getTime());productList.add(product);return product;}@Overridepublic Product createProduct(String name,int price){Product product = new Product();product.setId(new Date().getTime());product.setName(name);product.setPrice(price);productList.add(product);return product;}@Overridepublic Product updateProductById(long id, Map<String, Object> fieldMap) {Product product = retrieveProductById(id);if (product != null) {try {for (Map.Entry<String, Object> fieldEntry : fieldMap.entrySet()) {Field field = Product.class.getDeclaredField(fieldEntry.getKey());field.setAccessible(true);field.set(product, fieldEntry.getValue());}} catch (Exception e) {e.printStackTrace();}}return product;}@Overridepublic Product updateProduct(Product product){if(product != null){Product targetProduct = retrieveProductById(product.getId());if(targetProduct != null){targetProduct.setName(product.getName());targetProduct.setPrice(product.getPrice());}return targetProduct;}return null;}@Overridepublic Product updateProduct(long id,String name,int price){Product product = retrieveProductById(id);if(product != null){product.setName(name);product.setPrice(price);}return product;}@Overridepublic Product deleteProductById(long id) {Product targetProduct = null;Iterator<Product> it = productList.iterator();while (it.hasNext()) {Product product = it.next();if (product.getId() == id) {targetProduct = product;it.remove();break;}}return targetProduct;}@Overridepublic Product deleteProductById_param(long id){Product targetProduct = null;Iterator<Product> it = productList.iterator();while (it.hasNext()) {Product product = it.next();if (product.getId() == id) {targetProduct = product;it.remove();break;}}return targetProduct;}}

第四步、配置spring文件

spring.xml文件配置:
<?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:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.0.xsd"><bean id="productServiceImpl" class="com.test.rest.service.impl.ProductServiceImpl"></bean>    <import resource="spring-cxf.xml"/></beans>
将REST服务接口的实现类配置成SpringBean。
spring-cxf.xml文件配置:
<?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:jaxrs="http://cxf.apache.org/jaxrs"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://cxf.apache.org/jaxrs       http://cxf.apache.org/schemas/jaxrs.xsd">    <jaxrs:server address="/rest">        <jaxrs:serviceBeans>            <ref bean="productServiceImpl"/>        </jaxrs:serviceBeans>        <jaxrs:providers>            <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>        </jaxrs:providers>    </jaxrs:server></beans>
使用 CXF 提供的 Spring 命名空间来配置 Service Bean(即上文提到的 Resource Class)与 Provider。注意,这里配置了一个 address 属性为“/rest”,表示 REST 请求的相对路径,与 web.xml 中配置的“/ws/*”结合起来,最终的 REST 请求根路径是“/ws/rest”,在 ProductService 接口方法上 @Path 注解所配置的路径只是一个相对路径。

第五步、调用REST服务

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <title>Demo</title>    <link href="http://localhost:8080/springCxfREST/css/bootstrap.min.css" rel="stylesheet">    <link href="http://localhost:8080/springCxfREST/css/colorbox.css" rel="stylesheet" type="text/css" /></head><body><br><br><div class="container"><form role="form" id="form_name"><div class="form-group"><label for="input_name">根据名称检索</label><input type="text" class="form-control" id="input_name" placeholder="名称" name="name"/></div><button id="submit_name" type="button" class="btn btn-default">检索</button></form><form role="form" id="form_create"><div class="form-group"><label for="input_create_name">名称</label><input type="text" class="form-control" id="input_create_name" placeholder="名称" name="name"/></div><div class="form-group"><label for="input_create_price">价格</label><input type="text" class="form-control" id="input_create_price" placeholder="价格" name="price"/></div><button id="submit_create" type="button" class="btn btn-default">添加</button></form><div class="panel panel-default"><button id="all" type="button" class="btn btn-default">RetrieveAll</button><button id="byID" type="button" class="btn btn-default">RetrieveByID</button><button id="name" type="button" class="btn btn-default">RetrieveByName</button></div>    <div class="page-header">        <h1>Product</h1>    </div>    <div class="panel panel-default">        <div class="panel-heading">Product List</div>        <div class="panel-body">            <div id="product"></div>        </div>    </div></div><script src="http://localhost:8080/springCxfREST/script/jquery.js" type="text/javascript"></script><script type="text/javascript" src="http://localhost:8080/springCxfREST/script/bootstrap.min.js"></script><script type="text/javascript" src="http://localhost:8080/springCxfREST/script/handlebars-v4.0.5.js"></script><script type="text/x-handlebars-template" id="product_table_template">    {{#if data}}        <table class="table table-hover" id="product_table">            <thead>                <tr>                    <th>ID</th>                    <th>Product Name</th>                    <th>Price</th>                </tr>            </thead>            <tbody>                {{#data}}                    <tr data-id="{{id}}" data-name="{{name}}">                        <td>{{id}}</td>                        <td>{{name}}</td>                        <td>{{price}}</td>                    </tr>                {{/data}}            </tbody>        </table>    {{else}}        <div class="alert alert-warning">Can not find any data!</div>    {{/if}}</script><script>    $(function() {    //检索所有    $("#all").click(function(){    $("#product").html("");    $.ajax({                type: 'get',                url: 'http://localhost:8080/springCxfREST/ws/rest/products',                dataType: 'json',                success: function(data) {                    var template = $("#product_table_template").html();                    var render = Handlebars.compile(template);                    var html = render({                        data: data                    });                    $('#product').html(html);                }            });    });        //根据ID检索    $("#byID").click(function(){    $("#product").html("");    $.ajax({                type: 'get',                url: 'http://localhost:8080/springCxfREST/ws/rest/product/1',                dataType: 'json',                success: function(data) {                    var template = $("#product_table_template").html();                    var render = Handlebars.compile(template);                    var html = render({                        data: data                    });                    $('#product').html(html);                }            });    });        //根据名称检索    $("#name").click(function(){    $("#product").html("");    $.ajax({                type: 'get',                url: 'http://localhost:8080/springCxfREST/ws/rest/products/name?name=ipad mini',                dataType: 'json',                success: function(data) {                    var template = $("#product_table_template").html();                    var render = Handlebars.compile(template);                    var html = render({                        data: data                    });                    $('#product').html(html);                }            });    });        //提交表单,根据名称检索    $("#submit_name").click(function(){    $("#product").html("");    $.ajax({                type: 'post',                url: 'http://localhost:8080/springCxfREST/ws/rest/products/form/name?'+$("#form_name").serialize(),                dataType: 'json',                success: function(data) {                    var template = $("#product_table_template").html();                    var render = Handlebars.compile(template);                    var html = render({                        data: data                    });                    $('#product').html(html);                }            });    });        /* 提交表单,添加数据 */    $("#submit_create").click(function(){    $("#product").html("");    $.ajax({                type: 'post',                  url: 'http://localhost:8080/springCxfREST/ws/rest/product/create?'+$("#form_create").serialize(),                dataType: 'json',                success: function(data) {                    var template = $("#product_table_template").html();                    var render = Handlebars.compile(template);                    var html = render({                        data: data                    });                    $('#product').html(html);                }            });    });        });       function goColorBox(){        $("#goOne").colorbox({ opacity:0.65,width: 500, height:500, close: "关闭", overlayClose: true});  }</script></body></html>
使用一个简单的 HTML 页面来调用 REST 服务,也就是说,前端发送 AJAX 请求来调用后端发布的 REST 服务。这里使用了 jQuery、Bootstrap、Handlebars.js 等技术。

第六步、发布并访问

将项目部署到tomcat服务器中,启动tomcat服务器后,访问:http://localhost:8080/springCxfREST(项目名称)/index.jsp。

第七步、CXF对AJAX跨域的支持

AJAX跨域问题解决通常有两种方案,分别是JSONP和CORS,CXF分别对其都有很好的支持。
1、JSONP
添加JSONP依赖
<dependency>    <groupId>org.apache.cxf</groupId>    <artifactId>cxf-rt-rs-extension-providers</artifactId>    <version>${cxf.version}</version></dependency>
修改spring中CXF的配置如下:
<jaxrs:server address="/rest">    <jaxrs:serviceBeans>        <ref bean="productServiceImpl"/>    </jaxrs:serviceBeans>    <jaxrs:providers>        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>        <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPreStreamInterceptor"/>    </jaxrs:providers>    <jaxrs:inInterceptors>        <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpInInterceptor"/>    </jaxrs:inInterceptors>    <jaxrs:outInterceptors>        <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPostStreamInterceptor"/>    </jaxrs:outInterceptors></jaxrs:server>
注意:JsonpPreStreamInterceptor 一定要放在 <jaxrs:providers> 中,而不是 <jaxrs:inInterceptors> 中,这也许是 CXF 的一个 Bug,可以点击以下链接查看具体原因:
http://cxf.547215.n5.nabble.com/JSONP-is-not-works-td5739858.html
最后,使用 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);    }});
以上代码中有三个选项需要加以说明:
  1. dataType:必须为“jsonp”,表示返回的数据类型为 JSONP 格式
  2. jsonp:表示 URL 中 JSONP 回调函数的参数名,CXF 默认接收的参数名是“_jsonp”,也可以在 JsonpInInterceptor 中配置
  3. jsonpCallback:表示回调函数的名称,若未指定,则由 jQuery 自动生成
2、CORS
添加CORS依赖
<dependency>    <groupId>org.apache.cxf</groupId>    <artifactId>cxf-rt-rs-security-cors</artifactId>    <version>${cxf.version}</version></dependency>
修改CXF在spring配置文件中的配置如下:
<jaxrs:server address="/rest">    <jaxrs:serviceBeans>        <ref bean="productServiceImpl"/>    </jaxrs:serviceBeans>    <jaxrs:providers>        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>        <bean class="org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter">            <property name="allowOrigins" value="http://localhost"/>        </bean>    </jaxrs:providers></jaxrs:server>
在 CrossOriginResourceSharingFilter 中配置 allowOrigins 属性,将其设置为客户端的域名,示例中为“http://localhost”,需根据实际情况进行设置。

最后,使用 jQuery 发送 AJAX 请求:
就像在相同域名下访问一样,无需做任何配置。

注意:在 IE8 中使用 jQuery 发送 AJAX 请求时,需要配置 $.support.cors = true,才能开启 CORS 特性。








0 0
原创粉丝点击