1 对象处理转换

1.1 对象转换处理

1.1.1 概述

  • 在使用 SpringMVC 开发框架进行参数接收的时候,所有的请求参数都可以自动的转为 VO 类型,同时在进行 Handler 方法返回的时候也可以直接以 Restful 的形式返回 JSON 数据内容,SpringBoot 当然也支持类似的操作。

1.1.2 microboot-web 子模块

  • 本次将实现一个 Message 对象的数据转换。
  • 既然要进行数据的接收,那么必须先创建一个 VO 类:
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. import java.util.Date;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2022-01-05 09:32
  8. */
  9. @Data
  10. public class Message {
  11. /**
  12. * 字符串参数
  13. */
  14. private String title;
  15. /**
  16. * 日期参数
  17. */
  18. private Date publishDate;
  19. /**
  20. * 字符串参数
  21. */
  22. private String content;
  23. }

1.1.3 microboot 项目

  • 修改 build.gradle 配置文件,在公共模块中引入 spring-boot-starter-web 的依赖。
  1. project(':microboot-common') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation 'org.springframework.boot:spring-boot-starter-web' // 引入 SpringBoot 的 web 的依赖
  4. }
  5. }

1.1.4 microboot-common 子模块

  • 既然需要进行参数之间的字符串和日期之间的转换处理,最佳的做法就是定义一个公共的 Handler 父类,这种父类一般都不建议直接使用,所以都会通过 abstract 关键字进行定义。
  1. package com.github.fairy.era.common.web;
  2. import org.springframework.web.bind.WebDataBinder;
  3. import org.springframework.web.bind.annotation.InitBinder;
  4. import java.beans.PropertyEditorSupport;
  5. import java.time.LocalDateTime;
  6. import java.time.ZoneOffset;
  7. import java.time.format.DateTimeFormatter;
  8. import java.util.Date;
  9. /**
  10. * 定义一个公共的 Handler 类
  11. *
  12. * @author 许大仙
  13. * @version 1.0
  14. * @since 2022-01-05 09:45
  15. */
  16. public abstract class AbstractBaseHandler {
  17. private static final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  18. @InitBinder
  19. public void initBinder(WebDataBinder binder) {
  20. binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
  21. @Override
  22. public void setAsText(String text) throws IllegalArgumentException {
  23. LocalDateTime localDateTime = LocalDateTime.parse(text, df);
  24. super.setValue(new Date(localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli()));
  25. }
  26. });
  27. }
  28. }

1.1.5 microboot-common 子模块

  • 在项目搭建的时候在 Gradle 中引入了一个 spring-boot-gradle-plugin 插件,该插件是将已有的 Gradle 任务和 SpringBoot 的相关任务进行绑定,我们现在在 microboot-common 子模块中进行项目构建的时候表示当前项目是一个 SpringBoot 项目,但是这个模块实际上并不是 SpringBoot 的模块,所以自然就无法进行构建了,此时就需要修改当前模块中的 buid.gradle 配置文件,进行任务的禁用配置。
  1. plugins {
  2. id 'java'
  3. id 'idea'
  4. }
  5. jar {
  6. enabled = true // 允许当前的模块打包为 *.jar 文件
  7. }
  8. javadocTask {
  9. enabled = false // 不启用 javadoc 任务
  10. }
  11. javadocJar {
  12. enabled = false // 不启用 javadoc 的 *.jar 文件
  13. }
  14. bootJar {
  15. enabled = false // 不执行 SpringBoot 的打包任务
  16. }

1.1.6 microboot-web 子模块

  • 此时对于 microboot 项目的 build.gradle 配置文件来说已经在 web 子模块中引入了 common 子模块,所以可以直接进行类的引用。
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.common.web.AbstractBaseHandler;
  3. import com.github.fairy.era.vo.Message;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. /**
  9. * @author 许大仙
  10. * @since 2022-01-03 07-55
  11. */
  12. @RestController
  13. @RequestMapping("/message")
  14. @Slf4j
  15. public class MessageHandler extends AbstractBaseHandler {
  16. @GetMapping("/echo")
  17. public Message echo(Message message) {
  18. log.info("接收 msg 的请求参数,参数内容是 {}", message);
  19. message.setTitle("【ECHO】" + message.getTitle());
  20. message.setContent("【ECHO】" + message.getContent());
  21. return message;
  22. }
  23. }
  • 此时的返回是基于 Jackson 组件完成的,在实际开发中,不建议更换此组件。

