分享

JAX-RS:CXF的实现与集成

 KILLKISS 2014-11-12

依赖
本文基于cxf2.7.0,需要在前面的例子中加入对jaxrs的依赖:

Xml代码  收藏代码
  1. <dependency>    
  2.     <groupId>org.apache.cxf</groupId>    
  3.     <artifactId>cxf-rt-frontend-jaxrs</artifactId>    
  4.     <version>2.7.0</version>    
  5. </dependency>    

 由于2.7.0是采用jax-rs2.0版本,即JSR339,默认会引入:

Java代码  收藏代码
  1. <dependency>    
  2.     <groupId>javax.ws.rs</groupId>    
  3.     <artifactId>javax.ws.rs-api</artifactId>    
  4.     <version>2.0-m10</version>    
  5. </dependency>    

 当然对于之前的版本,基于jsr311。需要在pom中手动加入:1.0或1.1版本

 

Java代码  收藏代码
  1. <dependency>     
  2.     <groupId>javax.ws.rs</groupId>     
  3.     <artifactId>jsr311-api</artifactId>     
  4.     <version>1.1.1</version>     
  5. </dependency>    

 整合
这里主要考虑一下几种整合方式,更多见这里

 

  • 编程式发布server

在CXF中与JAX-WS一样,提供了JAXRSServerFactoryBean作为工厂服务类来编程式发布资源类

Java代码  收藏代码
  1. JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();    
  2. //Bind one or more resources    
  3. sf.setResourceClasses(CustomerService.class, AnotherService.class);    
  4. // JAX-RS默认是每个请求会实例,这样修改为单例    
  5. sf.setResourceProvider(CustomerService.class, new SingletonResourceProvider(new CustomerService()));    
  6. sf.setAddress("http://localhost:9000/");    
  7. BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class);    
  8. JAXRSBindingFactory factory = new JAXRSBindingFactory();    
  9. factory.setBus(sf.getBus());    
  10. manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);    
  11. sf.create();    
  •  spring

当然这里说的与spring整合少不了与web容器的整合,首先需要在web.xml中添加CXF的servlet监听请求路径:

