- 浏览: 117713 次
- 性别:
- 来自: 济南
文章分类
最新评论
http://blog.sina.com.cn/s/blog_616e189f0100snfg.html
使用Spring MVC 搭建Rest服务 (2011-06-24 21:48:28)转载▼标签: restfulspringmvcit 分类: java高级编程与虚拟计算平台
使用Spring MVC 搭建Rest服务
本文由大关总结整理所得,不保证内容的正确性,转载请标明出处!
Rest(Representational State Transfer,表述状态转移),是一种基于Http协议的,能够快速开发网络服务程序,并且提高网络服务系统伸展性的设计和开发方式。Rest的两端可以是不同构的编程和程序运行环境,Rest通过Http协议将通信的两端进行连接,在服务两端通过对Http协议的使用,最终完成数据的翻译和转换。
Rest有几个重要的特点,使得Rest能够在服务搭建上占有一定的优势。首先,Rest以一切皆资源的方式来看待所有的web提供的服务(包括web服务本身,以及web服务中某个具体的服务应用),所有的资源都采用URI进行定位。其次,Rest通过对Http中相应字段属性设置,确定客户端和服务端的缓存策略,在一定程度上,可以缓解服务器和客户端的压力(Rest服务被认为是无状态的web服务),最后,Rest通过对Http消息体中(MIME)Content-Type的内容不同,可以采用不同处理策略对消息分解,获取需要的信息,此外Rest还有很多优势,具体可以参考:Architectural Styles and the Design of Network-based Software Architectures这篇论文,文中详细描述了Rest服务的推导过程。
要想使用Rest必须要了解Http服务中定义的几个关键的方法。GET,用于从服务端获取信息,但并不更改服务端的任何状态;POST,用于提交创建的信息,一般更改服务端的数据状态,并且返回新创建对象的ID(一般来说,为了保证ID的唯一性,ID的值需要由服务端来确定,因此在创建结束后会将新分配的ID反馈给客户端);PUT,用于更改服务端某个对象的状态(一般被认为是Update),服务端状态受到影响,不需要返回影响后的结果;DELETE,删除服务端的对象,服务端的状态发生改变,可以返回删除后的对象,也可以不返回。(Http还有其他的方法,但在Rest中不是很常用,可以查看RFC2616)。这些方法仅是意义上这样说,但是并不是一定要这用,具体的方法对应的动作完全是服务两端确定的。
在JAVA中搭建Rest服务有很多种,其中JAX-RS是JAVA定义的Rest服务的API,可以使用Apache实现的jersy搭建Rest服务。在Spring MVC中也提供了搭建Rest服务的方法,下文主要探讨如何使用Spring MVC搭建Rest服务。
下面的内容需要您对如下内容有所了解:
(1) 会使用Spring框架,理解依赖注入原理,理解Spring的基本配置。
(2) 使用过Java相关的Web容器(例如,Tomcat或Apache等,这里使用的是Jetty,原理相同)
(3) 会使用一款OXM框架(例如:Castor或JAXB,这里使用的是JAXB2)
(4) 对什么是Http服务和什么是Rest服务略有了解
如果您完全满足上面的条件,那么理解下面的内容就根本不成问题,如果您对有些内容不是很清楚,可以先看整个服务的流程,具体细节可以略看。
1. Rest功能说明
下面我们将开始使用Spring MVC的方法搭建一个Rest服务,在这个服务中,服务器端维护了一个关于Student的集合,客户端可以通过连接到服务端,向服务端添加、删除、查找和更新学生信息等操作。
2. 学生数据结构(bean)
通过对Rest功能的分析,可以看出,服务端有两个关键的数据结构,一个是用来表示学生信息的Student类,另外一个是用来表示学生集合的StudentList类。下面我们通过schema定义这两个数据结构。
StudentLists.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="student">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" minOccurs="1"/>
<xsd:element name="age" type="xsd:int"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="students" type="student" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
可以看出,schema中定义了两个结构,一个是学生类型,一个是学生集合类型。学生类型包括两个元素(分别是name和age)和一个属性(id)。学生集合类型中仅包含一个元素(students,是一个学生类型的集合)。
通过使用xjc生成java类,xjc命令如下:
I:\programs\eclipse\SpringMVCRestTest\src>xjc -p com.upc.upcgrid.guan.springMvcR
estTest.bean.student StudentLists.xsd
xjc(xml to java compiler),可以将schema转换成java类,-p后面的两个参数分别是转换后的包名,以及需要转换的schema文件名字。此时,你刷新你的服务,可以看到xjc已经生成了Student类和StudentLists类,此外还生成了一个ObjectFactory类,这里我们不需要ObjectFactory类,因此直接将这个类删除,之后在Student类的声明前增加一条语句,使得Student类声明部分如下:
@XmlType(name = "student", propOrder = {
"name",
"age"
})
@XmlRootElement(name="student")
public class Student {
3. 学生管理类
学生的基本结构已经搭建完成,现在需要提供一个管理类,用于来管理学生集合(注意,正常情况下,应当将学生数据存入数据库,这里为了简化,仅将这些信息出入一个Map中进行管理)。学生管理类维护这学生集合,以及基于集合之上的操作。
StudentManager.java
@Component
public class StudentManager {
Map<String, Student> students = new ConcurrentHashMap<String, Student>();
public synchronized void addStudent(Student student){
if(students.containsKey(student.getId()))
return;
students.put(student.getId(), student);
}
public synchronized void deleteStudent(String id){
if(students.containsKey(id))
students.remove(id);
}
public synchronized void updateStudent(String id,Student student){
deleteStudent(id);
if(student.getId()==null || student.getId().equals(""))
student.setId(id);
addStudent(student);
}
public synchronized Student getStudent(String id){
return students.get(id);
}
public synchronized StudentList getStudent() {
StudentList sl = new StudentList();
for(String key : students.keySet())
{
sl.getStudents().add(students.get(key));
}
return sl;
}
}
可以看出,我们将StudentManager标记成component,以便Spring能够将这个类的实例注入到使用它的类中(注意,这个类必须是一个单例模式,因为这个类中维护着学生的集合)。此外,StudentManager中的所有方法都标记成了同步方法,并且Map集合也是一个同步集合。StudentManager提供了对学生集合Map的基本操作。
4. Rest服务实现
下面提供了使用Spring MVC实现Rest服务的方法。
@Controller
@RequestMapping(value="/students")
public class StudentServer {
private StudentManager manager;
@RequestMapping(method=RequestMethod.POST)
@ResponseBody
public String createStudent(@RequestBody Student student){
manager.addStudent(student);
return student.getId();
}
@RequestMapping(value="/{id}",method=RequestMethod.GET)
@ResponseBody
public Student getStudent(@PathVariable String id){
return manager.getStudent(id);
}
@RequestMapping(method=RequestMethod.GET)
@ResponseBody
public StudentList getStudent(){
return manager.getStudent();
}
@RequestMapping(value="/{id}",method=RequestMethod.PUT)
@ResponseBody
public void updateStudent(@RequestBody Student student,@PathVariable String id){
manager.updateStudent(id, student);
}
@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
@ResponseBody
public void deleteStudent(@PathVariable String id){
manager.deleteStudent(id);
}
@Autowired
public void setManager(StudentManager manager) {
this.manager = manager;
}
}
用Control标记,Spring会认为这是一个Web服务,使用RequestMapping指明了服务映射的URI内容和映射的Http请求的方法。使用PathVariable可以从URI中获取参数,使用RequestBody,Spring会将Http消息体内部的数据使用配置策略转换成一个对象(参考5. Spring的配置),使用ResponseBody,Spring会将返回值转换成消息体需要的格式(参考5.Spring的配置)。
5. Spring的配置
现在,我们需要对Spring进行基本的配置,以便Spring能够使用正确的方式对接收到(发送出)的Http请求(相应)使用正确的方法处理(生成)消息体。我们的基本策略是,如果接收到的是XML文档(即Content-type是***/xml类型),则使用JAXB2将消息体转换成对象,否则将消息体作为普通文本进行处理;如果要发送的消息是复杂对象(不是简单类型,例如:String、Integer、void等),则将消息使用JAXB2进行编组,否则仅生成文本消息。
Spring的配置如下:
SpringConfig.java
@Configuration
public class SpringConfig {
private Jaxb2Marshaller marshaller;
public @Bean Jaxb2Marshaller jaxb2Marshaller()//配置JAXB2Context
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();//创建JAXB上下文环境
marshaller.setClassesToBeBound(Student.class,StudentList.class);//映射的xml类放入JAXB环境中
this.marshaller = marshaller;
return marshaller;
}
public @Bean AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
{
AnnotationMethodHandlerAdapter adapter = new AnnotationMethodHandlerAdapter();//创建消息体转换器
HttpMessageConverter<?>[] converters = new HttpMessageConverter<?>[2];//创建转换数组
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();//创建字符转换器
MarshallingHttpMessageConverter marshallerConverter = new MarshallingHttpMessageConverter();//创建xom转换器
marshallerConverter.setMarshaller(marshaller);//设置marshaller
marshallerConverter.setUnmarshaller(marshaller);//设置unmarshaller
//将两个转换器放入列表
converters[0] = (stringConverter);
converters[1] = (marshallerConverter);
//将转换器列表赋值给消息体转换器
adapter.setMessageConverters(converters);
return adapter; //返回消息体转换器
}
}
SpringConfig中先配置了JAXB2,在JAXB2中需要指定在2学生数据结构中生成的两个类,因此JAXB环境会知道如何将Student和StudentList编组和解组。
SpringConfig中还配置了一个Adapter,这个转换器是Spring用来转换消息体时使用的,在这里,我们为Adapter配置了两个Converter,一个使用来处理XML的Marshaller的Converter,一个是用来处理普通类型String的Converter。因此Spring会根据情况,选择正确的Converter处理消息体。(注意,只要你提供了一个Adapter,Spring就会使用你提供的这个Adapter处理消息体,如果你没有提供,Spring将会使用默认的Adapter处理消息体,由于对于XML类型(或者JSON类型)的数据必须手动提供Converter,所以这里不能使用默认的Converter)。
之后是Spring的配置文件:
rest-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.upc.upcgrid.guan"/>
</beans>
Spring的配置文档中,仅让Spring环境扫描标有标记类的包。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/conf/rest-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Spring使用DispatcherServlet完成URI任务的匹配和分发,在创建DispatcherServlet的时候,提供了Spring配置文件的路径。
6. Jetty服务器的配置和启动
我们这里使用较轻量级的Jetty服务器,以嵌入式的方式启动Spring服务。Jetty服务要比Tomcat小的多,并且无需安装,可以嵌入到程序中执行。下面给出Jetty的启动过程。
JettyServerStart.java
public class JettyServerStart {
public static void main(String[] args) {
Server server= new Server();//创建jetty web容器(这与tomcat容器类似)
server.setStopAtShutdown(true);//在退出程序是关闭服务
//创建连接器,每个连接器都是由IP地址和端口号组成,连接到连接器的连接将会被jetty处理
//第一个连接器的连接方式为http://202.194.158.128:8586
Connector connector = new SelectChannelConnector();//创建一个连接器
connector.setPort(8586);//连接的端口号,(tomcat下)一般是8080,这里根据服务需求进行设置
connector.setHost("202.194.158.128");//ip地址
server.addConnector(connector);//添加连接
//创建本地连接器,连接方式为http://localhost:8585
Connector connectorLocal = new SelectChannelConnector();
connectorLocal.setPort(8585);
connectorLocal.setHost("localhost");
server.addConnector(connectorLocal);
//配置rest服务
WebAppContext context = new WebAppContext();//创建服务上下文
context.setContextPath("/SpringMVCRestTest");//访问服务路径 http://{ip}:8568/SpringMVCRestTest
context.setConfigurationDiscovered(true);
context.setDescriptor(System.getProperty("user.dir")+File.separator+"conf"+File.separator+"web.xml");//指明服务描述文件,就是web.xml
context.setResourceBase(System.getProperty("user.dir"));//指定服务的资源根路径,配置文件的相对路径与服务根路径有关
server.setHandler(context);//添加处理
try {
server.start();//开启服务
server.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}//开启服务
}
现在,我们执行main方法,会的到如下输出:
2011-06-24 21:22:07.609:INFO::jetty-7.3.0.v20110203
2011-06-24 21:22:07.796:INFO::NO JSP Support for /SpringMVCRestTest, did not find org.apache.jasper.servlet.JspServlet
2011-06-24 21:22:07.921:INFO::started o.e.j.w.WebAppContext{/SpringMVCRestTest,file:/I:/programs/eclipse/SpringMVCRestTest/}
2011-06-24 21:22:08.093:INFO:/SpringMVCRestTest:Initializing Spring FrameworkServlet 'rest'
2011-6-24 21:22:08 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'rest': initialization started
2011-6-24 21:22:08 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing WebApplicationContext for namespace 'rest-servlet': startup date [Fri Jun 24 21:22:08 CST 2011]; root of context hierarchy
2011-6-24 21:22:08 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from ServletContext resource [/conf/rest-servlet.xml]
2011-6-24 21:22:08 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7736bd: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,springConfig,studentManager,studentServer,jaxb2Marshaller,annotationMethodHandlerAdapter]; root of factory hierarchy
2011-6-24 21:22:08 org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromClasses
信息: Creating JAXBContext with classes to be bound [class com.upc.upcgrid.guan.springMvcRestTest.bean.student.Student,class com.upc.upcgrid.guan.springMvcRestTest.bean.student.StudentList]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}.*] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}/] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students.*] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'rest': initialization completed in 969 ms
2011-06-24 21:22:09.125:INFO::Started SelectChannelConnector@202.194.158.128:8586
2011-06-24 21:22:09.125:INFO::Started SelectChannelConnector@localhost:8585
从输出的内容可以看出,Spring服务已经正常启动,并且Jetty将会在本地的8586和8585两个端口进行监听。
7. 编写客户端测试程序
Rest的客户端可以使用Spring的RestTemplate进行编写。为了给使用这屏蔽掉我们后台与服务器进行通信的复杂操作,我们编写了一个RestUtility,这个类主要完成与服务器的通信。
RestUtility.java
public class RestUtility {
private static String url = "http://202.194.158.128:8586/SpringMVCRestTest/rest/students/";
private static RestTemplate restTemplate;
static {
createRestTemplate();
}
public static RestTemplate getRestTemplate(){
return restTemplate;
}
private static void createRestTemplate() {
restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();//消息体转换器列表
MarshallingHttpMessageConverter marshalConverter = new MarshallingHttpMessageConverter();//xom类型的消息体转换器
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();//创建JAXB2类型的xom环境
marshaller.setClassesToBeBound(Student.class,StudentList.class);//将类绑定到JAXB2
marshalConverter.setMarshaller(marshaller);//设置编组器
marshalConverter.setUnmarshaller(marshaller);//设置解组器
converters.add(marshalConverter);//将xom消息体转换器添加到列表
converters.add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(converters);//将转换器列表放入RestTemplate
}
public static void addStudent(Student student) throws RestClientException, URISyntaxException{
System.out.println(restTemplate.postForObject(new URI(url), student,String.class));
}
public static Student getStudent(String id){
return restTemplate.getForObject(url+"{id}", Student.class, id);
}
public static void updateStudent(String id,Student student){
restTemplate.put(url+"{id}", student, id);
}
public static void deleteStudent(String id){
restTemplate.delete(url+"{id}", id);
}
public static StudentList getAllStudents() throws RestClientException, URISyntaxException{
return restTemplate.getForObject(new URI(url),StudentList.class);
}
}
这个类主要使用RestTemplate对服务器进行通信的。
最后给出一个测试程序,并给出结果.
public class RestClient {
public static void main(String[] args) throws RestClientException, URISyntaxException {
Student student = new Student();
student.setAge(20);
student.setName("Mary");
student.setId("05080416");
RestUtility.addStudent(student);
student.setAge(21);
student.setName("Lucy");
student.setId("05080411");
RestUtility.addStudent(student);
student = RestUtility.getStudent("05080416");
System.err.println(student.getName());
StudentList sl = RestUtility.getAllStudents();
for(Student s : sl.getStudents())
{
System.err.println(s.getName());
}
}
在测试程序中,我们先创建了两个学生,并将两个学生添加到远程,之后从远程查询了一个学生,并输出信息,然后查询了所有学生,输出信息。整个过程没有对错误进行处理。
执行输出:
2011-6-24 21:36:55 org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromClasses
信息: Creating JAXBContext with classes to be bound [class com.upc.upcgrid.guan.springMvcRestTest.bean.student.Student,class com.upc.upcgrid.guan.springMvcRestTest.bean.student.StudentList]
05080416
05080411
Mary
Lucy
Mary
8. 程序结构
参考:
Spring3.0官方文档
Spring MVC与JAX-RS对比:http://www.infoq.com/articles/springmvc_jsx-rs
教材:Restful Java with JAX-RS
论文:Architectural Styles and the Design of Network-based Software Architectures
Jetty:http://blog.sina.com.cn/s/blog_616e189f0100r1fs.html
JAXB:http://blog.sina.com.cn/s/blog_616e189f0100slij.html
源码下载:http://guanxinquan.download.csdn.net/
使用Spring MVC 搭建Rest服务 (2011-06-24 21:48:28)转载▼标签: restfulspringmvcit 分类: java高级编程与虚拟计算平台
使用Spring MVC 搭建Rest服务
本文由大关总结整理所得,不保证内容的正确性,转载请标明出处!
Rest(Representational State Transfer,表述状态转移),是一种基于Http协议的,能够快速开发网络服务程序,并且提高网络服务系统伸展性的设计和开发方式。Rest的两端可以是不同构的编程和程序运行环境,Rest通过Http协议将通信的两端进行连接,在服务两端通过对Http协议的使用,最终完成数据的翻译和转换。
Rest有几个重要的特点,使得Rest能够在服务搭建上占有一定的优势。首先,Rest以一切皆资源的方式来看待所有的web提供的服务(包括web服务本身,以及web服务中某个具体的服务应用),所有的资源都采用URI进行定位。其次,Rest通过对Http中相应字段属性设置,确定客户端和服务端的缓存策略,在一定程度上,可以缓解服务器和客户端的压力(Rest服务被认为是无状态的web服务),最后,Rest通过对Http消息体中(MIME)Content-Type的内容不同,可以采用不同处理策略对消息分解,获取需要的信息,此外Rest还有很多优势,具体可以参考:Architectural Styles and the Design of Network-based Software Architectures这篇论文,文中详细描述了Rest服务的推导过程。
要想使用Rest必须要了解Http服务中定义的几个关键的方法。GET,用于从服务端获取信息,但并不更改服务端的任何状态;POST,用于提交创建的信息,一般更改服务端的数据状态,并且返回新创建对象的ID(一般来说,为了保证ID的唯一性,ID的值需要由服务端来确定,因此在创建结束后会将新分配的ID反馈给客户端);PUT,用于更改服务端某个对象的状态(一般被认为是Update),服务端状态受到影响,不需要返回影响后的结果;DELETE,删除服务端的对象,服务端的状态发生改变,可以返回删除后的对象,也可以不返回。(Http还有其他的方法,但在Rest中不是很常用,可以查看RFC2616)。这些方法仅是意义上这样说,但是并不是一定要这用,具体的方法对应的动作完全是服务两端确定的。
在JAVA中搭建Rest服务有很多种,其中JAX-RS是JAVA定义的Rest服务的API,可以使用Apache实现的jersy搭建Rest服务。在Spring MVC中也提供了搭建Rest服务的方法,下文主要探讨如何使用Spring MVC搭建Rest服务。
下面的内容需要您对如下内容有所了解:
(1) 会使用Spring框架,理解依赖注入原理,理解Spring的基本配置。
(2) 使用过Java相关的Web容器(例如,Tomcat或Apache等,这里使用的是Jetty,原理相同)
(3) 会使用一款OXM框架(例如:Castor或JAXB,这里使用的是JAXB2)
(4) 对什么是Http服务和什么是Rest服务略有了解
如果您完全满足上面的条件,那么理解下面的内容就根本不成问题,如果您对有些内容不是很清楚,可以先看整个服务的流程,具体细节可以略看。
1. Rest功能说明
下面我们将开始使用Spring MVC的方法搭建一个Rest服务,在这个服务中,服务器端维护了一个关于Student的集合,客户端可以通过连接到服务端,向服务端添加、删除、查找和更新学生信息等操作。
2. 学生数据结构(bean)
通过对Rest功能的分析,可以看出,服务端有两个关键的数据结构,一个是用来表示学生信息的Student类,另外一个是用来表示学生集合的StudentList类。下面我们通过schema定义这两个数据结构。
StudentLists.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="student">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" minOccurs="1"/>
<xsd:element name="age" type="xsd:int"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="students" type="student" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
可以看出,schema中定义了两个结构,一个是学生类型,一个是学生集合类型。学生类型包括两个元素(分别是name和age)和一个属性(id)。学生集合类型中仅包含一个元素(students,是一个学生类型的集合)。
通过使用xjc生成java类,xjc命令如下:
I:\programs\eclipse\SpringMVCRestTest\src>xjc -p com.upc.upcgrid.guan.springMvcR
estTest.bean.student StudentLists.xsd
xjc(xml to java compiler),可以将schema转换成java类,-p后面的两个参数分别是转换后的包名,以及需要转换的schema文件名字。此时,你刷新你的服务,可以看到xjc已经生成了Student类和StudentLists类,此外还生成了一个ObjectFactory类,这里我们不需要ObjectFactory类,因此直接将这个类删除,之后在Student类的声明前增加一条语句,使得Student类声明部分如下:
@XmlType(name = "student", propOrder = {
"name",
"age"
})
@XmlRootElement(name="student")
public class Student {
3. 学生管理类
学生的基本结构已经搭建完成,现在需要提供一个管理类,用于来管理学生集合(注意,正常情况下,应当将学生数据存入数据库,这里为了简化,仅将这些信息出入一个Map中进行管理)。学生管理类维护这学生集合,以及基于集合之上的操作。
StudentManager.java
@Component
public class StudentManager {
Map<String, Student> students = new ConcurrentHashMap<String, Student>();
public synchronized void addStudent(Student student){
if(students.containsKey(student.getId()))
return;
students.put(student.getId(), student);
}
public synchronized void deleteStudent(String id){
if(students.containsKey(id))
students.remove(id);
}
public synchronized void updateStudent(String id,Student student){
deleteStudent(id);
if(student.getId()==null || student.getId().equals(""))
student.setId(id);
addStudent(student);
}
public synchronized Student getStudent(String id){
return students.get(id);
}
public synchronized StudentList getStudent() {
StudentList sl = new StudentList();
for(String key : students.keySet())
{
sl.getStudents().add(students.get(key));
}
return sl;
}
}
可以看出,我们将StudentManager标记成component,以便Spring能够将这个类的实例注入到使用它的类中(注意,这个类必须是一个单例模式,因为这个类中维护着学生的集合)。此外,StudentManager中的所有方法都标记成了同步方法,并且Map集合也是一个同步集合。StudentManager提供了对学生集合Map的基本操作。
4. Rest服务实现
下面提供了使用Spring MVC实现Rest服务的方法。
@Controller
@RequestMapping(value="/students")
public class StudentServer {
private StudentManager manager;
@RequestMapping(method=RequestMethod.POST)
@ResponseBody
public String createStudent(@RequestBody Student student){
manager.addStudent(student);
return student.getId();
}
@RequestMapping(value="/{id}",method=RequestMethod.GET)
@ResponseBody
public Student getStudent(@PathVariable String id){
return manager.getStudent(id);
}
@RequestMapping(method=RequestMethod.GET)
@ResponseBody
public StudentList getStudent(){
return manager.getStudent();
}
@RequestMapping(value="/{id}",method=RequestMethod.PUT)
@ResponseBody
public void updateStudent(@RequestBody Student student,@PathVariable String id){
manager.updateStudent(id, student);
}
@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
@ResponseBody
public void deleteStudent(@PathVariable String id){
manager.deleteStudent(id);
}
@Autowired
public void setManager(StudentManager manager) {
this.manager = manager;
}
}
用Control标记,Spring会认为这是一个Web服务,使用RequestMapping指明了服务映射的URI内容和映射的Http请求的方法。使用PathVariable可以从URI中获取参数,使用RequestBody,Spring会将Http消息体内部的数据使用配置策略转换成一个对象(参考5. Spring的配置),使用ResponseBody,Spring会将返回值转换成消息体需要的格式(参考5.Spring的配置)。
5. Spring的配置
现在,我们需要对Spring进行基本的配置,以便Spring能够使用正确的方式对接收到(发送出)的Http请求(相应)使用正确的方法处理(生成)消息体。我们的基本策略是,如果接收到的是XML文档(即Content-type是***/xml类型),则使用JAXB2将消息体转换成对象,否则将消息体作为普通文本进行处理;如果要发送的消息是复杂对象(不是简单类型,例如:String、Integer、void等),则将消息使用JAXB2进行编组,否则仅生成文本消息。
Spring的配置如下:
SpringConfig.java
@Configuration
public class SpringConfig {
private Jaxb2Marshaller marshaller;
public @Bean Jaxb2Marshaller jaxb2Marshaller()//配置JAXB2Context
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();//创建JAXB上下文环境
marshaller.setClassesToBeBound(Student.class,StudentList.class);//映射的xml类放入JAXB环境中
this.marshaller = marshaller;
return marshaller;
}
public @Bean AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
{
AnnotationMethodHandlerAdapter adapter = new AnnotationMethodHandlerAdapter();//创建消息体转换器
HttpMessageConverter<?>[] converters = new HttpMessageConverter<?>[2];//创建转换数组
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();//创建字符转换器
MarshallingHttpMessageConverter marshallerConverter = new MarshallingHttpMessageConverter();//创建xom转换器
marshallerConverter.setMarshaller(marshaller);//设置marshaller
marshallerConverter.setUnmarshaller(marshaller);//设置unmarshaller
//将两个转换器放入列表
converters[0] = (stringConverter);
converters[1] = (marshallerConverter);
//将转换器列表赋值给消息体转换器
adapter.setMessageConverters(converters);
return adapter; //返回消息体转换器
}
}
SpringConfig中先配置了JAXB2,在JAXB2中需要指定在2学生数据结构中生成的两个类,因此JAXB环境会知道如何将Student和StudentList编组和解组。
SpringConfig中还配置了一个Adapter,这个转换器是Spring用来转换消息体时使用的,在这里,我们为Adapter配置了两个Converter,一个使用来处理XML的Marshaller的Converter,一个是用来处理普通类型String的Converter。因此Spring会根据情况,选择正确的Converter处理消息体。(注意,只要你提供了一个Adapter,Spring就会使用你提供的这个Adapter处理消息体,如果你没有提供,Spring将会使用默认的Adapter处理消息体,由于对于XML类型(或者JSON类型)的数据必须手动提供Converter,所以这里不能使用默认的Converter)。
之后是Spring的配置文件:
rest-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.upc.upcgrid.guan"/>
</beans>
Spring的配置文档中,仅让Spring环境扫描标有标记类的包。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/conf/rest-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Spring使用DispatcherServlet完成URI任务的匹配和分发,在创建DispatcherServlet的时候,提供了Spring配置文件的路径。
6. Jetty服务器的配置和启动
我们这里使用较轻量级的Jetty服务器,以嵌入式的方式启动Spring服务。Jetty服务要比Tomcat小的多,并且无需安装,可以嵌入到程序中执行。下面给出Jetty的启动过程。
JettyServerStart.java
public class JettyServerStart {
public static void main(String[] args) {
Server server= new Server();//创建jetty web容器(这与tomcat容器类似)
server.setStopAtShutdown(true);//在退出程序是关闭服务
//创建连接器,每个连接器都是由IP地址和端口号组成,连接到连接器的连接将会被jetty处理
//第一个连接器的连接方式为http://202.194.158.128:8586
Connector connector = new SelectChannelConnector();//创建一个连接器
connector.setPort(8586);//连接的端口号,(tomcat下)一般是8080,这里根据服务需求进行设置
connector.setHost("202.194.158.128");//ip地址
server.addConnector(connector);//添加连接
//创建本地连接器,连接方式为http://localhost:8585
Connector connectorLocal = new SelectChannelConnector();
connectorLocal.setPort(8585);
connectorLocal.setHost("localhost");
server.addConnector(connectorLocal);
//配置rest服务
WebAppContext context = new WebAppContext();//创建服务上下文
context.setContextPath("/SpringMVCRestTest");//访问服务路径 http://{ip}:8568/SpringMVCRestTest
context.setConfigurationDiscovered(true);
context.setDescriptor(System.getProperty("user.dir")+File.separator+"conf"+File.separator+"web.xml");//指明服务描述文件,就是web.xml
context.setResourceBase(System.getProperty("user.dir"));//指定服务的资源根路径,配置文件的相对路径与服务根路径有关
server.setHandler(context);//添加处理
try {
server.start();//开启服务
server.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}//开启服务
}
现在,我们执行main方法,会的到如下输出:
2011-06-24 21:22:07.609:INFO::jetty-7.3.0.v20110203
2011-06-24 21:22:07.796:INFO::NO JSP Support for /SpringMVCRestTest, did not find org.apache.jasper.servlet.JspServlet
2011-06-24 21:22:07.921:INFO::started o.e.j.w.WebAppContext{/SpringMVCRestTest,file:/I:/programs/eclipse/SpringMVCRestTest/}
2011-06-24 21:22:08.093:INFO:/SpringMVCRestTest:Initializing Spring FrameworkServlet 'rest'
2011-6-24 21:22:08 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'rest': initialization started
2011-6-24 21:22:08 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing WebApplicationContext for namespace 'rest-servlet': startup date [Fri Jun 24 21:22:08 CST 2011]; root of context hierarchy
2011-6-24 21:22:08 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from ServletContext resource [/conf/rest-servlet.xml]
2011-6-24 21:22:08 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7736bd: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,springConfig,studentManager,studentServer,jaxb2Marshaller,annotationMethodHandlerAdapter]; root of factory hierarchy
2011-6-24 21:22:08 org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromClasses
信息: Creating JAXBContext with classes to be bound [class com.upc.upcgrid.guan.springMvcRestTest.bean.student.Student,class com.upc.upcgrid.guan.springMvcRestTest.bean.student.StudentList]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}.*] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/{id}/] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students.*] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/students/] onto handler [com.upc.upcgrid.guan.springMvcRestTest.spring.StudentServer@497904]
2011-6-24 21:22:09 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'rest': initialization completed in 969 ms
2011-06-24 21:22:09.125:INFO::Started SelectChannelConnector@202.194.158.128:8586
2011-06-24 21:22:09.125:INFO::Started SelectChannelConnector@localhost:8585
从输出的内容可以看出,Spring服务已经正常启动,并且Jetty将会在本地的8586和8585两个端口进行监听。
7. 编写客户端测试程序
Rest的客户端可以使用Spring的RestTemplate进行编写。为了给使用这屏蔽掉我们后台与服务器进行通信的复杂操作,我们编写了一个RestUtility,这个类主要完成与服务器的通信。
RestUtility.java
public class RestUtility {
private static String url = "http://202.194.158.128:8586/SpringMVCRestTest/rest/students/";
private static RestTemplate restTemplate;
static {
createRestTemplate();
}
public static RestTemplate getRestTemplate(){
return restTemplate;
}
private static void createRestTemplate() {
restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();//消息体转换器列表
MarshallingHttpMessageConverter marshalConverter = new MarshallingHttpMessageConverter();//xom类型的消息体转换器
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();//创建JAXB2类型的xom环境
marshaller.setClassesToBeBound(Student.class,StudentList.class);//将类绑定到JAXB2
marshalConverter.setMarshaller(marshaller);//设置编组器
marshalConverter.setUnmarshaller(marshaller);//设置解组器
converters.add(marshalConverter);//将xom消息体转换器添加到列表
converters.add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(converters);//将转换器列表放入RestTemplate
}
public static void addStudent(Student student) throws RestClientException, URISyntaxException{
System.out.println(restTemplate.postForObject(new URI(url), student,String.class));
}
public static Student getStudent(String id){
return restTemplate.getForObject(url+"{id}", Student.class, id);
}
public static void updateStudent(String id,Student student){
restTemplate.put(url+"{id}", student, id);
}
public static void deleteStudent(String id){
restTemplate.delete(url+"{id}", id);
}
public static StudentList getAllStudents() throws RestClientException, URISyntaxException{
return restTemplate.getForObject(new URI(url),StudentList.class);
}
}
这个类主要使用RestTemplate对服务器进行通信的。
最后给出一个测试程序,并给出结果.
public class RestClient {
public static void main(String[] args) throws RestClientException, URISyntaxException {
Student student = new Student();
student.setAge(20);
student.setName("Mary");
student.setId("05080416");
RestUtility.addStudent(student);
student.setAge(21);
student.setName("Lucy");
student.setId("05080411");
RestUtility.addStudent(student);
student = RestUtility.getStudent("05080416");
System.err.println(student.getName());
StudentList sl = RestUtility.getAllStudents();
for(Student s : sl.getStudents())
{
System.err.println(s.getName());
}
}
在测试程序中,我们先创建了两个学生,并将两个学生添加到远程,之后从远程查询了一个学生,并输出信息,然后查询了所有学生,输出信息。整个过程没有对错误进行处理。
执行输出:
2011-6-24 21:36:55 org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromClasses
信息: Creating JAXBContext with classes to be bound [class com.upc.upcgrid.guan.springMvcRestTest.bean.student.Student,class com.upc.upcgrid.guan.springMvcRestTest.bean.student.StudentList]
05080416
05080411
Mary
Lucy
Mary
8. 程序结构
参考:
Spring3.0官方文档
Spring MVC与JAX-RS对比:http://www.infoq.com/articles/springmvc_jsx-rs
教材:Restful Java with JAX-RS
论文:Architectural Styles and the Design of Network-based Software Architectures
Jetty:http://blog.sina.com.cn/s/blog_616e189f0100r1fs.html
JAXB:http://blog.sina.com.cn/s/blog_616e189f0100slij.html
源码下载:http://guanxinquan.download.csdn.net/
相关推荐
总结来说,使用Spring MVC搭建REST服务涉及以下几个步骤: 1. 设计RESTful API,定义URI和HTTP方法。 2. 创建数据模型和相应的Java Bean。 3. 编写控制器类,使用`@Controller`、`@RequestMapping`等注解处理HTTP...
这些内容是作者个人总结所的,主要描述了如何使用Spring MVC搭建Rest服务的全过程,并提供了整个搭建过程的源码。整个搭建过程中使用了Jetty、JAXB2.0和Spring等相关框架和技术,希望下载的人能够对这些技术有所了解...
通过"spring mvc rest基础学习demo",初学者可以快速掌握如何使用Spring MVC创建RESTful服务,理解HTTP方法与REST原则,以及如何处理请求和响应。这个项目将涵盖基本的控制器设置、REST端点创建、数据交互和错误处理...
Spring MVC也支持创建RESTful Web服务,通过@RequestMapping的HTTP方法参数(GET、POST、PUT、DELETE等),可以轻松构建符合REST原则的API。 10. **Spring Data JPA 和 MyBatis集成** 虽然Spring JDBC提供了一层...
对于RESTful Web服务的支持,Spring MVC提供了一套强大的工具,允许开发者创建符合REST原则的API。书中详细介绍了如何定义RESTful资源,使用HTTP动词(GET、POST、PUT、DELETE等)处理操作,以及如何处理JSON和XML...
标题《Pro Spring MVC》指的是一个专注于Java框架Spring MVC的学习资源,旨在帮助开发者掌握使用Spring MVC框架构建企业级Web应用程序的整个过程。Spring MVC是Spring框架中的一个模块,它支持Web应用程序的构建,...
本书由浅入深地介绍了当今流行的Java Web框架Spring MVC的方方面面,从基础的环境搭建到微服务设计与架构,再到持久化、REST API构建、认证与测试……涵盖了Spring MVC诸多重要且常用的特性。值得一提的是,本书针对...
在Spring Boot中,Spring MVC被默认集成,使得创建REST服务变得更加简单。 **创建Spring Boot项目** 1. 初始化项目:你可以使用Spring Initializr(start.spring.io)来初始化一个新的Spring Boot项目。选择Java...
2. **配置与搭建**:教程可能会介绍如何配置Spring MVC项目,包括web.xml中的DispatcherServlet配置,以及Spring的applicationContext.xml或Spring Boot的配置文件,用于定义bean和依赖注入。 3. **Controller编程*...
本压缩包文件“自动配置和Spring MVC来创建一个RESTful Web服务.rar”提供了一个详细指南,教你如何利用Spring Boot和Spring MVC创建RESTful Web服务。以下是关于这些知识点的详细解释: 1. **Spring Boot**: ...
### Spring MVC、Spring Boot 和 Spring Cloud 三者的区别与联系 #### 一、Spring MVC Spring MVC 是 Spring Framework 的一个重要组成部分,主要用于构建基于 Java 的 Web 应用程序。该框架采用了 MVC(Model-...
在IT行业中,Spring MVC是一个广泛使用的Java Web框架,它提供了构建高效、可维护的Web应用程序的强大支持。在本文中,我们将深入探讨“Spring MVC JSON学习”这一主题,重点关注如何在Spring MVC应用中处理JSON数据...
4. Spring MVC和REST API设计 5. Spring Boot的集成测试和生产准备 6. Spring Boot进阶,包括Actuator和Spring Cloud 7. 安全性实践,如Spring Security的使用 8. 部署和持续集成/持续部署(CI/CD) 通过学习本教程...
在实际开发中,我们通常会结合Spring Boot使用Spring MVC,因为Spring Boot简化了Spring应用的初始搭建以及配置过程。通过自动配置,Spring Boot能够快速启动一个包含Spring MVC的Web应用。此外,Spring Data JPA或...
- Spring MVC支持创建符合REST原则的服务,通过HTTP方法(GET、POST、PUT、DELETE)操作资源。 13. **Ajax集成** - 通过jQuery或其他JavaScript库与Spring MVC的异步交互,实现部分页面刷新。 14. **MVC配置** ...
总的来说,"SpringMVCRESTServer"项目旨在提供一个易于集成到Android应用中的RESTful服务,利用Spring MVC和Hibernate的强大功能,帮助开发者快速搭建稳定、高效的后端服务器。开发者可以通过学习这个项目,深入了解...
Spring MVC还提供了对RESTful风格的支持,使得构建基于REST的服务更加容易。RESTful是一种软件架构风格,用于设计网络应用,通常使用HTTP协议。在Spring MVC中,可以通过注解如`@RequestMapping`来轻松实现RESTful...
它基于Spring框架,在Spring的基础上进行扩展,为创建Web应用提供了一个简单、灵活、可配置的模型-视图-控制器(MVC)设计。SpringMVC通过分离模型、视图和控制器三个主要部分,简化了Web应用程序的开发流程。 ...
这是一个Java开发相关的主题,意味着我们将深入探讨如何使用Spring MVC来构建可扩展、高性能的Web应用程序,并通过REST接口提供服务。 Spring MVC是Spring框架的一个核心模块,它为Java开发者提供了一个强大且灵活...