1.1.6 浏览器

  • 通过浏览器发送请求路径,并进行请求内容的查看。
  • 请求如下:
  1. http://localhost:80/message/echo?title=许大仙&publishDate=2011-11-11 11:11:11&content=你大爷
  • 请求内容:

1.png

  • 此时的程序就可以按照 SpringMVC 的方式进行数据和对象的转换了,同时也基于 Restful 的风格实现了数据的响应。

1.2 整合 FastJSON 组件(不推荐)

1.2.1 Maven 仓库

  • 获取 FastJSON 组件的依赖:
  1. implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.79'

1.2.2 microboot 项目

  • 修改 build.gradle 配置文件,在 microboot-web 子模块中添加 FastJSON 组件的依赖:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. implementation 'org.springframework.boot:spring-boot-starter-web' // 引入 SpringBoot 的 web 的依赖
  5. /* fastjson */
  6. implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.79'
  7. }
  8. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  9. tasks.each { task ->
  10. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  11. task.enabled = false // 当前任务不执行
  12. }
  13. }
  14. }
  15. }

1.2.3 microboot-web 子模块

  • 如果要想将当前的转换组件替换为 FastJSON 组件,必须创建一个 WebMVC 的配置类。
  1. package com.github.fairy.era.config;
  2. import com.alibaba.fastjson.serializer.SerializerFeature;
  3. import com.alibaba.fastjson.support.config.FastJsonConfig;
  4. import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.http.MediaType;
  7. import org.springframework.http.converter.HttpMessageConverter;
  8. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  9. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /**
  13. * @author 许大仙
  14. * @version 1.0
  15. * @since 2022-01-05 10:38
  16. */
  17. @Configuration
  18. public class WebConfig implements WebMvcConfigurer {
  19. @Override
  20. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  21. // ① 默认在 SpringBoot 里面提供的是 Jackson 组件,需要移除掉
  22. for (int i = 0; i < converters.size(); i++) {
  23. if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
  24. converters.remove(i);
  25. }
  26. }
  27. // ② 项目之中一定需要提供有转换器,所以就可以将 FastJson 组件配置到项目中
  28. FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
  29. // ③ FastJson 在进行最终消息转换的时候实际上是需要进行相关配置定义的
  30. FastJsonConfig fastJsonconfig = new FastJsonConfig();
  31. fastJsonconfig.setSerializerFeatures(
  32. // 允许 Map 的内容为 null
  33. SerializerFeature.WriteMapNullValue,
  34. // 允许 List 集合为 null, 使用 [] 代替
  35. SerializerFeature.WriteNullListAsEmpty,
  36. // 允许 String 内容为空使用空字符串代替
  37. SerializerFeature.WriteNullStringAsEmpty,
  38. // 日期格式化输出
  39. SerializerFeature.WriteDateUseDateFormat,
  40. // 数字为空使用 0 表示
  41. SerializerFeature.WriteNullNumberAsZero,
  42. // 禁用循环引用
  43. SerializerFeature.DisableCircularReferenceDetect,
  44. SerializerFeature.PrettyFormat
  45. );
  46. // ④ 处理中文乱码问题
  47. List<MediaType> fastMediaTypes = new ArrayList<>();
  48. fastMediaTypes.add(MediaType.APPLICATION_JSON);
  49. fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
  50. // ⑤ 在convert中添加配置信息
  51. fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonconfig);
  52. // ⑥ 将 FastJson 添加到 converters 中
  53. converters.add(fastJsonHttpMessageConverter);
  54. }
  55. }
  • 给 Message 的日期属性加上 @JsonFormat 注解:
  1. package com.github.fairy.era.vo;
  2. import com.fasterxml.jackson.annotation.JsonFormat;
  3. import lombok.Data;
  4. import java.util.Date;
  5. /**
  6. * @author 许大仙
  7. * @version 1.0
  8. * @since 2022-01-05 09:32
  9. */
  10. @Data
  11. public class Message {
  12. /**
  13. * 字符串参数
  14. */
  15. private String title;
  16. /**
  17. * 日期参数
  18. */
  19. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  20. private Date publishDate;
  21. /**
  22. * 字符串参数
  23. */
  24. private String content;
  25. }

1.3 返回 XML 数据(不推荐)

1.3.1 概述

  • 在很多的传统项目之中可能不会大量的去使用 JSON ,而是会使用传统的 XML ,尤其是一些超大型的系统(遗留项目),在这些的环境之中,SpringBoot 里面如果要以 XML 形式进行返回也是支持的。

1.3.2 Maven 仓库

  • 如果想实现这样的转换,一般会使用 Jackson 组件来完成:
  1. implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.13.1'

