文档章节

使用CXF开发RestFul WebService问题解决方案

xdev
 xdev
发布于 2013/05/18 18:16
字数 2343
阅读 11712
收藏 20

      最近在项目中,因为几个系统自己需要数据交换,所以采用进来都比较流行的RestFul风格WebService,实现框架采用apache的cxf,apache的东西一直以来都是比较的好用,回话少说,进入正题。

      首先,提出两个问题,已经解决方案,后面跟上代码。

      1、cxf中如何实现java中泛型的数据序列化和反序列化(通常使用json、xml格式,cxf默认不支持泛型)

      2、cxf后台异常,在前台如何反映

      问题1答案比较简单,cxf中的数据序列化是可以替换掉使用你实现MessageBodyReader<Object>和MessageBodyWriter<Object>接口就可以啦,针对xml,cxf采用stax2、jaxb、xmlschema、Woodstox库,针对json默认使用jettison实现的几乎都是codehaus作品。知道cxf序列化和反序列化方式就比较容易解决问题啦。默认情况下cxf的jettison对泛型序列化存在问题,因为时间紧(一天就要做好restful webservice部署),没有具体去研究实现问题,我只是在之前使用过jackson,去处理json问题,而且cxf拥有jackson的MessageBodyReader和MessageBodyWriter实现类,我只要导入包并告诉cxf使用我指定的json provider就可以了,所以在客户端和服务器端双方都指定json privoder,jackson 库对json序列化实现非常的到位,异常的强大。我们都知道,只要java源码中指定的泛型类我没都可以反射出来,如果使用泛型站位符,就没法反射,因为java中的擦除法的原因(比如List<String>、List<T>,前面是清楚的指定泛型参数类型,后面一种是在运行时指定),我这里讨论的也是指定泛型参数类型情况下,jackson在这种情况下已经支持,所以不需要自己实现MessageBodyReader和MessageBodyWriter接口。如果是使用xml方式,除自己实现接口外,有更简单的方法,那就是在你的泛型类上面指定@XmlSeeAlso({某某类1.class,某某类2.class...})

      问题2同样的比较简单,因为基于http的restful实现时,服务器返回数据的时候都会告诉客户端一个响应状态吗,就是我们常看到的200、404、500等,cxf框架的rs webservice客户端实现是通过判断状态大于等于300时,抛出异常webapplicationexception,所以如果服务器端有异常时,通过设置状态就可以实现,并返回Response(通过实现ExceptionMapper<E extends Throwable>接口,并加入到provider实现),如果客户端需要错误消息(这里不得不说jcp设计的jsr311比较的细腻),可以在Response中设置,客户端catch掉webapplicationexception异常,并可以读取错误消息。cxf到这里还没有完,cxf提供一个ResponseExceptionMapper接口,客户端实现这个接口并加入到provider中,客户端在调用的时候就不用去处理cxf的异常webapplicationexception,而是你自己接口的异常,因为客户端在调用webservice时,cxf创建调用接口的代理,代理在接收到300错误时,他知道服务器是返回webapplicationexception异常,他就是用你的ResponseExceptionMapper处理异常,因为这个接口中唯一方法fromResponse(Response r)返回的是一个异常。也就是说,实现这个类方法时,可以读取webapplicationexception中的Response所包含的消息,并要求返回一次异常对象。这样就达到客户端不用关心webapplicationexception异常而是关系自己接口上面声明的异常。

代码:

@XmlRootElement(name="Customer")
public class Customer {
	private String id;  
    private String name;  
    private Date birthday;  
    public String getId() {  
        return id;  
    }  
    public void setId(String id) {  
        this.id = id;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Date getBirthday() {  
        return birthday;  
    }  
    public void setBirthday(Date birthday) {  
        this.birthday = birthday;  
    }  
}
@XmlRootElement(name="Me")
public class Me implements Serializable {

	private String name;

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

}
@XmlRootElement(name = "Page")
@XmlSeeAlso({Customer.class,Me.class})
public class Page<T> implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 5859907455479273251L;

	public static int DEFAULT_PAGE_SIZE = 10;

	private int pageSize = DEFAULT_PAGE_SIZE; // 每页的记录数

	private long start; // 当前页第一条数据在List中的位置,从0开始

	private List<T> data; // 当前页中存放的记录,类型一般为List

	private long totalCount; // 总记录数

