RESTful 简化了 web service 的设计,它不再需要 wsdl ,也不再需要 soap 协议,而是通过最简单的 http 协议传输数据 ( 包括 xml 或 json) 。既简化了设计,也减少了网络传输量(因为只传输代表数据的 xml 或 json ,没有额外的 xml 包装),JAX-RS ,全称 Java API for RESTful WebService。该规范定义了一系列的注解。
下面为大家介绍使用 cxf 开发 RESTful WebService
Cxf3.1.5 实现 jax-rs 的全套规范。在使用它时,最好全部包含它的lib(在它安装目录下的lib下面),从其官网上下载apache-cxf-3.1.5.zip,解压后,在解压的lib目录就是运行时需要的文件。
服务端
Spring4 +cxf3.1.5 开发 RESTfulweb service。
在eclipse上,选择开发动态web 应用项目,然后选择tomcat8作为web server。
然后,在项目视图中选择build path,去添加spring4,cxf3.1.5的lib (为了省事,直接把cxf下面的lib全部包含过来就可以了),如图:
同时,你得把这些libs都copy到 webcontent|web-inf|lib下面去,这样,eclipse在制作WAR文件时,会把这些.jar文件copy到war文件中去。
否则,tomcat会因为找不到这些jar文件而无法启动this web service.
本例子中,开发环境:
服务器端: eclipse + tomcat8 + CXF3.1.5 + Sring4
自己得去下载Spring4的jar包和CXF3.1.5的jar包,放到eclipse这个项目的web-content | web-info| lib下面
Client端: eclipse + CXF3.1.5 + httpclient + JSON + Java SE8
在tomcat上面的运行的server代码
EmployeeWSRestFulService.java (interface)
package cxf.server;
import java.util.*;
import javax.ws.rs.core.*;
import javax.ws.rs.*;
/**
*
* @author bigtree
* restful URI 匹配原理,它是咋构成的? access the below page
*http://blog.csdn.net/bigtree_3721/article/details/51158758
*
*/
@Path("/")
public interface EmployeeWSRestFulService {
/**
* JSON提交
* url:
* http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/addUser
* Json format:{"user":{"id":123,"name":"newBook"}}
* @param book
* @return
*/
@Path("/addUser/")
@POST
@Consumes({"application/json","application/xml"})
Response addUser(User user);
@Path("/delUser/{id}/")
@DELETE
@Consumes({"application/json","application/xml"})
Response delUser(@PathParam("id") String id);
@Path("/updateUser/")
@PUT
@Consumes({"application/json","application/xml"})
Response updateUser(User user);
/* http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/getUserById/1/
* JSON: response content as below
*
* HTTP/1.1 200 OK
* {"user":{"id":1,"name":"bigtree"}}
*/
/*
* XML response content as below
*
* HTTP/1.1 200 OK
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><user><id>1</id><name>bigtree</name></user>
*
*/
@Path("/getUserById/{id}/")
@GET
@Produces({"application/json","application/xml"}) //json is high priority
User getUserById(@PathParam("id") String id);
/* http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/
* response:
* HTTP/1.1 200 OK
* {"user":[{"id":1,"name":"bigtree"},{"id":2,"name":"jackie"}]}
*
*/
@Path("/")
@GET
//json is high priority, default is application/xml
@Produces({"application/json","application/xml"})
List<User> findAllUsers();
}
MyServiceImpl.java (server 的实现类)
package cxf.server;
import java.util.*;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.net.URI;
import javax.ws.rs.core.Response.*;
public class MyServiceImpl implements EmployeeWSRestFulService {
private void init(){
User user = new User();
user.setId("1");
user.setName("bigtree");
users.put(user.getId(), user);
user = new User();
user.setId("2");
user.setName("jackie");
users.put(user.getId(), user);
}
private HashMap<String, User> users = new HashMap<String,User>();
public MyServiceImpl(){
init();
}
public Response addUser(User user) {
users.put(user.getId(), user);
System.out.println("User entity to add: user id= "+user.getId()+" name="+user.getName());
System.out.println("adding user succeed");
System.out.println("total users in pool="+users.size());
return Response.ok().build();
}
public Response delUser(String id) {
User muser=users.remove(id);
if(muser == null) {
// this user with this id not exist
System.out.println("delUser(): no user entry found to delete");
return Response.status(Status.BAD_REQUEST).build();
}
else
return Response.ok().build();
}
public Response updateUser(User user) {
users.put(user.getId(), user);
System.out.println(users.get("1").getName());
return Response.ok().build();
}
public User getUserById(String id) {
User muser=users.get(id);
if(muser==null)
System.out.println("getUserById(): no user entry found");
return muser;
}
public List<User> findAllUsers() {
List<User> userlist = new ArrayList<User>();
Iterator userit = users.keySet().iterator();
while(userit.hasNext()){
userlist.add(users.get(userit.next()));
}
return userlist;
}
}
Spring 配置文件 applicationContext.xml 配置内容如下, (applicationContext是 Spring从tomcat启动需要的缺省配置文件)
<?xml version="1.0" encoding="GBK"?> <!-- Spring配置文件的根元素,使用spring-beans-4.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd "> <bean id="WSRestfulBean" class="cxf.server.MyServiceImpl"/> <jaxrs:server id="userService" address="/userws"> <jaxrs:serviceBeans> <ref bean="WSRestfulBean"/> </jaxrs:serviceBeans> </jaxrs:server> </beans>
web.xml 文件配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 配置spring --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置cxf servlet --> <servlet> <servlet-name>CXF315</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXF315</servlet-name> <url-pattern>/WSRest/*</url-pattern> </servlet-mapping> </web-app>
上面绿色部分需要自己写的。
然后在eclipse中以输出上面的项目(eclipse会产生一个.war文件),启动tomcat,然后从进入tomcat管理页面(
缺省是http://localhost:8080),点击"manager app",然后选择“WAR file to deploy”,选择上面产生的war文件,即可部署该web service服务了。
客户端
因为 RESTful 就是利用最原始的 http 协议传输数据,所以客户端其实就是一个 http客户端,有以下几种实现方式
1) JAX-RS Client API --cxf3.0+
2)Proxy 【使用起来简单,代理封装通信细节】
3)Apache HttpClient
4) WebClient
为了简单我使用了 HttpClient方式
代码如下
WSRestFulClient.java
package JAXRSClient;
/*
* How Request URI is Matched?
*
* http://blog.csdn.net/bigtree_3721/article/details/51158758
*
Lets assume
1) you have a web application called 'rest' (example, a 'rest.war' archive).
2) CXFServlet's url-pattern is "/test/*" (here it is defined in web.xml as below example shows
<servlet>
<servlet-name>CXF315</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXF315</servlet-name>
<url-pattern>/WSRest/*</url-pattern>
</servlet-mapping>
3) Finally, jaxrs:server's address is "/bar"
(it is defined in Spring's bean xml such as applicationContext.xml as below shown)
<bean id="WSRestfulBean" class="cxf.server.MyServiceImpl"/>
<jaxrs:server id="userService" address="/userws">
<jaxrs:serviceBeans>
<ref bean="WSRestfulBean"/>
</jaxrs:serviceBeans>
</jaxrs:server>
Requests like /rest/test/bar or /rest/test/bar/baz will be delivered to one of the resource classes in a given jaxrs:server
endpoint. For the former request to be handled, a resource class with @Path("/") should be available, in the latter case -
at least @Path("/") or a more specific @Path("/baz").
*
*/
/*
* Output format:
*
* http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/getUserById/1/
*
* Single user information format:
** JSON response format as below:
* HTTP/1.1 200 OK
* {"user":{"id":1,"name":"bigtree"}}
** XML response format as below:
* HTTP/1.1 200 OK
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?><user><id>1</id><name>bigtree</name></user>
*
* Multi-users response data format:
* JSON response format as below
* HTTP/1.1 200 OK
* {"user":[{"id":1,"name":"bigtree"},{"id":2,"name":"jackie"}]}
*
** XML response format as below
* HTTP/1.1 200 OK
* <?xml version="1.0" encoding="UTF-8"?><users><user><id>1</id><name>bigtree</name></user><user><id>2</id><name>jackie</name></user></users>
*/
/* console output
*
* httpMethodgo(): request string={"user":{"name":"Jane","id":"5"}}
HTTP/1.1 200 OK
HttpPutgo(): request string={"user":{"name":"Jane5","id":"5"}}
HTTP/1.1 200 OK
get one user via Get method of WS-Restful
HTTP/1.1 200 OK
{"user":{"id":5,"name":"Jane5"}}
receive OK rsp
id=5 name=Jane5
get all users via Get method of WS-Restful
HTTP/1.1 200 OK
{"user":[{"id":1,"name":"bigtree"},{"id":2,"name":"jackie"},{"id":5,"name":"Jane5"}]}
receive OK rsp
id=1 name=bigtree
id=2 name=jackie
id=5 name=Jane5
deleting user failed
HTTP/1.1 400 Bad Request
HTTP/1.1 200 OK
{"user":[{"id":1,"name":"bigtree"},{"id":2,"name":"jackie"},{"id":5,"name":"Jane5"}]}
receive OK rsp
id=1 name=bigtree
id=2 name=jackie
id=5 name=Jane5
*
*/
import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.*;
import org.apache.http.util.*;
import org.apache.http.entity.*;
import java.net.URI;
import java.util.*;
import java.io.*;
import org.json.*;
import cxf.server.*;
public class WSRestFulClient {
public static void main(String[] args) throws Exception {
User muser = new User();
muser.setId("5");
muser.setName("Jane");
//post method to add user
httpMethodgo("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/addUser", muser);
//put method to update user
muser.setName("Jane5");
HttpPutgo("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/updateUser", muser);
/*
* get a specified user via ID
*/
System.out.println("get one user via Get method of WS-Restful");
HttpGetgo("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/getUserById",5, true);
System.out.println("get all users via Get method of WS-Restful");
/*
* get all users
*/
HttpGetgo("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws",1,false);
//delete this user
muser.setId("100");
HttpDeletego("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws/delUser", muser);
//get all users again
HttpGetgo("http://localhost:8080/MyWebserviceRestfulSpringServer/WSRest/userws",1,false);
return;
}
private static void HttpPutgo(String uri, User muser ) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPut httpMethod = new HttpPut(uri);
httpMethod.addHeader("content-type","application/json");
JSONObject c = new JSONObject();
c.put("name", muser.getName());
c.put("id", muser.getId());
JSONStringer js = new JSONStringer();
js.object();
js.key("user").value(c);
js.endObject();
String ms=js.toString();
/** Console output
* HttpPutgo(): request string={"user":{"name":"Jane5","id":"5"}}
*/
System.out.println("HttpPutgo(): request string="+ms);
StringEntity entity = new StringEntity(ms,"UTF-8");
httpMethod.setEntity(entity);
CloseableHttpResponse response2 = httpclient.execute(httpMethod);
System.out.println(response2.getStatusLine());
String rst=EntityUtils.toString(response2.getEntity());
/**
* console output:
* HTTP/1.1 200 OK
*/
System.out.println(rst);
}
private static void httpMethodgo(String uri, User muser) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpMethod = new HttpPost(uri);
httpMethod.addHeader("content-type","application/json");
JSONObject c = new JSONObject();
c.put("name", muser.getName());
c.put("id", muser.getId());
JSONStringer js = new JSONStringer();
js.object();
js.key("user").value(c);
js.endObject();
String ms=js.toString();
/** console output:
* httpMethodgo(): request string={"user":{"name":"Jane","id":"5"}}
*/
System.out.println("httpMethodgo(): request string="+ms);
StringEntity entity = new StringEntity(ms,"UTF-8");
httpMethod.setEntity(entity);
CloseableHttpResponse response2 = httpclient.execute(httpMethod);
System.out.println(response2.getStatusLine());
String rst=EntityUtils.toString(response2.getEntity());
/**
* HTTP/1.1 200 OK
*/
System.out.println(rst);
}
private static void HttpGetgo(String resource, Integer userid, boolean SingleResult) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet request=null;
try {
if (SingleResult) {
//get one user via id
request=new HttpGet(resource+"/"+userid.toString());
}
else
request = new HttpGet(resource);
HttpResponse response = httpclient.execute(request);
try {
System.out.println(response.getStatusLine());
String rst=EntityUtils.toString(response.getEntity());
System.out.println(rst);
if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK) {
System.out.println("receive OK rsp");
HttpEntity entity = response.getEntity();
if(entity == null) {
System.out.println("entity is null");
return;
}
JSONObject jsonrsp = null;
jsonrsp = new JSONObject(new JSONTokener(rst));
if(SingleResult==false) {
JSONArray mobj=jsonrsp.getJSONArray("user");
Iterator it=mobj.iterator();
while(it.hasNext()){
JSONObject obj=(JSONObject)it.next();
System.out.println("id="+obj.getInt("id")+" name="+obj.getString("name"));
}
}
else {
JSONObject obj=jsonrsp.getJSONObject("user");
System.out.println("id="+obj.getInt("id")+" name="+obj.getString("name"));
}
}
else {
System.out.println("receive error rsp");
}
}
finally {
;
}
}
finally {
httpclient.close();
}
}
private static void HttpDeletego(String resource, User muser) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpDelete request=null;
try {
//delete one user via id
request=new HttpDelete(resource+"/"+muser.getId().toString());
HttpResponse response = httpclient.execute(request);
if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
System.out.println("deleting user succeed");
}
else {
System.out.println("deleting user failed");
}
System.out.println(response.getStatusLine());
String rst=EntityUtils.toString(response.getEntity());
System.out.println(rst);
}
finally {
httpclient.close();
}
}
}
另外,如果你不想用注解来标注代码,也可以用spring的 xml来配置,就像配置java bean一样,在上面的例子中,在applicatoinContent.xml中,可以写进如下的代码(例子)。
<bean id="restfulRegeditService" class="zxn.ws.service.impl.RestfulRegeditServiceImpl" />
<!--restful服务 -->
<jaxrs:server id="restServiceContainer" address="/regedit">
<jaxrs:serviceBeans>
<ref bean="restfulRegeditService" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
<jaxrs:languageMappings>
<entry key="en" value="en-gb" />
</jaxrs:languageMappings>
</jaxrs:server>
JSON-Java API page:
http://stleary.github.io/JSON-java/index.html
使用 TCPMON 这个工具监控以下,可以看到 http body 中只是简单的 json串,没有像 soap 协议那样的“信封”包装
使用 RESTful 设计风格 + 传输 json 数据格式 可以大大的简化 web service 的设计并提高传输效率
其实springMVC也采用了RESTful的设计风格,不过它使用的是spring自己的注解,这些注解和jax-rs中的注解惊奇的类似。如果大家有兴趣可以研究一下springMVC的RESTful特性。