本文介绍使用CXF实现基于Rest方式的WebService(CXF的版本是3.0.0)
一. 前言
Java有三种WebService规范:Jax-WS,Jax-RS,Jaxm
1. Jax-WS(Java Api for XML-Based WebService):实现Soap协议(Simple Object Access Protocol)(用的也不多了)
2. Jax-RS(Java Api for Resource-Based WebService):实现Rest方式(Representational State Transfer)(推荐)
3. Jaxm支持文件传输,暴露更多底层细节(不推荐)
二. 引入依赖
<!-- Jax-RS前端控制模块,处理服务业务的请求 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Jax-RS客户端,用到WebClient等客户端代码调用类时需引入 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Json格式自动转换Provider,Jackson好用一些 -->
<!-- 也可以引入CXF自带的cxf-rt-rs-extension-providers(以及它默认使用jettison)-->
<!-- 也可以不使用Provider,直接使用CXF默认的Response.ResponseBuilder生成Response并返回,格式需要自己转换,例如使用Gson -->
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
<!-- 数据传输模块(与Soap一样) -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.0.0</version>
</dependency>
<!-- 引入内置的Jetty,如在Tomcat中发布服务可以不引入(与Soap一样) -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.0</version>
</dependency>
三. 编写SEI(Service Endpoint Interface)
1. 接口
import javax.ws.rs.core.Response;
//Produces & Consumes既可以加在Class上,也可以加在Method上,Method上的优先
@Path("/hello")
@Produces({ MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_JSON })
public interface HelloWorld {
//Response是CXF默认的返回对象,实际数据可以封装在里面,如果使用WebClient等客户端测试类时,推荐返回Response
@GET
@Path(value = "/resp")
public Response sayHelloResponse();
@GET
@Path(value = "/string/{name}")
public Result sayHelloString(@PathParam("name") String name);
//Result、User都是POJO,代码略,需要使用某种第三方*JsonProvider,这样普通对象和Json格式可以自动互相转换
@POST
@Path(value = "/user")
public Result sayHelloUser(User user);
}
2. 实现类
public class HelloWorldImpl implements HelloWorld {
// 使用Response.ResponseBuilder生成Response,格式需要自己转换,例如使用Gson
@Override
public Response sayHelloResponse() {
Response.ResponseBuilder rb = Response.status(Status.OK);
return rb.entity("Hello").build();
}
@Override
public Result sayHelloString(String name) {
return new Result("Hello, (String) " + name);
}
@Override
public Result sayHelloUser(User user) {
return new Result(new User("Rest_" + user.getName()));
}
}
四. 发布服务
1. 发布方式一:使用默认的Jetty时,使用CXF提供的JAXRSServerFactoryBean(与Soap类似)
// 1). 服务端工厂类
JAXRSServerFactoryBean server = new JAXRSServerFactoryBean();
// 2). 设置了二个属性
server.setAddress("http://localhost:8088/testcxf/cxf/rest");
server.setServiceBean(new HelloWorldImpl());
// 3). 添加 Provider,用于支持自动解析各种数据格式、如Json
List<Object> providerList = new ArrayList<Object>();
providerList.add(new JacksonJsonProvider());
server.setProviders(providerList);
// 添加输入&输出日志(可选)
server.getInInterceptors().add(new LoggingInInterceptor());
server.getOutInterceptors().add(new LoggingOutInterceptor());
// 4). 创建并发布服务,会发起一个http服务,默认使用Jetty
server.create();
(http://localhost:8088/testcxf/cxf/rest/hello/string/Jimmy)
2. 发布方式二:在Web应用中,使用CXFNonSpringServlet发布(实际是显式调用了发布方式一)
a. 在web.xml中添加CXFNonSpringServlet的实现类(与Soap一样)
<servlet>
<servlet-name>CXFNonSpring</servlet-name>
<servlet-class>net.jmystudio.servlet.WebServiceNonSpringServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFNonSpring</servlet-name>
<url-pattern>/cxfns/*</url-pattern>
</servlet-mapping>
b. 实现WebServiceNonSpringServlet类(与Soap类似)
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
public class WebServiceNonSpringServlet extends CXFNonSpringServlet {
@Override
protected void loadBus(ServletConfig servletConfig) {
super.loadBus(servletConfig);
//使用JAXRSServerFactoryBean,代码类似发布方式一,此处简略
// 1). 服务端工厂类
JAXRSServerFactoryBean server = new JAXRSServerFactoryBean();
// 2). 设置了两个属性
......
// 3). 创建并发布服务
server.create();
}
}
(http://localhost:8090/testcxf/cxfns/rest/hello/string/Jimmy)(应用包名是testcxf)
3. 发布方式三:在Web应用中,整合Spring+CXFServlet发布(实际是隐式调用了发布方式一)(最常用)
a. 需要引入Spring相关的Jar包(与Soap一样)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
b. 在web.xml中添加Spring配置和CXFServlet(与Soap一样)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>/cxf/*</url-pattern>
</servlet-mapping>
c. 添加关于cxf的spring配置文件(例spring-cfx-rest.xml)(与Soap类似)
<!-- 初始化cxf servlet -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- 日志拦截器bean -->
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<!-- 发布方式1:使用JAXRSServerFactoryBean-->
<jaxrs:server address="/rest">
<jaxrs:serviceBeans>
<ref bean="helloWorldService" />
</jaxrs:serviceBeans>
<!-- Provider -->
<jaxrs:providers>
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>
</jaxrs:providers>
<!-- 输入日志拦截器 -->
<jaxrs:inInterceptors>
<ref bean="loggingInInterceptor"/>
</jaxrs:inInterceptors>
<!-- 输出日志拦截器 -->
<jaxrs:outInterceptors>
<ref bean="loggingOutInterceptor" />
</jaxrs:outInterceptors>
</jaxrs:server>
<bean id="helloWorldService" class="net.jmystudio.cxf.rest.HelloWorldImpl" />
(http://localhost:8090/testcxf/cxf/rest/hello/string/Jimmy) (应用包名是testcxf)
五. 调用方式
无论使用哪种发布方式,发布成功后,都可以在火狐RestCilent的调试插件里调试,均可看到该WebService的接口定义的返回内容
例如 http://localhost:8088/testcxf/cxf/rest/hello/string/Jimmy
代码调用主要有3种方式
1. 调用方式一:使用JAXRSClientFactory获得静态的代理Client(显式依赖WebService接口,需要引入服务提供方提供的jar包)(JAX-RS 1.0,已过时,不推荐)(与Soap类似)
HelloWorld staticClient = JAXRSClientFactory.create("http://localhost:8088/testcxf/cxf/rest", HelloWorld.class);
Response resp1 = staticClient.sayHelloResponse();
System.out.println(resp1.getMetadata());
System.out.println(resp1.readEntity(String.class));
2. 调用方式二:使用ClientBuilder、WebTarget
WebTarget webTarget = ClientBuilder.newClient().target("http://localhost:8088/testcxf/cxf/rest").path("/hello/string").path("/Tony");
Response resp2 = webTarget.request(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).get();
System.out.println(resp2.getMetadata());
System.out.println(resp2.readEntity(String.class));
3. 调用方式三:使用WebClient(推荐)
WebClient webClient = WebClient.create("http://localhost:8088/testcxf/cxf/rest").path("/hello/string").path("/Kevin");
Response resp3 = webClient.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).get();
System.out.println(resp3.getMetadata());
System.out.println(resp3.readEntity(String.class));
4. 其它调用方式:使用HttpClient或HttpURLConnection等连接,与常规的URL获得数据的方式一致,详情略。