Java开发RESTful(四)SpringMVC开发上

【原创文章,转载请注明原文章地址,谢谢!】

从本章起,我们正式进入RESTful的开发。

Java领域中RESTful开发利器

在Java领域中,关于RESTful开发有很多的框架,基本上所有的MVC/Web框架都在一定程度上支持RESTful的开发。以下只列举几个最为常用的RESTful开发框架。

Jersey

Jersey RESTful 框架是开源的RESTful框架, 实现了JAX-RS (JSR 311 & JSR 339) 规范。它扩展了JAX-RS 参考实现, 提供了更多的特性和工具, 可以进一步地简化 RESTful service 和 client 开发。尽管相对年轻,它已经是一个产品级的 RESTful service 和 client 框架。

优点(下面优点来源于官方说明):
优秀的文档和例子
快速开发
超级容易的路由
平滑的 JUnit 集成
就个人而言, 当开发 RESTful service 时, JAX-RS(使用RESTful 风格来开发web service服务的规范) 实现要好于 MVC 框架。
可以集成到其它库/框架 (Grizzly, Netty). 这也可能是很多产品使用它的原因。
支持异步链接
使用Jersey的时候可以不依赖于Servlet容器。
WADL, XML/JSON support

SpringMVC

SpringMVC对RESTful开发也有完善的支持。因为SpringMVC在Web开发这块占有率达到50%以上(如果算上Springboot的话,应该超过这个比例,千万不要认为Springboot是一个新框架!!)。这就是他最大的优势。

Play

Play也是现在使用越来越多的一个Web框架。使用Play Framework 很容易地创建,构建和发布 web 应用程序,支持 Java & Scala。它使用Akka, 基于一个轻量级的无状态的架构。它应该应用于大规模地低CPU和内存消耗的应用。

优点
易于开发
基于 Netty, 支持非阻塞的 I/O. 并行处理远程调用的时候很优秀
社区很大较大
快速的项目构建和启动
REST, JSON/XML, Web Sockets, non-blocking I/O
只需刷新浏览器就可以看到最新的改变
支持异步调用

怎么选择?

本系列文章中,我们会优先使用SpringMVC来完成RESTful相关开发讲解,然后再介绍JSR311/JSR339及Jersey的开发。

使用Postman测试

有很多测试的方法。因为我们只是把RESTful作为接口使用,是没有前端应用的,所以我们需要使用一些测试工具来辅助我们接口的测试。在本系列文章中,我们使用Postman来完成测试。

Postman是基于Chrome浏览器的一个插件应用,提供了完善的HTTP请求模拟功能,在组套测试,团队开发,API文档生成,接口共享这块也越来越完善。
REST测试功能如果能够翻墙,那么直接从google商店即可安装,否则,可以通过下面连接下载
Mac版本下载地址:https://dl.pstmn.io/download/latest/osx
Win版本下载地址:https://dl.pstmn.io/download/latest/win
下载安装即可。

安装好之后,打开应用,即看到postman的运行界面:
WX20180120-152959@2x.png
整体的使用还是非常简单的,多用一下,就很容易上手了。如果想要看到更详细的Postman的使用,可以访问Postman的官网:https://www.getpostman.com/

SpringMVC开发REST基本使用

接下来,正式进入SpringMVC开发RESTful应用。首先介绍的是@RequestMapping标签。我们知道@RequestMapping标签是RequestMappingHandlerMapping类用来完成请求到方法的映射。我们之前使用这个标签做普通的Web应用,也只是用于做映射和请求路径窄化(@RequestMapping标签放在Controller类上面)。但是要开发REST应用,@RequestMapping标签是非常重要的一个标签。

测试基础设置

接下来具体看一下SpringMVC对REST开发的支持。我们构建一个演示应用来演示一些具体的功能点(具体应用创建过程适当简化,包括SpringMVC的配置等,都不在文中展示,有需要可以查看文末中关于SpringMVC的视频地址)。首先创建一个普通的Maven项目,加入相关的SpringMVC依赖和Jackson依赖。

<dependencies>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.5.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.5.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.5.0</version>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.16.6</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>4.2.3.RELEASE</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.7.4</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.7.4</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>4.2.3.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.23</version>
 </dependency>
 <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
  </dependency>
</dependencies>

其次,创建一个模型:Employee,代表员工:

@Setter
@Getter
public class Employee {
private Long id;
private String name;

public Employee(Long id, String name) {
    super();
    this.id = id;
    this.name = name;
}
public Employee() {
    super();
}
}

创建一个Controller,完成一个最简单的使用json返回一个员工列表:

@Controller
@RequestMapping("employees")
public class EmployeeRestController {

/**
 * 需求:得到所有的员工信息 GET emps 返回员工信息json列表
 */
@RequestMapping
@ResponseBody
public List<Employee> listEmps() {
    List<Employee> emps = new ArrayList<>();
    emps.add(new Employee(1L, "emp1"));
    emps.add(new Employee(2L, "emp2"));
    return emps;
}
}

启动应用,打开Postman,在url地址栏输入localhost:8080/employees及可以看到响应中的员工json数据:
image.png

至此,最基本的测试环境搭建完成,开始REST开发。

映射请求方式

在上面的基础案例中,我们在postman中,不管设置何种请求方式,比如默认的GET,还是POST,DELETE,都可以得到相同的正确的结果,这显然和RESTful本身的规定是背离的,所以第一步,我们要处理的是限制请求方式。

/**
 * 需求:得到所有的员工信息 GET emps 返回员工信息json列表
 * 需求2:限定只能使用GET的方法来请求改方法 使用method参数规定请求该方法所需要的http请求方式
 */
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<Employee> listEmps() {
    List<Employee> emps = new ArrayList<>();
    emps.add(new Employee(1L, "emp1"));
    emps.add(new Employee(2L, "emp2"));
    return emps;
}

@RequestMapping中使用method属性来限制该方法能够接受的请求方法,可以通过RequestMethod这个枚举类来选择:

public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}

此时,如果使用非GET方式请求,则自动会返回405异常:
image.png

在spring4.3版本之后,提供了一个更为简单的标签来支持GET方法的映射:@GetMapping。先看看该标签源码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {

可以看到,GetMapping注解上直接添加了@RequestMapping(method = RequestMethod.GET),所以我们的方法可以简化为:

@GetMapping
@ResponseBody
public List<Employee> listEmps() {

即可。

@RestController

如果在一个控制器中,所有的结果都是为了支持REST,即在一个Controller中,所有的方法都是返回JSON或者XML格式数据,那么我们没有必要在每一个方法上面都加上一个@ResponseBody标签,只需要在类上面统一使用@RestController注解即可。@RestController标签是一个复合注解,我们看一下声明代码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

可以很清楚的看到,RestController即为@Controller@ResponseBody的复合注解。所以我们代码最终简化为:

@RestController
@RequestMapping("employees")
public class EmployeeRestController {

@GetMapping
public List<Employee> listEmps() {
    List<Employee> emps = new ArrayList<>();
    emps.add(new Employee(1L, "emp1"));
    emps.add(new Employee(2L, "emp2"));
    return emps;
}
}

这就是我们做REST开发最基础的一个代码骨架。

@RequestMapping的path参数化

接下来我们考虑一个需求,查询某一个指定Employee的信息,按照REST设计原则,URI应该为GET /employees/ID,那么就要想办法完成这个id的映射了,代码如下:

/**
 * 需求:得到某一个指定员工的信息 GET emps/1 返回一个json对象
 * 
 * 使用URL变量+@PathVariable来完成url地址中可变内容的映射
 * 
 */
@GetMapping(value = "{id}")
public Employee getEmp(@PathVariable("id") Long id) {
    return new Employee(id, "emp");
}

如果单纯使用@RequestMapping,则注解应该为@RequestMapping(method=RequestMethod.GET,value=”{id}”)。这里有两点需要注意,第一,{id}可以理解为一个占位符,配合在类上面的@RequestMapping(“employees”),则整个资源路径完整的应该是employees/{id}。第二,在方法的参数中,我们使用@PathVariable注解来完成了URL中的占位符到参数值的映射,即URI中的{id}映射到Long id参数上。花括号{}中的参数名字可以任意起名。比如获取某个部门下的指定员工信息可以表示为:

@GetMapping(value = "depts/{deptId}/emps/{empId}")
public Employee getEmpBelongDept(@PathVariable("deptId") Long deptId,@PathVariable("empId") Long empId){

}

小结

在本章中,我们从最入门的角度去了解了SpringMVC做REST开发的基础。下一节,我们会详细的去看@RequestMapping中的更多属性。


猜你喜欢

转载自blog.csdn.net/wolfcode_cn/article/details/80810169