1.3.3 microboot 项目

  • 修改 build.gradle 配置文件,在 microboot-web 子模块中添加相关依赖:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. implementation 'org.springframework.boot:spring-boot-starter-web' // 引入 SpringBoot 的 web 的依赖
  5. implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml' // jackson-dataformat-xml
  6. }
  7. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  8. tasks.each { task ->
  9. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  10. task.enabled = false // 当前任务不执行
  11. }
  12. }
  13. }
  14. }

1.3.4 microboot-web 子模块

  • 如果要想通过 Jackson 生成 XML 数据,最重要的一点就是修改 VO 类:
  1. package com.github.fairy.era.vo;
  2. import com.fasterxml.jackson.annotation.JsonFormat;
  3. import lombok.Data;
  4. import javax.xml.bind.annotation.XmlElement;
  5. import javax.xml.bind.annotation.XmlRootElement;
  6. import java.util.Date;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-05 09:32
  11. */
  12. @Data
  13. @XmlRootElement // 配置 XML 的根元素
  14. public class Message {
  15. /**
  16. * 字符串参数
  17. */
  18. @XmlElement
  19. private String title;
  20. /**
  21. * 日期参数
  22. */
  23. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  24. @XmlElement
  25. private Date publishDate;
  26. /**
  27. * 字符串参数
  28. */
  29. @XmlElement
  30. private String content;
  31. }
  • 修改 Handler 方法,表明返回的是 XML :
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.common.web.AbstractBaseHandler;
  3. import com.github.fairy.era.vo.Message;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. /**
  10. * @author 许大仙
  11. * @since 2022-01-03 07-55
  12. */
  13. @RestController
  14. @RequestMapping("/message")
  15. @Slf4j
  16. public class MessageHandler extends AbstractBaseHandler {
  17. // produces = MediaType.APPLICATION_XML_VALUE 表明返回的是 XML
  18. @GetMapping(value = "/echo", produces = MediaType.APPLICATION_XML_VALUE)
  19. public Message echo(Message message) {
  20. log.info("接收 msg 的请求参数,参数内容是 {}", message);
  21. message.setTitle("【ECHO】" + message.getTitle());
  22. message.setContent("【ECHO】" + message.getContent());
  23. return message;
  24. }
  25. }

1.3.5 浏览器

  • 通过浏览器发送请求路径,并进行请求内容的查看。
  • 请求如下:
  1. http://localhost:80/message/echo?title=许大仙&publishDate=2011-11-11 11:11:11&content=你大爷
  • 请求内容:

2.png

2 SpringBoot 数据响应

2.1 返回 PDF 数据

2.1.1 概述

  • 到现在为止,所有返回的实际最为常见的类型就是 Restful (JSON 结构)类型,但是随着各种应用技术的不断出现以及客户方要求的不断出现,所以在实际的开发之中一个 WEB 应用程序里面除了要有基础的 Restful 返回之外,实际上也会包含各种类型文件的响应,最为常见的就是 PDF 了。

2.1.2 PDF

  • PDF(Portable Document Format,可携带文档)是一种和操作系统无关的电子文件格式,可以有效支持多媒体集成信息,将文字、格式以及图像封装在一个文件中,同时也支持特长文件的存储,避免了不同的文本编辑器所造成的文本显示格式错乱的问题,在 Java 项目中 itextpdf 组件是比较常见的 PDF 创建工具。

2.1.3 Maven 仓库

  • 如果想让 SpringBoot 程序以 PDF 文件格式进行响应,那么就需要采用一个专属的生成组件—— itextpdf :
  1. implementation group: 'com.itextpdf', name: 'itextpdf', version: '5.5.13.2'

2.1.4 microboot 项目

  • 修改 build.gradle 配置文件,在 microboot-web 子模块中添加相关依赖:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. implementation 'org.springframework.boot:spring-boot-starter-web' // 引入 SpringBoot 的 web 的依赖
  5. implementation group: 'com.itextpdf', name: 'itextpdf', version: '5.5.13.2'// PDF
  6. }
  7. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  8. tasks.each { task ->
  9. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  10. task.enabled = false // 当前任务不执行
  11. }
  12. }
  13. }
  14. }

2.1.5 microboot-web 子模块

  • 如果要生成 PDF 文件,那么一般都需要设置字体库,如果在 Windows 中,可以直接引入 Windows 中的字体库,但是如果现在项目迁移到 Linux 中,那么字体库可能就不一样了,所以最佳的做法就是将可能用到的 PDF 生成的字体库放在项目的资源路径 src/main/resources 路径下,在此目录中新建一个 fonts 的子目录,保存字体文件。

