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

 最近在项目中,因为几个系统自己需要数据交换,所以采用进来都比较流行的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异常而是关系自己接口上面声明的异常。

代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
@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;
     }
     
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  
<p>
public class ServiceException extends RuntimeException {
</p>
 
<p>
  private static final long serialVersionUID = 7607640803750403555L;
  public ServiceException() {
         super ();
     }
</p>
 
<p>
     public ServiceException(String message) {
         super (message);
     }
</p>
 
<p>
     public ServiceException(String message, Throwable cause) {
         super (message, cause);
     }
</p>
 
<p>
     public ServiceException(Throwable cause) {
         super (cause);
     }
}
</p>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<span></span> @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;
    
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@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版本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值