一、Spring Boot 请求处理
1、请求处理流程
Spring Boot 的接口请求处理流程主要基于 Spring MVC 架构,以下是详细的请求处理流程:
-
客户端发送请求:客户端发送HTTP请求到Spring Boot应用的URL。
-
DispatcherServlet 接收请求:Spring Boot应用中的DispatcherServlet拦截所有的请求。
-
HandlerMapping 进行映射:DispatcherServlet 通过 HandlerMapping 找到处理请求的 Controller。
-
Controller 方法处理请求:Controller 中的相应方法处理请求,并调用适当的业务逻辑,返回数据或者视图名。
-
数据绑定与验证:如果请求中包含数据,Spring Boot 将数据绑定到相应的参数上,并执行验证(如果有的话),验证失败会产生相应的错误。
-
调用 Service 层:Controller 方法通常会调用 Service 层的方法来执行业务逻辑。
-
返回数据或视图:Controller 方法处理完请求后,返回数据或者视图名给 DispatcherServlet。
-
ViewResolver 解析视图:如果返回的是视图名,DispatcherServlet 会使用 ViewResolver 来解析视图名,得到具体的视图对象。
-
视图渲染:视图对象将模型数据填充到视图中,生成最终的响应结果。
-
响应返回给客户端:DispatcherServlet 将最终的响应返回给客户端,请求处理完成。
需要注意的是,Spring Boot 的自动配置大大简化了这个流程,大部分情况下,开发者只需要专注于编写 Controller 和相应的业务逻辑,其他的请求处理流程由 Spring Boot 自动完成。
2、处理请求的相关注解
在Spring Boot中,用于处理请求的相关注解包括但不限于以下几种:
-
@Controller:用于标识控制器类,处理HTTP请求。
-
@RestController:类似于@Controller,但是其方法默认返回JSON格式的数据,常用于构建RESTful风格的服务。
-
@RequestMapping:用于映射HTTP请求到控制器的处理方法,并可以定义请求的URL路径、HTTP方法等。
-
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping:这些注解分别用于标识处理GET、POST、PUT、DELETE请求的方法,并可以指定URL路径。
-
@RequestParam:用于从请求中获取参数值,常用于处理HTTP请求中的查询参数。
-
@PathVariable:用于从URL路径中获取参数值,常用于RESTful风格的请求。
-
@RequestBody:用于将请求体中的数据绑定到方法的参数上,常用于处理POST请求中的请求体数据。
-
@ResponseBody:用于将方法的返回值直接作为HTTP响应体返回给客户端。
-
@ModelAttribute:用于将请求参数绑定到一个对象上,并将该对象添加到模型中传递给视图。
-
@Valid、@Validated:用于执行Bean验证,通常与JSR-303/JSR-380规范的验证注解一起使用。
-
@ExceptionHandler:用于捕获处理方法中抛出的异常,可以定义全局异常处理方法或特定异常的处理方法。
-
@ControllerAdvice:用于定义全局控制器的建言(增强)类,可以在其中定义全局异常处理方法、全局数据绑定等。
这些注解是Spring Boot中处理请求时最常用的注解,通过它们可以方便地定义控制器类和处理方法,并实现与请求相关的业务逻辑。
3、序列化与反序列化
Http请求处理流程实际上就是数据的序列化与反序列化的过程,如下图所示:
二、自定义返回类型处理(HttpMessageConverter)
下面以自定Excel文件下载为例,自定义请求处理类 XlsHttpMessageConverter
1、新建返回类型 XlsResult
@Data
public class XlsResult<T> {
// 文件名称
private String filename;
// 表格 sheet名称
private String sheetName;
// 数据列表
private List<T> data;
// 数据类型
private Class<T> type;
}
1、新建自定义返回类型处理器 XlsHttpMessageConverter
1)XlsHttpMessageConverter
继承 AbstractHttpMessageConverter
,并传入返回值泛型 XlsResult
2)实现构造函数,传入Excel媒体类型
3)实现supports
方法,判断返回类型
4)实现writeInternal
写入数据方法,使用EasyExcel
写入数据到输出流
5)添加注解@Component
,会在AbstractMessageConverterMethodProcessor#messageConverters
列表的第一个位置添加该自定义处理类XlsHttpMessageConverter
@Component
public class XlsHttpMessageConverter extends AbstractHttpMessageConverter<XlsResult<Object>> {
private static final MediaType mediaType = MediaType.valueOf("application/vnd.ms-excel;charset=UTF-8");
public XlsHttpMessageConverter() {
super(mediaType);
}
@Override
protected boolean supports(Class<?> clazz) {
return clazz == XlsResult.class;
}
@Override
protected XlsResult<Object> readInternal(Class<? extends XlsResult<Object>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
protected void writeInternal(XlsResult<Object> objectXlsResult, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
EasyExcel.write(outputMessage.getBody(), objectXlsResult.getType())
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
.registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
.sheet(objectXlsResult.getSheetName()).doWrite(objectXlsResult.getData());
}
}
2、添加Controller 请求方法,并注明 produces
这里以导出用户数据为例。
1)从数据库中查询用户数据列表
2)返回类型 XlsResult<UserRespVO>
3)设置 produces = {"application/vnd.ms-excel"}
在请求返回时,
AbstractHttpMessageConverter.canWrite
同时判断了supports
和mediaType
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canWrite(mediaType); }
@GetMapping(value = "/exportList", produces = {"application/vnd.ms-excel"})
@Operation(summary = "导出用户")
public XlsResult<UserRespVO> exportList(@Validated UserPageReqVO exportReqVO,
HttpServletResponse response) {
XlsResult<UserRespVO> xlsResult = new XlsResult<>();
xlsResult.setFilename("用户数据.xls");
xlsResult.setSheetName("用户列表");
xlsResult.setType(UserRespVO.class);
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
// 从数据库查询用户列表
List<UserRespVO> list = userService.getUserPage(exportReqVO).getList();
xlsResult.setData(UserConvert.INSTANCE.convertList(list, deptMap));
return xlsResult;
}
3、发送测试请求
打开 Postman,点击 Send and Download
如果出现文件下载框,说明处理正常!