	/**
	 * 构造方法,只构造空页.
	 */
	public Page() {
		this(0, 0, DEFAULT_PAGE_SIZE, new ArrayList<T>());
	}

	public Page(int pageSize) {
		this(0, 0, pageSize, new ArrayList<T>());
	}

	/**
	 * 默认构造方法.
	 * 
	 * @param start
	 *            本页数据在数据库中的起始位置
	 * @param totalSize
	 *            数据库中总记录条数
	 * @param pageSize
	 *            本页容量
	 * @param data
	 *            本页包含的数据
	 */
	public Page(long start, long totalSize, int pageSize, List<T> data) {
		this.pageSize = pageSize;
		this.start = start;
		this.totalCount = totalSize;
		this.data = data;
	}

	/**
	 * 取总记录数.
	 */
	public long getTotalCount() {
		return this.totalCount;
	}

	/**
	 * 取总页数.
	 */
	public long getTotalPageCount() {
		if (totalCount % pageSize == 0)
			return totalCount / pageSize;
		else
			return totalCount / pageSize + 1;
	}

	/**
	 * 取每页数据容量.
	 */
	public int getPageSize() {
		return pageSize;
	}

	/**
	 * 取当前页中的记录.
	 */
	public List<T> getResult() {
		return data;
	}

	/**
	 * 取该页当前页码,页码从1开始.
	 */
	public long getCurrentPageNo() {
		return start / pageSize + 1;
	}

	/**
	 * 该页是否有下一页.
	 */
	public boolean hasNextPage() {
		return this.getCurrentPageNo() < this.getTotalPageCount();
	}

	/**
	 * 该页是否有上一页.
	 */
	public boolean hasPreviousPage() {
		return this.getCurrentPageNo() > 1;
	}

	/**
	 * 获取任一页第一条数据在数据集的位置,每页条数使用默认值.
	 * 
	 * @see #getStartOfPage(int,int)
	 */
	protected static int getStartOfPage(int pageNo) {
		return getStartOfPage(pageNo, DEFAULT_PAGE_SIZE);
	}

	/**
	 * 获取任一页第一条数据在数据集的位置.
	 * 
	 * @param pageNo
	 *            从1开始的页号
	 * @param pageSize
	 *            每页记录条数
	 * @return 该页第一条数据
	 */
	public static int getStartOfPage(int pageNo, int pageSize) {
		return (pageNo - 1) * pageSize;
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/**
	 * @return the start
	 */
	public long getStart() {
		return start;
	}

	/**
	 * @param start the start to set
	 */
	public void setStart(long start) {
		this.start = start;
	}

	/**
	 * @return the data
	 */
	public List<T> getData() {
		return data;
	}

	/**
	 * @param data the data to set
	 */
	public void setData(List<T> data) {
		this.data = data;
	}

	/**
	 * @param pageSize the pageSize to set
	 */
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	/**
	 * @param totalCount the totalCount to set
	 */
	public void setTotalCount(long totalCount) {
		this.totalCount = totalCount;
	}
	
}
 

public class ServiceException extends RuntimeException {

 private static final long serialVersionUID = 7607640803750403555L;  public ServiceException() {         super();     }

    public ServiceException(String message) {         super(message);     }

    public ServiceException(String message, Throwable cause) {         super(message, cause);     }

    public ServiceException(Throwable cause) {         super(cause);     } }

@Path(value = "/customer")   
@Produces({"application/xml","application/json"})  
public interface CustomerService {  
	
    @GET  
    @Path(value = "/{id}/info")  
    Customer findCustomerById(@PathParam("id")String id);  
      
    @GET  
    @Path(value = "/search")  
    Customer findCustomerByName(@QueryParam("name")String name);  
    
    @POST  
    @Path(value = "/all")  
    List<Customer> findAllCustomer();
    
    @POST  
    @Path(value = "/page")  
    Page<Customer> findPageCustomer() throws ServiceException;
    
    @POST  
    @Path(value = "/pageMe") 
    Page<Me> findPage();
}
public class CustomerServiceImpl  implements CustomerService {  
  
    public Customer findCustomerById(String id) {  
        Customer customer = new Customer();  
        customer.setId(id);  
        customer.setName(id);  
        customer.setBirthday(Calendar.getInstance().getTime());  
        return customer;  
    }  
      
    public Customer findCustomerByName(String name) {  
        Customer customer = new Customer();  
        customer.setId(name);  
        customer.setName(name);  
        customer.setBirthday(Calendar.getInstance().getTime());  
        return customer;  
    }

