SpringBoot 项目的分层#
SpringBoot 框架项目一般分为五层:
-
View 层:向用户展示页面
-
Controller 层:前后端交互层,接收前端请求,调用 Service 层中的方法,接收 Service 层返回的数据并将其返回到前端。
-
Service 层:存放业务处理的逻辑,以及一些操作数据库的接口
-
Mapper 层:也可以成为 DAO 层,是数据库 CRUD 的接口,只有方法名,具体实现在 mapper.xml 文件中,对数据库进行数据持久化操作
-
Entity 层:存放实体类,与数据库中的属性基本保持一致。
实现一个最简单的 POST 接口#
一个最简单的 POST 接口,只需要在 Controller 层中声明一个 RestController 类,随后在类中使用 PostMapping 注解,表明这是一个 POST 接口,并填写相关方法即可,如下所示:
@RestController
@RequestMapping("/api")
public class CreateNumberController {
@PostMapping("/number")
public String createNumber(@RequestParam String param1, @RequestParam int param2) {
// 处理POST请求的代码
return "success";
}
}
运行程序后,使用 Postman 向http://localhost:8000/api/number
发送 POST 请求,输入相应请求参数,得到接口响应:
将接口连接事务处理逻辑函数#
实际业务中的接口肯定不仅仅是如同上面的示例那么简单的一个函数,程序内部对于接收到的数据会有一定的逻辑处理。
接下来列出一个实际的业务场景:
需求#
现在需要一个接口用于按照一定规则为项目赋予项目编码,项目请求内容为 xml 报文,例如:
<?xml version="1.0" encoding="UTF-8"?> <name>Example Project</name> <operate>01</operate> <source>A</source> <status>01</status>
当请求参数 operate 是 1 时为新增项目编码,请求参数 source 是 A、B、C 时,项目编码为 Axxxx,“xxxx” 为四位包含「0~9 的 10 个阿拉伯数字」和「A~Z 的 26 个大写英文字母」共同组成的流水码次序为:0/1/2/3/4/5/6/7/8/9/A/B/C/D~/X/Y/Z,增加 2 条约束:
①第 5 位、第 4 位与第 3 位从 000 开始取值,第 2 位从 B 开始取值,即初始编码为:AB000;第 5 位至第 2 位,每一位的取值范围为「0~9 的 10 个阿拉伯数字」和「A~Z 的 24 个大写英文字母(26 个字母中去除 I 和 O)」的 34 个字符,顺序依次为 0/1/2/3/4/5/6/7/8/9/A/B/C/D~/X/Y/Z,此时 “xxxx” 为 34 进制的 4 位数。第 5 位 0 至 Z 循环一周期后即向第 4 位进 1,第 4 位循环一周期后即向第 3 位进 1,第 3 位循环一周期后向第 2 位进 1。
②第 2 位除了 I 和 O 之外,A、C、Z 也不可取,循环取值从 B 开始至 A 结束,依次为 B/D…G/H/J/K…M/N/P…X/Y/0/1…9 共计 31 个字符。
要求返回值为 xml 报文,形式为:
<?xml version="1.0" encoding="UTF-8"?> <number>AB000</number> <status>01</status>
由于暂未规定如何校验重复编码(这个需要用到数据库查询,在接下来的部分继续补充),因此可以将需求简单理解为:接收一个 xml 报文,按照规则生成一个编码,返回编码。
这时接口的输入和输出都是 xml 报文,仅仅依靠简单的 Controller 已经不能满足需求了,需要将输入报文进行处理拆分对应的参数,依靠 Service 对输入数据进行处理,使用函数生成符合要求的输出,并最终封装成 xml 报文返回。
POST 接口接收 XML 报文#
通常来说,在进行接口设计时思考问题是自顶向下的,即 “接口可以接收并解析请求体→调用 Service 使用请求体中的数据运行业务逻辑→将得到的结果封装完成响应”。但实际的开发步骤是:根据需求明确请求体和响应体包括哪些属性以及这些属性的数据类型,构造请求类与响应类→新增 Service 类编写请求类的处理逻辑和响应类的生成逻辑→新增 Controller 类添加 POST 接口可以接收 XML 报文并在经过 Service 处理后返回 XML 报文。
导入相关依赖#
处理 xml 报文需要 Gradle 导入 jackson-dataformat-xml 依赖,为保证项目的可迁移性与可复用性,放弃引入 Lombok。
在 Maven 仓库(例如https://mvnrepository.com/)中搜索 jackson-dataformat-xml 依赖,选择 Gradle,将下面这行复制进 build.gradle 的 dependencies 部分:
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.2'
Jackson 库会自动将 XML 元素映射到同名的 Java 属性。
创建 Request 类和 Response 类#
根据请求报文,在 Controller 文件夹中新增 ProjectNumberRequest 类和 ProjectNumberResponse 类,分别如下:
ProjectNumberRequest.java
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
public class ProjectNumberRequest {
private String name;
private String operate;
private String source;
private String status;
//get方法
public String getName() {
return this.name;
}
public String getOperate() {
return this.operate;
}
public String getSource() {
return this.source;
}
public String getStatus() {
return this.status;
}
//set方法
public void setName(String projectName) {
this.name = projectName;
}
public void setOperate(String projectOperate) {
this.operate = projectOperate;
}
public void setSource(String projectSource) {
this.source = projectSource;
}
public void setStatus(String projectStatus) {
this.status = projectStatus;
}
}
ProjectNumberResponse.java
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "response")
public class ProjectNumberResponse {
private String number;
private String status;
//get方法
public String getNumber() {
return this.number;
}
public String getStatus() {
return this.status;
}
//set方法
public void setNumber(String number) {
this.number = number;
}
public void setStatus(String projectStatus) {
this.status = projectStatus;
}
}
注意:诸如@JacksonXmlRootElement(localName = "response")
的注解可以将 XML 的根节点重命名为 response,如果不添加注解则 XML 文件根节点名称默认使用类名。
编写业务逻辑#
在 Service 文件夹下新增 CreateNumberService 类,用于处理请求报文的请求体,并按照一定规则生成项目编码和项目状态这两个属性,将其转换为 XML 文件,组成响应报文的响应体。
CreateNumberService 包括两部分,第一部分是 response 对象生成函数,用于创建响应体对象,第二部分是 XML 对象生成函数,用于将 response 对象封装成 XML 对象。
CreateNumberService.java
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.stereotype.Service;
import studio.tsukistar.demo.Controller.ProjectNumberRequest;
import studio.tsukistar.demo.Controller.ProjectNumberResponse;
import java.util.Objects;
@Service
public class CreateNumberService {
private static final String XML_HEAD = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
public String createService(ProjectNumberRequest request) { //构建response对象
ProjectNumberResponse response = new ProjectNumberResponse();
response.setNumber( "AB000" );
response.setStatus(request.getStatus());
return javaBeanToXml (response);
}
public static String javaBeanToXml(Object obj) { //将Object转换为xml形式
String xml= "";
if (Objects.isNull(obj)) {
return xml;
}
try {
XmlMapper xmlMapper = new XmlMapper();
xml = xmlMapper.writeValueAsString(obj);
} catch (Exception e) {
return "";
}
return XML_HEAD + xml;
}
}
编写控制器#
在 Controller 文件夹下新增 CreateNumberController 类,设置请求体传入类型为 XML 格式,返回经过 CreateNumberService 处理请求体后得到的响应报文。
CreateNumberController.java
import jakarta.annotation.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import studio.tsukistar.demo.Service.CreateNumberService;
@RestController
@RequestMapping("/api")
public class CreateNumberController {
@Resource
private CreateNumberService numberService;
@PostMapping(value="/number", produces = MediaType.APPLICATION_XML_VALUE)
public String createNumber(@RequestBody ProjectNumberRequest request) { // 处理POST请求的代码
return numberService.createService(request);
}
}
运行测试#
运行程序后,使用 Postman 向http://localhost:8000/api/number
发送 POST 请求,在 “Body” 中选择 “raw-XML”,填入请求体:
<?xml version="1.0" encoding="UTF-8" ?>
<request>
<name>测试</name>
<operate>01</operate>
<source>A</source>
<status>01</status>
</request>
点击 “Send” 可以得到接口的响应报文,如下图所示:
总结#
相较于上次,这段时间新学习了 POST 接口的开发方式、项目分层、各层之间的交互等内容,对 SpringBoot 框架也有了更深入的了解(也许?希望不要误入歧途)。代码也一并传到了 github 仓库中,向前迈进了一大步。
本文多数开发知识来源于 Bing AI,若有错误之处请各位 dalao 多多赐教,不胜感激!