Jersey搭建Rest web服务
本文由大关整理所得,转载请标明出处
1.
本文以实现一个简易的学生管理系统为例,讲解如何搭建web服务,在这个学生管理系统中,Rest服务端将会为客户端提供对学生的添加、修改、查询和删除功能。客户端通过Spring 的RestTemplate访问Rest服务端,完成对学生信息的操控。
2.
简易学生管理系统服务端使用Rest方式搭建,使用标准的JAX-RS接口和apache的Jersey实现。此外服务端使用Spring做为基础注入容器,Jetty作为嵌入式的web服务器(即Http服务器)。使用JAXB将对象转换成XML文档,通过Rest消息体传输。如果您对上述的某些概念不熟悉,请查看我的其他文档。
客户端使用Spring的RestTemplate完成访问。
3.
我们这里的model是学生数据的原型,在这个学生管理系统中,我们主要定义两个数据模型,第一个是Student,用来表示一个学生的信息的实体类,另外一个是StudentLists类,用来表示一个学生的集合。(由于使用JAXB无法直接将集合转换为XML文档,我们就采用了一种迂回的方式)。在建立Rest服务的时候,定义通信两端数据类型,往往是通过定义xsd(即schema)文档开始的。因为对于通信两端来说,schema就是通信签订的协议。这里我们的studentLists.xsd定义如下:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
</xs:schema>
当数据确定下来之后,我们需要使用java工具,将xsd转换成java对应的实体类。
使用的命令如下:
I:\programs\Rest\RestJersey\src>xjc -p org.example.model studentLists.xsd
上述的命令可以通过cmd窗口执行,首先要到达我们项目定义xsd的目录下,之后使用xjc(xml java compiler)将xsd文档转换成java类。-p 后面的第一个参数是转换后java类的包名字,第二个参数是xsd文档的名字。
此时,我们刷新项目,可以发现,系统已经为我们创建了两个实体类,两个类的大致内容如下:
package org.example.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "student", propOrder = {
})
@XmlRootElement(name = "student")
public class Student {
}
package org.example.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
})
@XmlRootElement(name = "studentList")
public class StudentList {
}
上面提供的两个类是同过xjc生成的,因此这些类已经添加了JAXB需要的标记。(注意,我们在Student类中略加修改,添加了一个XmlRootElement标签,因为我们希望,Student类可以作为一个独立的单位进行传输,更多细节可以参考我的关于JAXB2.0使用方法的文章)。
4.
在实际应用中,model是需要与实际数据库中的表相对应的,但是为了简化考虑,我们没有引进对数据库的操作(如果使用数据库,可以考虑使用Hibernate)。在服务端,仅用一个线程安全的Map来存储数据。DAO用来完成对数据的存取和更新操作。我们提供的DAO内容如下:
package org.example.model;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Component;
@Component
public class StudentDao {
}
实际上,students集合的操作换成数据库的操作并不是很难,读者可以自己去实现。
5.
这里的资源访问器,是指服务端对外部提供的访问服务端资源的接口。客户端通过调用这些接口,完成对服务端的具体操作。定义如下:
package org.example.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.example.model.Student;
import org.example.model.StudentDao;
import org.example.model.StudentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/student/")
@Component
@Scope("prototype")
public class StudentResource {
}
资源访问接口主要使用了JAX-RS接口,向外提供服务访问。
6.
我们使用嵌入式的jetty容器,作为http服务容器。Jetty的创建和开启很简单,如下:
package org.example.server;
import java.io.File;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;
public class StartServer {
}
注意,在创建和开启jetty服务时,提供了jetty容器的监听端口号12123,Rest服务的访问目录名称/RestJersey,以及配置文件web.xml的具体位置(即当前工作目录下的resources目录下)。
7.
我们知道,使用web应用程序都需要配置一个web.xml文件,web容器可以同过web.xml文件来获取服务器的具体配置和使用信息。这里我们需要在web.xml文件中提供Spring监听器,并将Spring监听器中监听到的所有resources为前缀的消息投递给Jersey监听器。还要配置Spring的一些基本信息。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
</web-app>
在web.xml配置文档中,先指明了spring配置文档的位置,之后指明了一个jersey服务接收的消息模式。上面的配置说明,Jetty服务器接收到请求,先投递给Spring,之后Spring会将接收到的,以resources为前缀的请求提交给Jersey执行。
8.
Spring配置文档需要扫描所有内部的标记,其中包括Spring IOC的标记、JAXB的标记、JAX-RS的标记。之后根据标记创建相应的具体实例。配置文档很简单,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
</beans>
9.
最后我们提供一个关于log4j的配置,这个配置主要用于输出log内容(jetty等使用的是log4j日志)。
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS}:: %-4r [%t] %-5p %c %x %n%-5p %m%n
log4j.rootLogger=info, stdout, R
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.file=gridServer.log
log4j.appender.R.MaxFileSize=100KB
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-mm-dd HH:mm:ss.SSS} ::%-4r [%t] %-5p %c %x %n%-5p %m%n
10.
现在,执行第六步中的jetty服务的main方法,可以得到如下输出:
2011-07-08 19:05:31.656:: 0
INFO
2011-07-08 19:05:31.843:: 187
INFO
2011-07-08 19:05:32.031:: 375
INFO
2011-7-8 19:05:32 org.springframework.web.context.ContextLoader initWebApplicationContex
信息: Root WebApplicationContext: initialization started
2011-7-8 19:05:32 org.springframework.context.support.AbstractApplicationConte
信息: Refreshing Root WebApplicationContext: startup date [Fri Jul 08 19:05:32 CST 2011]; root of context hierarchy
2011-7-8 19:05:32 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from ServletContext resource [/resources/spring/spring-servlet.xml]
2011-7-8 19:05:33 org.springframework.beans.factory.support.DefaultListableBeanFacto
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFacto
2011-7-8 19:05:33 org.springframework.web.context.ContextLoader initWebApplicationContex
信息: Root WebApplicationContext: initialization completed in 1875 ms
2011-07-08 19:05:33.921:: 2265 [main] INFO
INFO
2011-07-08 19:05:33.984:: 2328 [main] INFO
INFO
2011-07-08 19:05:33.984:: 2328 [main] INFO
INFO
11.
最后,我们提供一个客户端,用来访问服务端,并对服务端进行操作。
package org.example.client;
import java.net.URI;
import java.net.URISyntaxException;
import org.example.model.Student;
import org.example.model.StudentList;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
public class Client {
}
测试结果:
S09080707
李雷
33
S09080808
Lucy
23
S09080458
大关
22
12.
通过上面提供的例子,我们可以掌握了使用JAX-RS编写Rest服务的全过程,这里需要注意的是,在服务端与客户端传递java对象的时候(不是指基础对象,String、int等),使用的是JAXB,而JAXB 可以通过Spring创建其JAXBContent,因此无需单独提供JAXB的上下文设置。
本文使用jetty、Spring、JAX-RS、Jersey、JAXB2.0、Log4j等技术搭建Rest服务,并使用Spring的RestTemplate编写客户端,如果读者想要继续学习Rest服务,可以参考我的其他文章。
13.
Spring官方文档(在Spring 下载后的jar包中)
JAXB 使用文档(http://jaxb.java.net/tutorial/index.html)
JAX-RS教材(Restful Java with JAX-RS)
Rest 服务论文(Architectural Style and the Design of Network-base Software Architectures)
Jersey 官方网站:http://jersey.java.net/
SpringMVC与JAX-RS:http://www.infoq.com/articles/springmvc_jsx-rs
14.
I:\programs\Rest\RestJersey>tree /F
文件夹 PATH 列表
卷序列号为 801B-4CB2
I:.
├─lib
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
├─resources
│
│
│
│
│
└─src
I:\programs\Rest\RestJersey>