	/** (non-Javadoc)
	 * @see edu.xdev.restful.CustomerService#findAllCustomer()
	 */
	@Override
	public  List<Customer> findAllCustomer() {
		List<Customer> tar = new LinkedList<Customer>();
		Customer customer = new Customer();  
		customer.setId("e24234");  
        customer.setName("張三");  
        customer.setBirthday(Calendar.getInstance().getTime()); 
        tar.add(customer);
        customer = new Customer();  
        customer.setId("324324");  
        customer.setName("李四");  
        customer.setBirthday(Calendar.getInstance().getTime()); 
        tar.add(customer);
		return tar;
	}

	/** (non-Javadoc)
	 * @see edu.xdev.restful.CustomerService#findPageCustomer()
	 */
	public Page<Customer> findPageCustomer() throws ServiceException {
		
		List<Customer> tar = new LinkedList<Customer>();
		Customer customer = new Customer();  
        customer.setId("e24234");  
        customer.setName("張三");  
        customer.setBirthday(Calendar.getInstance().getTime()); 
        tar.add(customer);
        customer = new Customer();  
        customer.setId("324324");  
        customer.setName("李四");  
        customer.setBirthday(Calendar.getInstance().getTime()); 
        tar.add(customer);
        Page<Customer> page = new Page<Customer>(1, 2, 1, tar);
        if(1==1){
        	throw new ServiceException("abcd");
        }
		return page;
	}

	/** (non-Javadoc)
	 * @see edu.xdev.restful.CustomerService#findPage()
	 */
	public Page<Me> findPage() {
		List<Me> tar = new LinkedList<Me>();
		Me m = new Me();
		m.setName("中文");
		tar.add(m);
		m = new Me();
		m.setName("English");
		tar.add(m);
		
		Page<Me> page = new Page<Me>(1, 2, 1, tar);
		return page;
	}  
}
@Provider
public class InvokeFaultExceptionMapper implements ExceptionMapper<ServiceException> {

	/** (non-Javadoc)
	 * @see javax.ws.rs.ext.ExceptionMapper#toResponse(java.lang.Throwable)
	 */
	@Override
	public Response toResponse(ServiceException ex) {
		ResponseBuilder rb = Response.status(Response.Status.INTERNAL_SERVER_ERROR);  
		rb.type("application/json;charset=UTF-8");  
		rb.entity(ex);  
		rb.language(Locale.SIMPLIFIED_CHINESE);  
		Response r = rb.build();  
		return r;

	}

}
public class ServiceExceptionMapper implements ResponseExceptionMapper<ServiceException>{

	/** (non-Javadoc)
	 * @see org.apache.cxf.jaxrs.client.ResponseExceptionMapper#fromResponse(javax.ws.rs.core.Response)
	 */
	@Override
	public ServiceException fromResponse(Response r) {
		Object obj = r.getEntity();
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		try {
			return mapper.readValue(obj.toString(), ServiceException.class);
		} catch (JsonParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new ServiceException(obj.toString());
	}
	
}
public class Server {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
        
        JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean();  
        factoryBean.getInInterceptors().add(new LoggingInInterceptor());  
        factoryBean.getOutInterceptors().add(new LoggingOutInterceptor());  
        factoryBean.setResourceClasses(CustomerServiceImpl.class);  
        List<Object> list = new LinkedList<Object>();
        list.add(new org.codehaus.jackson.jaxrs.JacksonJsonProvider());
        list.add(new InvokeFaultExceptionMapper());
        factoryBean.setProviders(list);
        
        factoryBean.setAddress("http://localhost:9000/ws/jaxrs");  
        factoryBean.create(); 
	}

}
public class ClientTest {