3.png

  • 本次可以考虑将一个图片设置到 PDF 之中,所以这个图片也是应该保存到 src/main/resources 目录中的 images 的子目录。

4.png

  • 创建一个专属 PDF 的 Handler :
  1. package com.github.fairy.era.web;
  2. import com.itextpdf.text.*;
  3. import com.itextpdf.text.pdf.BaseFont;
  4. import com.itextpdf.text.pdf.PdfPCell;
  5. import com.itextpdf.text.pdf.PdfPTable;
  6. import com.itextpdf.text.pdf.PdfWriter;
  7. import lombok.SneakyThrows;
  8. import org.springframework.core.io.ClassPathResource;
  9. import org.springframework.core.io.Resource;
  10. import org.springframework.stereotype.Controller;
  11. import org.springframework.web.bind.annotation.GetMapping;
  12. import org.springframework.web.bind.annotation.RequestMapping;
  13. import javax.servlet.http.HttpServletResponse;
  14. /**
  15. * @author 许大仙
  16. * @version 1.0
  17. * @since 2022-01-05 15:56
  18. */
  19. @Controller
  20. @RequestMapping("/pdf")
  21. public class PDFHandler {
  22. @GetMapping(value = "/createPDF")
  23. @SneakyThrows
  24. public void createPDF(HttpServletResponse response) {
  25. // 设置响应类型
  26. response.setHeader("Content-Type", "application/pdf");
  27. // 一般情况下将生产的 PDF 进行下载,所以此时需要强制性的开启下载,并且配置下载文件名称
  28. response.setHeader("Content-Disposition", "attachment;filename=test.pdf");
  29. // 下面开始使用 itextpdf 组件在内存之中形成一份生成的 PDF 文件
  30. // 设置页面大小
  31. Document document = new Document(PageSize.A4, 10, 10, 50, 20);
  32. // 获取 PDF 的输出流配置
  33. PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
  34. // 开始构建 PDF 的文档内容
  35. document.open();
  36. // 获取图片的资源
  37. Resource imageResource = new ClassPathResource("/images/beauty.jpg");
  38. // 通过指定的路径加载图片
  39. Image image = Image.getInstance(imageResource.getFile().getAbsolutePath());
  40. float scalePercentage = (72 / 300f) * 100.0f;
  41. image.scalePercent(scalePercentage, scalePercentage);
  42. image.setAlignment(Element.ALIGN_CENTER);
  43. // 追加图片到文档之中
  44. document.add(image);
  45. // 图片之后换三行输出文字信息
  46. document.add(new Paragraph("\n\n\n"));
  47. // 设置字体库
  48. Resource fontResource = new ClassPathResource("/fonts/Alibaba-PuHuiTi-Bold.ttf");
  49. BaseFont baseFont = BaseFont.createFont(fontResource.getFile().getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  50. // 定义要使用的字库
  51. Font font = new Font(baseFont);
  52. // 在 PDF 上开始绘制文本信息
  53. String[] titles = {"许大仙", "你大爷的", "牛逼"};
  54. String[] contents = {"许仙", "你大爷的", "牛逼"};
  55. // 定义表格
  56. PdfPTable table = new PdfPTable(2);
  57. for (int i = 0; i < titles.length; i++) {
  58. // 创建单元格
  59. PdfPCell cell = new PdfPCell();
  60. //设置水平居中
  61. cell.setHorizontalAlignment(Element.ALIGN_CENTER);
  62. //设置垂直居中
  63. cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
  64. // 输出文字定义
  65. cell.setPhrase(new Paragraph(titles[i], font));
  66. // 追加单元格
  67. table.addCell(cell);
  68. cell = new PdfPCell();
  69. //设置水平居中
  70. cell.setHorizontalAlignment(Element.ALIGN_CENTER);
  71. //设置垂直居中
  72. cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
  73. cell.setPhrase(new Paragraph(contents[i], font));
  74. // 追加单元格
  75. table.addCell(cell);
  76. }
  77. document.add(table);
  78. document.close();
  79. }
  80. }

2.2 返回 Excel 数据

2.2.1 概述

  • 在当今所谓的办公自动化处理的过程之中,Excel 表格文件已经变得非常重要了,在项目的运行过程之中,会由系统保存有大量的数据,所以下载的结果一般都愿意使用 *.xls*.xlsx 文件来描述。
  • 在 Java 开发领域之中有一个著名的 POI 开源组件,这个组件功能非常强大,可以专门处理处理各种 Excel 文件,在 SpringBoot 之中为了便于用户操作 Excel 文件,提供了 easypoi-spring-boot-starter 依赖库:
  1. implementation group: 'cn.afterturn', name: 'easypoi-spring-boot-starter', version: '4.4.0'

2.2.2 microboot 项目

  • 修改 build.gradle 配置文件,在 microboot-web 子模块中添加相关依赖:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. implementation 'org.springframework.boot:spring-boot-starter-web' // 引入 SpringBoot 的 web 的依赖
  5. implementation group: 'cn.afterturn', name: 'easypoi-spring-boot-starter', version: '4.4.0' // easypoi
  6. }
  7. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  8. tasks.each { task ->
  9. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  10. task.enabled = false // 当前任务不执行
  11. }
  12. }
  13. }
  14. }

