从零开始 Spring Boot 2:处理请求
图源:简书 (jianshu.com)
经过上一篇从零开始Spring Boot 1:快速构建 - 魔芋红茶’s blog (icexmoon.cn)后,我们已经搭建起Spring Boot项目的开发环境,以及一个简单的使用Spring Boot的Web应用。本篇将演示在这个应用基础上,如何实现一个简单的可以进行增删改查(CURD,Create Update Retrieve Delete)的Web应用。
实体类
在创建Controller 之前,我们需要先创建一个实体类(Model),而为了更方便的创建实体类,我们需要引入一个有用的中间件lombok 。
引入这个中间件的方式同样是修改pom.xml ,在其中添加依赖:
...
<dependencies>
...
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
...
这些依赖也叫做启动器(starter),Spring Boot正是通过它来简化框架代码的相关配置。这些启动器内含了各种组件的引入,以及相关的默认设置,它们大致上可以分为官方以及第三方。通过引入这些启动器,我们可以很方便地给Spring Boot项目添加各种功能,并且这些功能是经过官方整合后的,可靠的组件。
我们这个示例项目初始包含两个启动器:
spring-boot-starter-web ,Web应用相关的基础功能,内含一个Nginx。spring-boot-starter-test ,Spring Boot的测试框架。
在从零开始Spring Boot 1:快速构建 - 魔芋红茶’s blog (icexmoon.cn)中介绍热启动时,添加了一个:
spring-boot-starter-test ,提供热启动等开发者需要的功能。
spring-boot-xxx 这样方式命名的启动器都是官方提供的组件,我们同样可以引入地方组件,比如这里的lombok 。
修改Maven配置并添加新的依赖后需要通过Maven重新加载项目,具体的方式有很多种,我习惯于在pom.xml 文件上右键选择Maven >Reload project 。
此时右下角可能会弹出一个Lombok requires ... 的提示,点击Enable 启用。
现在创建实体类:
package cn.icexmoon.my_first_app.model;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
}
这里使用了lombok 的Data 注解,其用途是可以“自动”给实体类附加上getter 和setter 方法,而避免了手动添加的额外工作。
这里的id 和age 属性可以使用基础类型long 和int ,但是数据库中间件会大量使用泛型(基础类型无法使用泛型),所以最好还是将实体类的属性定义为包装类而非基础类型。
Controller
这里将按照restfull 风格的API创建Controller ,用于处理下面几种URL请求:
POST /user ,添加新用户。GET /user ,获取全部用户信息。PUT /user/{id} ,修改指定用户信息。DELETE /user/{id} ,删除指定用户。GET /user/{id} ,获取指定用户信息。
下面是UserController 的全部代码:
package cn.icexmoon.my_first_app.controller;
import cn.icexmoon.my_first_app.model.Result;
import cn.icexmoon.my_first_app.model.User;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@RestController
@RequestMapping("/user")
public class UserController {
private Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());
private AtomicLong index = new AtomicLong();
@PostMapping("")
public String addUser(@RequestBody User user) {
Long newId = index.incrementAndGet();
user.setId(newId);
users.put(newId, user);
Result result = new Result();
result.setData(newId);
return result.toString();
}
@DeleteMapping("/{id}")
public Result deleteUser(@PathVariable long id) {
Result result = new Result();
if (!users.containsKey(id)) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
users.remove(id);
result.setMsg("delete success.");
return result;
}
@PutMapping("/{id}")
public Result updateUser(@PathVariable long id,@RequestBody User user) {
Result result = new Result();
if (!users.containsKey(id)) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
user.setId(id);
users.put(id, user);
return result;
}
@GetMapping("/{id}")
public Result getUser(@PathVariable long id) {
Result result = new Result();
User user = users.get(id);
if (user == null) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
result.setData(user);
return result;
}
@GetMapping("")
public String getUsers() {
Result result = new Result();
result.setData(users.values());
return result.toString();
}
}
因为所有的URL请求都以/user 开头,所以这里直接在Controller 类上加入@RequestMapping("/user") 注解以接收所有的/user/xxx 请求(当然也包含全部四种HTTP Method)。
具体的请求由Controller 的相关方法负责接收和处理,这里用GetMapping 、PutMapping 这样的注解来区分不同的HTTP Method。并且使用@PathVariable 注解将“路径参数”绑定到方法参数,用@RequestBody 注解将请求体(JSON格式纯文本)绑定到方法参数,此时框架会自动做“解JSON”处理。
数据的存储这里使用的是“内存中的容器”,其实就是一个HashMap 。所以每次重启应用你都会发现数据被清空,这是显而易见的…
需要注意的是,因为Java的Web服务是用多线程实现的,所以这里作为多个线程共享的Map 必须是一个“线程安全的容器”,因此这里使用了Collections.synchronizedMap 方法。
关于容器,可以阅读Java编程笔记8:容器(上) - 魔芋红茶’s blog (icexmoon.cn)。
这里用自增的整数作为User.id ,并且作为Map 的key使用,因此它也是多个线程共享的。这就需要在线程使用它产生一个新值时进行同步。这里可以使用锁或者syncronize 关键字,但更简洁的方式是使用原子类。这里使用了AtomicLong 类型的index ,并通过index.incrementAndGet 方法产生新的索引,因为原子类的相关操作都具备“原子性”,所以这样做是线程安全的。
最后,为了方便返回统一样式的结果,并JSON化,这里创建了一个辅助的实体类Result :
package cn.icexmoon.my_first_app.model;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
@Data
public class Result {
private boolean success = true;
private String msg = "";
private Object data = null;
@Override
public String toString(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("success",success);
jsonObject.put("msg",msg);
jsonObject.put("data",data);
return jsonObject.toString();
}
}
这里引入了一个阿里开发的处理JSON的中间件[fastjson ](alibaba/fastjson: A fast JSON parser/generator for Java. (github.com)),所以需要添加依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
测试
Spring Boot可以使用测试框架编写单元测试代码来进行测试,但我更喜欢使用HTTP客户端测试,这样更直观。
这里使用的测试用HTTP客户端工具是Apipost。
老实说这个乱七八糟的界面是需要习惯好一会的…
接口调用方式也比较简单,具体可以参考我的ApiPost项目。
一个简单的Web应用就完成了,我们在下一篇介绍如何使用数据库,谢谢阅读。
参考资料
|