	private static List<Object> getJacksonJsonProvider(){
		List<Object> providers = new LinkedList<Object>();
		providers.add(new ServiceExceptionMapper());
		JacksonJsonProvider provider = new JacksonJsonProvider();
		provider.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		providers.add(provider);
		return providers;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		try{
			Page<Me> pages = getServiceInstance(CustomerService.class).findPage();
			for(Me u:pages.getResult()){
				System.out.println(u.getName());
			}
			
			Page<Customer> page = getServiceInstance(CustomerService.class).findPageCustomer();
			for(Customer u:page.getResult()){
				System.out.println(u.getName());
			}
		}catch(WebApplicationException e){
			if(e instanceof WebApplicationException){
				WebApplicationException we = (WebApplicationException)e;
				System.out.println(we.getMessage());
				//System.out.println(we.getCause().getMessage());
			}
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	private static Map<Class<?>, Object> repos = new HashMap<Class<?>, Object>();
	
	private static String baseUrl;
	
	static {
		baseUrl = "http://localhost:9000/ws/jaxrs";
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getServiceInstance(Class<T> clazz){
		T t = (T) repos.get(clazz);
		if(t==null){
			synchronized (clazz) {
				t = (T) repos.get(clazz);
				if(t==null){
					
					t = JAXRSClientFactory.create(baseUrl, clazz, getJacksonJsonProvider());
					Client client = WebClient.client(t);
					WebClient.getConfig(client).getInInterceptors().add(new LoggingInInterceptor());
					WebClient.getConfig(client).getInFaultInterceptors().add(new LoggingInInterceptor());
					WebClient.getConfig(client).getOutFaultInterceptors().add(new LoggingOutInterceptor()); 
					WebClient.getConfig(client).getOutInterceptors().add(new LoggingOutInterceptor()); 
					client.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).acceptEncoding("UTF-8");
					repos.put(clazz, t);
				}
			}
		}
		return t;
	}
}

 

总结:

问题1:针对json格式使用jackson替换jettison库。针对xml格式,只要在指定泛型参数类上面同同过@XmlSeeAlso注解指定泛型参数类class即可。

问题2:通过ExceptionMapper接口和webapplicationexception异常实现,如果想更进一步可以加上ResponseExceptionMapper完成更舒坦的WebService设计

这里特别指出一下。MessageBodyReader、MessageBodyWriter、ExceptionMapper、webapplicationexception、XmlSeeAlso都是java规范中的api,ResponseExceptionMapper为cxf中的api。如果大家选择maven依赖管理cxf,注意cxf默认的jax-rs api依赖,其中2.7.4中默认依赖是javax.ws.rs-api-2.0-m10.jar,cxf2.5.10默认依赖是jsr311-api.1.1.1.jar。也就是说,要默认按照它依赖的jar,不要以为jax-rs 2.0的api还是m阶段,就降低api使用低版本正是版本jsr311-api.1.1.1.jar,这里在cxf中是有问题的。cxf官网上面明明说cxf目前实JAX-RS 1.1 and JAX-RS 1.0 (JSR-311),可实际已经开始支持jax-rs2版本,而jax-rs2 还没正式发布,所以cxf对jax-rs2实现自然就有问题。我开发时,被这里害惨啦,最终选2.5.10版本

© 著作权归作者所有

上一篇: Java学习路线
xdev
粉丝 35
博文 25
码字总数 6706
作品 1
广州
架构师
私信 提问
加载中

评论(3)

wpb王鹏博
wpb王鹏博
wpb6112@163.com 邮箱
wpb王鹏博
wpb王鹏博
求代码和jar
老乔928
好,学习了!
spring+restful service

Spring整合CXF,发布RSETful 风格WebService 这篇文章是承接之前CXF整合Spring的这个项目示例的延伸,所以有很大一部分都是一样的。关于发布CXF WebServer和Spring整合CXF这里就不再多加赘述...

游宵
2014/04/25
1K
1
WebServiceCXF与Restful架构风格简介

什么是WebService 简单的说,WebService是一种多系统之间通信的技术. WebService是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML标准来描述、发布、发现、...

稻香今天更新文章了吗
2018/07/01
0
0
使用cxf发布webservice总结

一、概念 1、什么是webservice Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML标准来描述、发布、发现、协调和配置这些应用程序,用于开发分...

漂泊者及其影子
2014/08/21
12.4K
0
Apache CXF 和 Spring 开发 Web Service 1

正文 为什么使用CXF 本文段摘录自 http://www.ibm.com/developerworks/cn/webservices/ws-pojo-springcxf/index.html Apache CXF 是一个开放源代码框架,提供了用于方便地构建和开发 Web 服务...

洋葱94
2015/12/04
268
0
CXF WebService 可以自定义返回类型为csv吗?

工作中用户提了这样的要求,通过CXF WebService + spring3 来实现API的功能。 用户要求我们提供SOAP和Restful两种方式的API,Rest方式需要支持json/xml/csv这3种格式的返回值。 之前使用过C...

Yue-Su
2014/04/30
335
1

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
14
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
15
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部