2.2.3 microboot-web 子模块

  • 现在如果要生成 Excel 数据,那么这个数据可能有很多种来源:大数据采集、数据库或异构平台的信息,本次将利用一个固定的数据来演示 Excel 的生成,此时就需要修改 Message 类:
  1. package com.github.fairy.era.vo;
  2. import cn.afterturn.easypoi.excel.annotation.Excel;
  3. import com.fasterxml.jackson.annotation.JsonFormat;
  4. import lombok.Builder;
  5. import lombok.Data;
  6. import java.util.Date;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-05 09:32
  11. */
  12. @Builder
  13. @Data
  14. public class Message {
  15. @Excel(name = "信息的标题", orderNum = "0", width = 30) // 生成 Excel 表格列的配置
  16. private String title;
  17. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  18. @Excel(name = "信息的发布日期", format = "yyyy-MM-dd HH:mm:ss", orderNum = "1", width = 30)
  19. private Date publishDate;
  20. @Excel(name = "信息内容", orderNum = "2", width = 30)
  21. private String content;
  22. }
  • 定义 Handler 类,并结合 Message 类实现 Excel 的生成:
  1. package com.github.fairy.era.web;
  2. import cn.afterturn.easypoi.excel.ExcelExportUtil;
  3. import cn.afterturn.easypoi.excel.entity.ExportParams;
  4. import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
  5. import com.github.fairy.era.vo.Message;
  6. import lombok.SneakyThrows;
  7. import org.apache.poi.ss.usermodel.Workbook;
  8. import org.springframework.stereotype.Controller;
  9. import org.springframework.web.bind.annotation.GetMapping;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.util.ArrayList;
  13. import java.util.Date;
  14. import java.util.List;
  15. /**
  16. * @author 许大仙
  17. * @version 1.0
  18. * @since 2022-01-06 08:53
  19. */
  20. @Controller
  21. @RequestMapping(value = "/messageExcel")
  22. public class MessageExcelHandler {
  23. @SneakyThrows
  24. @GetMapping(value = "/createExcel")
  25. public void createExcel(HttpServletResponse response) {
  26. // 设置响应类型
  27. response.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  28. // 一般情况下将生产的 PDF 进行下载,所以此时需要强制性的开启下载,并且配置下载文件名称
  29. response.setHeader("Content-Disposition", "attachment;filename=test.xlsx");
  30. // 随后通过一些固定数据的来生成相应的 Excel 数据
  31. List<Message> messageList = new ArrayList<>();
  32. messageList.add(Message.builder().title("许大仙").publishDate(new Date()).content("你大爷").build());
  33. messageList.add(Message.builder().title("许小仙").publishDate(new Date()).content("你小爷").build());
  34. // 生成 Excel
  35. Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("消息管理", "最新消息", ExcelType.XSSF), Message.class, messageList);
  36. workbook.write(response.getOutputStream());
  37. }
  38. }

2.3 返回图像流(不推荐)

2.3.1 概述

  • 如果在进行前后端分离设计的时候,需要进行一些资源的加载,一般会有两种做法:
    • ① 直接通过远程的文件服务器进行资源的加载操作,如:FastDFS、minio、阿里云的 OSS 等(推荐)。
    • ② 通过程序来实现加载,这样可以方便的进行验证上的控制。
  • 程序在进行图像流返回的时候只需要将返回的类型设置为图片即可,但是如果要想进行返回处理的时候往往需需要追加一个转换的配置类,在 SpringBoot 里面最大的特点就是可以直接返回。

