依赖 本文基于cxf2.7.0,需要在前面的例子中加入对jaxrs的依赖:
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-frontend-jaxrs</artifactId>
- <version>2.7.0</version>
- </dependency>
由于2.7.0是采用jax-rs2.0版本,即JSR339,默认会引入:
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- <version>2.0-m10</version>
- </dependency>
当然对于之前的版本,基于jsr311。需要在pom中手动加入:1.0或1.1版本
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>jsr311-api</artifactId>
- <version>1.1.1</version>
- </dependency>
整合 这里主要考虑一下几种整合方式,更多见这里
在CXF中与JAX-WS一样,提供了JAXRSServerFactoryBean作为工厂服务类来编程式发布资源类
- JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
- //Bind one or more resources
- sf.setResourceClasses(CustomerService.class, AnotherService.class);
- // JAX-RS默认是每个请求会实例,这样修改为单例
- sf.setResourceProvider(CustomerService.class, new SingletonResourceProvider(new CustomerService()));
- sf.setAddress("http://localhost:9000/");
- BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class);
- JAXRSBindingFactory factory = new JAXRSBindingFactory();
- factory.setBus(sf.getBus());
- manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
- sf.create();
当然这里说的与spring整合少不了与web容器的整合,首先需要在web.xml中添加CXF的servlet监听请求路径:
- <servlet>
- <servlet-name>CXFServlet</servlet-name>
- <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
- <load-on-startup>5</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>CXFServlet</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
下面介绍两种spring的配置,这也是通用的
这是一个最简单的配置:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www./schema/beans"
- xmlns:xsi="http://www./2001/XMLSchema-instance"
- xmlns:jaxrs="http://cxf./jaxrs"
- xsi:schemaLocation="
- http://www./schema/beans
- http://www./schema/beans/spring-beans.xsd
- http://cxf./jaxrs
- http://cxf./schemas/jaxrs.xsd">
-
- <import resource="classpath:META-INF/cxf/cxf.xml" />
- <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
-
- <jaxrs:server id="customerService" address="/jaxrs">
- <jaxrs:serviceBeans>
- <ref bean="restPathService1"/>
- </jaxrs:serviceBeans>
-
- </jaxrs:server>
-
- <bean id="restPathService1" class="org.ws.server.cxf.chap3.cxf.server.CustomerService"/>
- </beans>
当然在jaxrs:server中可以加入其他节点,具体可参考http://cxf./schemas/jaxrs.xsd
也可以使用普通的bean配置,只是需要作为JAXRSServerFactoryBean实例的属性:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www./schema/beans"
- xmlns:xsi="http://www./2001/XMLSchema-instance"
- xsi:schemaLocation="
- http://www./schema/beans
- http://www./schema/beans/spring-beans.xsd">
-
- <import resource="classpath:META-INF/cxf/cxf.xml" />
- <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
-
- <bean class="org.apache.cxf.jaxrs.JAXRSServerFactoryBean" init-method="create">
- <property name="address" value="/jaxrs"/>
- <property:serviceBeans>
- <ref bean="restPathService1" />
- </property:serviceBeans>
- </bean>
-
- <bean id="restPathService2" class="org.ws.server.cxf.chap3.cxf.server.AnotherService"/>
- </beans>
数据绑定(Data Bindings) CXF对JAX-RS的数据绑定与之前的一样,需要选择相应的绑定类型,如需要加入JAXB对XML的处理:
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-databinding-jaxb</artifactId>
- <version>${cxf.version}</version>
- </dependency>
这也是CXF对JAX-RS的默认编码类型,因此我们在采用XML作为传输时只需要做简单的处理: 资源类中的一个方法:
- @GET
- @Path("/meth1")
- @Produces({ MediaType.APPLICATION_XML })
- public User meth1() {
- return new User(1, "robin", "123");
- }
其中User作为返回需要以XML的格式传输,最简单的处理只需要在:
- @XmlRootElement(name = "user")
- public class User {
-
- private Integer id;
- private String username;
- private String password;
当然更多操作需要了解
再介绍对JSON格式的处理,由于CXF默认支持是依赖Jettison通常我们更喜欢Jackson。所以,首先需要加入其依赖:
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-jaxrs</artifactId>
- <version>1.9.0</version>
- </dependency>
需要更换默认的JSON providers为Jackson:
- <jaxrs:providers>
- <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>
- </jaxrs:providers>
或
- <jaxrs:providers>
- <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
- </jaxrs:providers>
一个完整的例子: 资源类的方法声明:
- @GET
- @Path("/meth2")
- @Produces({ MediaType.APPLICATION_JSON })
- public User meth2() {
- return new User(1, "robin", "123");
- }
spring配置文件:
- <jaxrs:server id="customerService" address="/jaxrs">
- <jaxrs:serviceBeans>
- <ref bean="restPathService3"/>
- </jaxrs:serviceBeans>
-
- <jaxrs:providers>
- <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>
- </jaxrs:providers>
- </jaxrs:server>
-
- <bean id="restPathService3" class="org.ws.server.cxf.chap3.cxf.data.RESTDataBindService"/>
更多信息见这里
URI路径选择算法 当一个请求URI匹配了多个resource类,进行下面的算法: 1、优先匹配resource类@Path中更多的文字
- @Path("/bar/{id}")
- public class Test1 {}
- @Path("/bar/{id}/baz")
- public class Test2 {}
- @Path("/foo")
- public class Test3 {}
- @Path("/foo/")
- public class Test4 {}
对请求URI: /bar/1/baz 同时匹配上了Test1和Test2,但Test2中有9个文字而Test1只有5个,同样对请求/foo/将匹配Test4
2、优先匹配resource类@Path中更多组类
- @Path("/bar/{id}/")
- public class Test1 {}
- @Path("/bar/{id}/{id2}")
- public class Test2 {}
对请求URI:/bar/1/2同时匹配Test1和Test2,但Test2中有3个分组将优先匹配
3、优先匹配resource类@Path中捕获更多的正则组
- @Path("/bar/{id:.+}/baz/{id2}")
- public class Test1 {}
- @Path("/bar/{id}/bar/{id2}")
- public class Test2 {}
对请求URI:/bar/1/baz/2同时匹配Test1和Test2,且文字长度和组都一样,但Test1中匹配为1而Test2匹配中0
在多个resource方法中选择 根据上面的规则,我们已经选择了resource类。接下来对类中方法的选择也遵循这个原则,其中只有一点额外需要注意:
如果同时包含sub-resource优先选择非sub-resource
- @Path("/rest")
- public class Test1 {
-
- @Path("/bar")
- @GET
- public Order getOrder() {...}
-
- @Path("/bar")
- public Order getOrderFromSubresource() {...}
- }
-
- public class Order {
-
- @Path("/")
- @GET
- public Order getOrder() { return this; }
-
- }
对请求URI: /rest/bar将匹配方法getOrder()因为另外一个是sub-resource
方法与MediaType的选择 考虑这样的情况如果方法都匹配,但是MediaType不一样:
- @Path("/rest")
- public class Test1 {
-
- @Path("/bar")
- @POST
- @Consumes("application/json")
- @Produces("application/json")
- public Order addOrderJSON(OrderDetails details) {...}
-
- @Path("/bar")
- @POST
- @Consumes("application/xml")
- @Produces("application/xml")
- public Order getOrderXML(OrderDetails details) {...}
-
- }
对请求URI: /rest/bar以上两个都匹配,当然如果请求的Content-Type和Accept都有设置相应的类型,那么就好判断,如application/json指向addOrderJSON,如果是application/xml则指向方法getOrderXML当然,如果客户端并没明显指定而是直接在浏览器请求,可能会选择的是getOrderXML因为默认的可能是:
- Content-Type: text/html
- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
当然其实可以更好的方式处理这种情况:
- @Path("/")
- public class Test1 {
-
- @Path("/bar")
- @POST
- @Consumes({"application/json", "application/xml"})
- @Produces({"application/json", "application/xml"})
- public Order addOrder(OrderDetails details) {...}
-
- }
自定义URI选择算法 CXF 2.2.5以后提供了自定义的URI选择器,需要实现ResourceComparator接口
- public class QueryResourceInfoComperator extends OperationResourceInfoComparator implements ResourceComparator {
-
- public QueryResourceInfoComperator() {
- super(null, null);
- }
-
- @Override
- public int compare(ClassResourceInfo cri1, ClassResourceInfo cri2, Message message) {
- return 0;
- }
-
- @Override
- public int compare(OperationResourceInfo oper1, OperationResourceInfo oper2, Message message) {
-
- int cxfResult = super.compare(oper1, oper2);
- if (cxfResult != 0)
- return cxfResult;
- //OperationResourceInfo获取resource方法请求路径相关信息
- int op1Counter = getMatchingRate(oper1, message);
- int op2Counter = getMatchingRate(oper2, message);
- //通过两两比较权重选择具体的方法
- return op1Counter == op2Counter ? 0 : op1Counter < op2Counter ? 1 : -1;
- }
-
- protected int getMatchingRate(OperationResourceInfo operation, Message message) {
-
- List<Parameter> params = operation.getParameters();
- if (params == null || params.size() == 0)
- return 0;
-
- Set<String> qParams = getParams((String) message.get(Message.QUERY_STRING));
-
- //匹配更多的参数获得更多的权限
- int rate = 0;
- for (Parameter p : params) {
- switch (p.getType()) {
- case QUERY:
- if (qParams.contains(p.getName()))
- rate += 2;
- else if (p.getDefaultValue() == null)
- rate -= 1;
- break;
- default:
- break;
- }
- }
- return rate;
- }
-
- /**
- * @param query URL Query Example: 'key=value&key2=value2'
- * @return A Set of all keys, contained within query.
- */
- protected Set<String> getParams(String query) {
- Set<String> params = new HashSet<String>();
- if (query == null || query.length() == 0)
- return params;
-
- MultivaluedMap<String, String> allQueries = JAXRSUtils.getStructuredParams(query, "&", false, false);
- return allQueries.keySet();
- }
- }
注册相应的服务:
- <jaxrs:server id="customerService" address="/jaxrs">
- <jaxrs:serviceBeans>
- <ref bean="restPathService6"/>
- </jaxrs:serviceBeans>
-
- <jaxrs:resourceComparator>
- <bean class="org.ws.server.cxf.chap3.cxf.uri.QueryResourceInfoComperator"/>
- </jaxrs:resourceComparator>
- </jaxrs:server>
-
- <bean id="restPathService6" class="org.ws.server.cxf.chap3.cxf.uri.CustomURITemplateService"/>
其中resource类的声明:
- @Path("/rest")
- public class CustomURITemplateService {
-
- @GET
- @Path("/meth")
- public Response meth1(@QueryParam("name") String name) {
- return Response.status(200).entity("/rest/meth1 called[name=" + name + "]").build();
- }
-
- @GET
- @Path("/meth")
- public Response meth2(@QueryParam("name") String name, @QueryParam("age") Integer age) {
- return Response.status(200).entity("/rest/meth2 called[name=" + name + ", age=" + age + "]").build();
- }
-
- @GET
- @Path("/meth")
- public Response meth3(@QueryParam("name") String name, @QueryParam("age") Integer age,
- @QueryParam("password") String password) {
- return Response.status(200)
- .entity("/rest/meth3 called[name=" + name + ", age=" + age + ",password=" + password + "]").build();
- }
-
- }
对请求遵循以下规则:
- /jaxrs/rest/meth?name=abd&age=123&password=aaa --> meth3
- /jaxrs/rest/meth?name=abd&age=123 --> meth2
- /jaxrs/rest/meth?name=abd --> meth1
更多信息见这里
|