Xml代码  收藏代码
  1. <servlet>    
  2.     <servlet-name>CXFServlet</servlet-name>    
  3.     <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>    
  4.     <load-on-startup>5</load-on-startup>    
  5. </servlet>    
  6. <servlet-mapping>    
  7.     <servlet-name>CXFServlet</servlet-name>    
  8.     <url-pattern>/*</url-pattern>    
  9. </servlet-mapping>    

 下面介绍两种spring的配置,这也是通用的

  • jaxrs命名空间

这是一个最简单的配置:

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www./schema/beans"    
  3.     xmlns:xsi="http://www./2001/XMLSchema-instance"    
  4.     xmlns:jaxrs="http://cxf./jaxrs"    
  5.     xsi:schemaLocation="    
  6.     http://www./schema/beans    
  7.     http://www./schema/beans/spring-beans.xsd    
  8.     http://cxf./jaxrs    
  9.     http://cxf./schemas/jaxrs.xsd">    
  10.     
  11.     <import resource="classpath:META-INF/cxf/cxf.xml" />    
  12.     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />    
  13.     
  14.     <jaxrs:server id="customerService" address="/jaxrs">    
  15.         <jaxrs:serviceBeans>    
  16.             <ref bean="restPathService1"/>    
  17.         </jaxrs:serviceBeans>    
  18.     
  19.     </jaxrs:server>    
  20.     
  21.     <bean id="restPathService1" class="org.ws.server.cxf.chap3.cxf.server.CustomerService"/>    
  22. </beans>    

 当然在jaxrs:server中可以加入其他节点,具体可参考http://cxf./schemas/jaxrs.xsd

 

  • spring bean

也可以使用普通的bean配置,只是需要作为JAXRSServerFactoryBean实例的属性:

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www./schema/beans"    
  3.     xmlns:xsi="http://www./2001/XMLSchema-instance"    
  4.     xsi:schemaLocation="    
  5.     http://www./schema/beans    
  6.     http://www./schema/beans/spring-beans.xsd">    
  7.     
  8.     <import resource="classpath:META-INF/cxf/cxf.xml" />    
  9.     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />    
  10.     
  11.     <bean class="org.apache.cxf.jaxrs.JAXRSServerFactoryBean" init-method="create">    
  12.         <property name="address" value="/jaxrs"/>    
  13.         <property:serviceBeans>    
  14.             <ref bean="restPathService1" />    
  15.         </property:serviceBeans>    
  16.     </bean>    
  17.     
  18.     <bean id="restPathService2" class="org.ws.server.cxf.chap3.cxf.server.AnotherService"/>    
  19. </beans>    

 
数据绑定(Data Bindings)
CXF对JAX-RS的数据绑定与之前的一样,需要选择相应的绑定类型,如需要加入JAXB对XML的处理:

Xml代码  收藏代码
  1. <dependency>    
  2.         <groupId>org.apache.cxf</groupId>    
  3.         <artifactId>cxf-rt-databinding-jaxb</artifactId>    
  4.         <version>${cxf.version}</version>    
  5. </dependency>    

 这也是CXF对JAX-RS的默认编码类型,因此我们在采用XML作为传输时只需要做简单的处理:
资源类中的一个方法:

Java代码  收藏代码
  1. @GET    
  2. @Path("/meth1")    
  3. @Produces({ MediaType.APPLICATION_XML })    
  4. public User meth1() {    
  5.     return new User(1, "robin", "123");    
  6. }    

 其中User作为返回需要以XML的格式传输,最简单的处理只需要在: 

Java代码  收藏代码
  1. @XmlRootElement(name = "user")    
  2. public class User {    
  3.     
  4.     private Integer id;    
  5.     private String  username;    
  6.     private String  password;     

 当然更多操作需要了解

 

再介绍对JSON格式的处理,由于CXF默认支持是依赖Jettison通常我们更喜欢Jackson。所以,首先需要加入其依赖:

Java代码  收藏代码
  1. <dependency>    
  2.   <groupId>org.codehaus.jackson</groupId>    
  3.   <artifactId>jackson-jaxrs</artifactId>    
  4.   <version>1.9.0</version>    
  5. </dependency>    

 需要更换默认的JSON providers为Jackson:

Xml代码  收藏代码
  1. <jaxrs:providers>    
  2.    <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>    
  3. </jaxrs:providers>    

 

Xml代码  收藏代码
  1. <jaxrs:providers>    
  2.    <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>    
  3. </jaxrs:providers>  

 一个完整的例子:
资源类的方法声明:

Java代码  收藏代码
  1. @GET    
  2. @Path("/meth2")    
  3. @Produces({ MediaType.APPLICATION_JSON })    
  4. public User meth2() {    
  5.     return new User(1, "robin", "123");    
  6. }    

 spring配置文件:

Xml代码  收藏代码
  1. <jaxrs:server id="customerService" address="/jaxrs">  
  2.     <jaxrs:serviceBeans>  
  3.         <ref bean="restPathService3"/>  
  4.     </jaxrs:serviceBeans>  
  5.   
  6.     <jaxrs:providers>  
  7.         <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>  
  8.     </jaxrs:providers>  
  9. </jaxrs:server>  
  10.   
  11. <bean id="restPathService3" class="org.ws.server.cxf.chap3.cxf.data.RESTDataBindService"/>  

  更多信息见这里


URI路径选择算法
当一个请求URI匹配了多个resource类,进行下面的算法:
1、优先匹配resource类@Path中更多的文字

Java代码  收藏代码
  1. @Path("/bar/{id}")    
  2. public class Test1 {}    
  3. @Path("/bar/{id}/baz")    
  4. public class Test2 {}    
  5. @Path("/foo")    
  6. public class Test3 {}    
  7. @Path("/foo/")    
  8. public class Test4 {}    

 对请求URI: /bar/1/baz 同时匹配上了Test1和Test2,但Test2中有9个文字而Test1只有5个,同样对请求/foo/将匹配Test4

 

2、优先匹配resource类@Path中更多组类

Java代码  收藏代码
  1. @Path("/bar/{id}/")    
  2. public class Test1 {}    
  3. @Path("/bar/{id}/{id2}")    
  4. public class Test2 {}    

 对请求URI:/bar/1/2同时匹配Test1和Test2,但Test2中有3个分组将优先匹配

 

3、优先匹配resource类@Path中捕获更多的正则组

Java代码  收藏代码
  1. @Path("/bar/{id:.+}/baz/{id2}")    
  2. public class Test1 {}    
  3. @Path("/bar/{id}/bar/{id2}")    
  4. public class Test2 {}    

 对请求URI:/bar/1/baz/2同时匹配Test1和Test2,且文字长度和组都一样,但Test1中匹配为1而Test2匹配中0

 

在多个resource方法中选择
根据上面的规则,我们已经选择了resource类。接下来对类中方法的选择也遵循这个原则,其中只有一点额外需要注意:

如果同时包含sub-resource优先选择非sub-resource

Java代码  收藏代码
  1. @Path("/rest")    
  2. public class Test1 {    
  3.     
  4.  @Path("/bar")    
  5.  @GET    
  6.  public Order getOrder() {...}    
  7.     
  8.  @Path("/bar")    
  9.  public Order getOrderFromSubresource() {...}    
  10. }    
  11.     
  12. public class Order {    
  13.     
  14.  @Path("/")    
  15.  @GET    
  16.  public Order getOrder() { return this; }    
  17.     
  18. }    

 对请求URI: /rest/bar将匹配方法getOrder()因为另外一个是sub-resource

 

 

方法与MediaType的选择
考虑这样的情况如果方法都匹配,但是MediaType不一样:

Java代码  收藏代码
  1. @Path("/rest")    
  2. public class Test1 {    
  3.     
  4.      @Path("/bar")    
  5.      @POST     
  6.      @Consumes("application/json")    
  7.      @Produces("application/json")    
  8.      public Order addOrderJSON(OrderDetails details) {...}    
  9.     
  10.      @Path("/bar")    
  11.      @POST    
  12.      @Consumes("application/xml")    
  13.      @Produces("application/xml")    
  14.      public Order getOrderXML(OrderDetails details) {...}    
  15.     
  16. }    

 对请求URI: /rest/bar以上两个都匹配,当然如果请求的Content-Type和Accept都有设置相应的类型,那么就好判断,如application/json指向addOrderJSON,如果是application/xml则指向方法getOrderXML当然,如果客户端并没明显指定而是直接在浏览器请求,可能会选择的是getOrderXML因为默认的可能是:

Xml代码  收藏代码
  1. Content-Type:   text/html    
  2. Accept:         text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8    

 当然其实可以更好的方式处理这种情况:

Java代码  收藏代码
  1. @Path("/")    
  2. public class Test1 {    
  3.     
  4.  @Path("/bar")    
  5.  @POST     
  6.  @Consumes({"application/json", "application/xml"})    
  7.  @Produces({"application/json", "application/xml"})    
  8.  public Order addOrder(OrderDetails details) {...}    
  9.     
  10. }   

 
 自定义URI选择算法
CXF 2.2.5以后提供了自定义的URI选择器,需要实现ResourceComparator接口

Java代码  收藏代码
  1. public class QueryResourceInfoComperator extends OperationResourceInfoComparator implements ResourceComparator {    
  2.     
  3.     public QueryResourceInfoComperator() {    
  4.         super(null, null);    
  5.     }    
  6.     
  7.     @Override    
  8.     public int compare(ClassResourceInfo cri1, ClassResourceInfo cri2, Message message) {    
  9.         return 0;    
  10.     }    
  11.     
  12.     @Override    
  13.     public int compare(OperationResourceInfo oper1, OperationResourceInfo oper2, Message message) {    
  14.     
  15.         int cxfResult = super.compare(oper1, oper2);    
  16.         if (cxfResult != 0)    
  17.             return cxfResult;    
  18.         //OperationResourceInfo获取resource方法请求路径相关信息    
  19.         int op1Counter = getMatchingRate(oper1, message);    
  20.         int op2Counter = getMatchingRate(oper2, message);    
  21.         //通过两两比较权重选择具体的方法    
  22.         return op1Counter == op2Counter ? 0 : op1Counter < op2Counter ? 1 : -1;    
  23.     }    
  24.     
  25.     protected int getMatchingRate(OperationResourceInfo operation, Message message) {    
  26.     
  27.         List<Parameter> params = operation.getParameters();    
  28.         if (params == null || params.size() == 0)    
  29.             return 0;    
  30.     
  31.         Set<String> qParams = getParams((String) message.get(Message.QUERY_STRING));    
  32.     
  33.         //匹配更多的参数获得更多的权限    
  34.         int rate = 0;    
  35.         for (Parameter p : params) {    
  36.             switch (p.getType()) {    
  37.                 case QUERY:    
  38.                     if (qParams.contains(p.getName()))    
  39.                         rate += 2;    
  40.                     else if (p.getDefaultValue() == null)    
  41.                         rate -= 1;    
  42.                     break;    
  43.                 default:    
  44.                     break;    
  45.             }    
  46.         }    
  47.         return rate;    
  48.     }    
  49.     
  50.     /**  
  51.      * @param query URL Query Example: 'key=value&key2=value2'  
  52.      * @return A Set of all keys, contained within query.  
  53.      */    
  54.     protected Set<String> getParams(String query) {    
  55.         Set<String> params = new HashSet<String>();    
  56.         if (query == null || query.length() == 0)    
  57.             return params;    
  58.     
  59.         MultivaluedMap<String, String> allQueries = JAXRSUtils.getStructuredParams(query, "&", false, false);    
  60.         return allQueries.keySet();    
  61.     }    
  62. }    

 注册相应的服务:

Xml代码  收藏代码
  1. <jaxrs:server id="customerService" address="/jaxrs">  
  2.     <jaxrs:serviceBeans>  
  3.         <ref bean="restPathService6"/>  
  4.     </jaxrs:serviceBeans>  
  5.   
  6.     <jaxrs:resourceComparator>  
  7.         <bean class="org.ws.server.cxf.chap3.cxf.uri.QueryResourceInfoComperator"/>  
  8.     </jaxrs:resourceComparator>  
  9. </jaxrs:server>  
  10.   
  11. <bean id="restPathService6" class="org.ws.server.cxf.chap3.cxf.uri.CustomURITemplateService"/>    

 其中resource类的声明:

Java代码  收藏代码
  1. @Path("/rest")    
  2. public class CustomURITemplateService {    
  3.     
  4.     @GET    
  5.     @Path("/meth")    
  6.     public Response meth1(@QueryParam("name") String name) {    
  7.         return Response.status(200).entity("/rest/meth1 called[name=" + name + "]").build();    
  8.     }    
  9.     
  10.     @GET    
  11.     @Path("/meth")    
  12.     public Response meth2(@QueryParam("name") String name, @QueryParam("age") Integer age) {    
  13.         return Response.status(200).entity("/rest/meth2 called[name=" + name + ", age=" + age + "]").build();    
  14.     }    
  15.     
  16.     @GET    
  17.     @Path("/meth")    
  18.     public Response meth3(@QueryParam("name") String name, @QueryParam("age") Integer age,    
  19.                           @QueryParam("password") String password) {    
  20.         return Response.status(200)    
  21.                 .entity("/rest/meth3 called[name=" + name + ", age=" + age + ",password=" + password + "]").build();    
  22.     }    
  23.     
  24. }    

 对请求遵循以下规则:

Java代码  收藏代码
  1. /jaxrs/rest/meth?name=abd&age=123&password=aaa   --> meth3    
  2. /jaxrs/rest/meth?name=abd&age=123                --> meth2    
  3. /jaxrs/rest/meth?name=abd                        --> meth1    

 更多信息见这里

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多