2.3.2 microboot-web 子模块

  • 创建 ImageHandler 程序类,这个类负责返回图像:
  1. package com.github.fairy.era.web;
  2. import lombok.SneakyThrows;
  3. import org.springframework.core.io.ClassPathResource;
  4. import org.springframework.http.MediaType;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import javax.imageio.ImageIO;
  9. import java.awt.image.BufferedImage;
  10. /**
  11. * @author 许大仙
  12. * @version 1.0
  13. * @since 2022-01-06 09:25
  14. */
  15. @RestController
  16. @RequestMapping(value = "/image")
  17. public class ImageHandler {
  18. @GetMapping(value = "/create", produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_GIF_VALUE})
  19. @SneakyThrows
  20. public BufferedImage createImage() {
  21. // 资源加载
  22. ClassPathResource resource = new ClassPathResource("/images/beauty.jpg");
  23. // 实现文件的加载
  24. return ImageIO.read(resource.getInputStream());
  25. }
  26. }
  • 按照 SpringBoot 原始的处理方式,此时如果返回的是一个类对象,则会基于 Jackson 将对象的信息转换为 JSON 数据,但是既然是一个图像肯定不能够以文本的方式显示,那么就需要追加一个新的转换器。
  1. package com.github.fairy.era.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.http.converter.BufferedImageHttpMessageConverter;
  4. import org.springframework.http.converter.HttpMessageConverter;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  6. import java.util.List;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 09:39
  11. */
  12. @Configuration
  13. public class MvcConfig implements WebMvcConfigurer {
  14. @Override
  15. public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
  16. converters.add(new BufferedImageHttpMessageConverter());
  17. }
  18. }

2.4 返回视频流(不推荐)

2.4.1 概述

  • 在实际的项目运行过程中,视频流也是可以通过程序的方式来实现控制的,在 SpringBoot 里面同样支持对视频流的控制。

2.4.2 microboot-web 子模块

  • 将视频拷贝到 src/main/resource 目录中的 videos 下:

5.png

  • 如果要想进行视频流的控制,那么首先就需要提供有一个视频资源的请求处理器。
  1. package com.github.fairy.era.config;
  2. import org.springframework.core.io.ClassPathResource;
  3. import org.springframework.core.io.Resource;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
  6. import javax.servlet.http.HttpServletRequest;
  7. import java.io.IOException;
  8. /**
  9. * @author 许大仙
  10. * @version 1.0
  11. * @since 2022-01-06 09:54
  12. */
  13. @Component
  14. public class VideoResourceHttpRequestHandler extends ResourceHttpRequestHandler {
  15. @Override
  16. protected Resource getResource(HttpServletRequest request) throws IOException {
  17. return new ClassPathResource("/videos/1.mp4");
  18. }
  19. }
  • 随后对该资源进行响应处理,定义一个专属的 Handler 类进行操作:
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.config.VideoResourceHttpRequestHandler;
  3. import lombok.NonNull;
  4. import lombok.RequiredArgsConstructor;
  5. import lombok.SneakyThrows;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. /**
  11. * @author 许大仙
  12. * @version 1.0
  13. * @since 2022-01-06 10:19
  14. */
  15. @RestController
  16. @RequiredArgsConstructor
  17. public class VideoHandler {
  18. @NonNull
  19. private VideoResourceHttpRequestHandler videoResourceHttpRequestHandler;
  20. @SneakyThrows
  21. @GetMapping(value = "/createVideo")
  22. public void createVideo(HttpServletRequest request, HttpServletResponse response) {
  23. this.videoResourceHttpRequestHandler.handleRequest(request, response);
  24. }
  25. }

2.5 文件下载

2.5.1 概述

  • 在之前所做的一切操作实际上都是操作一些专属的特殊的文件,比如:PDF、Excel、图像、视频等,但是在一些情况下可能只是要求用户进行文件的下载。

2.5.2 microboot-web 子模块

  • 将本次模拟强制性下载的文件保存在 src/main/resources/files 目录中。

6.png

  • 如果现在仅仅是要将文件直接进行下载操作,本质上非常的容易,只需要通过输出流完成即可。
  1. package com.github.fairy.era.web;
  2. import lombok.SneakyThrows;
  3. import org.springframework.core.io.ClassPathResource;
  4. import org.springframework.util.FileCopyUtils;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.servlet.http.HttpServletResponse;
  8. /**
  9. * @author 许大仙
  10. * @version 1.0
  11. * @since 2022-01-06 10:34
  12. */
  13. @RestController
  14. public class DownloadHandler {
  15. @GetMapping(value = "/download")
  16. @SneakyThrows
  17. public void download(HttpServletResponse response) {
  18. // 设置响应类型 强制下载
  19. response.setHeader("Content-Type", "application/force-download");
  20. // 一般情况下将生产的 PDF 进行下载,所以此时需要强制性的开启下载,并且配置下载文件名称
  21. response.setHeader("Content-Disposition", "attachment;filename=Kotlin.rar");
  22. ClassPathResource resource = new ClassPathResource("/files/Kotlin.rar");
  23. FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
  24. }
  25. }

