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

第一步、添加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));
	}

	@Override
	public List<Product> retrieveAllProducts() {

		Collections.sort(productList, new Comparator<Product>() {

			@Override
			public int compare(Product p1, Product p2) {
				return (p1.getId() > p2.getId()) ? -1 : 1;
			}
		});
		return productList;
	}

	@Override
	public Product retrieveProductById(long id) {
		Product targetProduct = null;
		for (Product product : productList) {
			if (product.getId() == id) {
				targetProduct = product;
				break;
			}
		}
		return targetProduct;
	}
	
	@Override
	public 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;
	}
	@Override
	public 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;
	}

	@Override
	public Product createProduct(Product product) {
		product.setId(new Date().getTime());
		productList.add(product);
		return product;
	}
	
	@Override
	public 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;
	}

	@Override
	public 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;
	}
	
	@Override
	public 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;
	}
	
	@Override
	public Product updateProduct(long id,String name,int price){
		Product product = retrieveProductById(id);
		if(product != null){
			product.setName(name);
			product.setPrice(price);
		}
		
		return product;
	}

	@Override
	public 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;
	}
	
	@Override
	public 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,可以点击以下链接查看具体原因:
最后,使用 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
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值