3 属性注入管理

3.1 属性定义和注入

3.1.1 概述

  • 在 Spring开发框架为了便于 Bean 的配置支持,可以基于 properties 资源文件实现相关内容的定义,随后在程序之后基于 SPEL 表达式进行内容的配置,而现在使用到了 SpringBoot ,所以在 SpringBoot 里面也可以直接基于 application.yml 配置文件实现同样的属性定义。
  • 在 Spring 框架里面如果要进行一些配置,一般都是通过 *.xml 文件的方式完成的,但是在 SpringBoot里面不提倡使用 XML 配置文件的,所以所有的相关配置可以在 src/main/resource 目录中的 application.properties 或 application.yml 来实现配置,同等目录中 application.properties 的优先级高于 application.yml 。

3.1.2 microboot-web 子模块

  • 在 src/main/resources 目录下新建 application.yml 配置文件,内容如下:
  1. source: # 是一个自定义的 KEY 的名称
  2. mysql: jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC # 自定义信息(KEY = source.mysql ,value = 内容)
  3. message: 许大仙,厉害
  4. info: '{ hello: 123,world: 456 }'
  • 定义一个 SourceHandler 的处理类,通过该类实现资源的注入处理:
  1. package com.github.fairy.era.web;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. /**
  9. * @author 许大仙
  10. * @version 1.0
  11. * @since 2022-01-06 11:06
  12. */
  13. @RestController
  14. public class SourceHandler {
  15. @Value("${source.mysql}")
  16. private String mysql;
  17. @Value("#{'${source.message}'.split(',')}")
  18. private List<String> message;
  19. @Value("#{${source.info}}")
  20. private Map<String, Object> info;
  21. @GetMapping("/show")
  22. public Map<String, Object> show() {
  23. Map<String, Object> maps = new HashMap<>(16);
  24. maps.put("mysql", this.mysql);
  25. maps.put("message", this.message);
  26. maps.put("info", this.info);
  27. return maps;
  28. }
  29. }

3.1.3 浏览器

  • 通过浏览器来进行数据访问。
  • 请求如下:
  1. http://localhost/show
  • 请求内容:

7.png

3.2 @ConfigurationProperties 注解

3.2.1 概述

  • 在之前是通过 application.yml 配置所需要的资源信息,随后再利用 @Value 注解进行了资源的注入处理;但是除了这样的配置模式之外,实际上也可以采用一种 application.yml 和 Bean 属性之间的自动注入操作。

3.2.2 microboot 项目

  • 修改 buid.gradle 文件,在公共依赖中添加 spring-boot-configuration-processor :
  1. subprojects { // 子模块的配置
  2. dependencies { // 公共依赖管理
  3. implementation group: 'org.springframework.boot', name: 'spring-boot-devtools' // 允许进行项目的热部署
  4. testImplementation 'org.springframework.boot:spring-boot-starter-test' //引入 SpringBoot 的 test 的依赖
  5. // lombok
  6. compileOnly 'org.projectlombok:lombok' // 编译生效
  7. annotationProcessor 'org.projectlombok:lombok' // 注解生效
  8. testCompileOnly 'org.projectlombok:lombok' // 编译生效
  9. testAnnotationProcessor 'org.projectlombok:lombok' // 注解生效
  10. // spring-boot-configuration-processor
  11. annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
  12. }
  13. // 其余略
  14. }

3.2.3 microboot-web 子模块

  • 在 application.yml 中配置一些属性内容:
  1. person:
  2. name: 许大仙
  3. school:
  4. - 南京大学
  5. - 北京大学
  6. - 家里蹲大学
  7. age: 18
  8. message: [ '许大仙','厉害' ]
  9. courses:
  10. JavaBase: Java 基础从入门到入土》
  11. JavaWEB: JavaWEB 从入门到入土》
  12. Spring: Spring 从入门到入土》
  • 新建 Person 实体类,并将 application.yml 中配置的属性映射到实体类属性之中:
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. import java.util.List;
  6. import java.util.Map;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 13:46
  11. */
  12. @Data
  13. @Component
  14. @ConfigurationProperties(prefix = "person")
  15. public class Person {
  16. private String name;
  17. private List<String> school;
  18. private Integer age;
  19. private List<String> message;
  20. private Map<String, String> courses;
  21. }
  • 新建一个 Handler 类,注入 Person 的实例,并将 Person 的实例返回:
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.vo.Person;
  3. import lombok.NonNull;
  4. import lombok.RequiredArgsConstructor;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 13:51
  11. */
  12. @RestController
  13. @RequiredArgsConstructor
  14. public class PersonHandler {
  15. @NonNull
  16. private Person person;
  17. @GetMapping(value = "/person")
  18. public Person person() {
  19. return this.person;
  20. }
  21. }

3.2.4 浏览器

  • 通过浏览器来进行数据访问。
  • 请求如下:
  1. http://localhost/person
  • 请求内容:

8.png

3.3 注入级联对象数据

3.3.1 概述

  • 在之前进行处理的时候都是直接在 application.yml 里面配置了一些基本信息,而后要求类的属性名称和配置的属性名称要一致,随后才能实现注入的处理。
  • 本次将采用一个部门和雇员的对应信息,一个部门中会存在多个雇员信息,而一个雇员属于一个部门。

3.3.2 microboot-web 子模块

  • 定义 VO 类:
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2022-01-06 14:06
  7. */
  8. @Data
  9. public class Emp {
  10. private String id;
  11. private String name;
  12. private Double salary;
  13. }
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. import java.util.List;
  6. /**
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2022-01-06 14:04
  10. */
  11. @Data
  12. @ConfigurationProperties(prefix = "dept")
  13. @Component
  14. public class Dept {
  15. private String id;
  16. private String name;
  17. private String location;
  18. private List<Emp> emps;
  19. }
  • 修改 application.yml 文件,进行内容的注入配置:
  1. dept:
  2. id: 001
  3. name: 研发部
  4. location: 上海
  5. emps:
  6. - id: 001
  7. name: 张三
  8. salary: 5000
  9. - id: 002
  10. name: 李四
  11. salary: 6000
  • 创建一个 handler 类,显示 Dept 的属性信息:
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.vo.Dept;
  3. import lombok.NonNull;
  4. import lombok.RequiredArgsConstructor;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 14:13
  11. */
  12. @RestController
  13. @RequiredArgsConstructor
  14. public class DeptHandler {
  15. @NonNull
  16. private Dept dept;
  17. @GetMapping(value = "/dept")
  18. public Dept dept() {
  19. return this.dept;
  20. }
  21. }

3.3.3 浏览器

  • 通过浏览器来进行数据访问。
  • 请求如下:
  1. http://localhost/dept
  • 请求内容:

9.png

3.4 自定义注入配置文件(不推荐)

3.4.1 概述

  • 在之前如果要想进行一些类属性的配置,一般都是在 application.yml 配置文件中进行定义,但是如果将所有的配置项都定义在这里面会比较麻烦,因为整个 SpringBoot 项目里面会存在有大量的配置项。
  • 所以,此时可以考虑 properties 配置文件的特点,利用资源文件来实现属性内容的定义。

3.4.2 microboot-web 子模块

  • 新建 dept.properties 文件,内容如下:
  1. dept.id=001
  2. dept.name=研发部
  3. dept.location=上海
  4. dept.emps[0].id=001
  5. dept.emps[0].name=张三
  6. dept.emps[0].salary=5000
  7. dept.emps[1].id=002
  8. dept.emps[1].name=李四
  9. dept.emps[1].salary=6000
  • 定义 VO 类:
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2022-01-06 14:06
  7. */
  8. @Data
  9. public class Emp {
  10. private String id;
  11. private String name;
  12. private Double salary;
  13. }
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.context.annotation.PropertySource;
  5. import org.springframework.stereotype.Component;
  6. import java.util.List;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 14:04
  11. */
  12. @Data
  13. @PropertySource("classpath:/dept.properties")
  14. @ConfigurationProperties(prefix = "dept")
  15. @Component
  16. public class Dept {
  17. private String id;
  18. private String name;
  19. private String location;
  20. private List<Emp> emps;
  21. }
  • 创建一个 handler 类,显示 Dept 的属性信息:
  1. package com.github.fairy.era.web;
  2. import com.github.fairy.era.vo.Dept;
  3. import lombok.NonNull;
  4. import lombok.RequiredArgsConstructor;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-06 14:13
  11. */
  12. @RestController
  13. @RequiredArgsConstructor
  14. public class DeptHandler {
  15. @NonNull
  16. private Dept dept;
  17. @GetMapping(value = "/dept")
  18. public Dept dept() {
  19. return this.dept;
  20. }
  21. }

3.4.3 浏览器

  • 通过浏览器来进行数据访问。
  • 请求如下:
  1. http://localhost/dept
  • 请求内容:

9.png