SpringBoot 自定义对象映射器的使用
SpringBoot 底层默认使用 Jackson 进行 Java 实体对象与 Json 之间的转换,有时转换的效果并不是我们所期望的,需要进行额外的处理工作,有经验的小伙伴们,肯定遇到过下面两种典型的情况:
- 当对象的属性是日期类型时,转换成 json 后的结果并不是我们想要的效果,还需要我们额外进行显示格式的处理。
- 当对象的属性是 BigInteger、Long 等数字类型时,如果数字很大或者位数比较长的话,返回给前端页面时,js 获取数据后会丢失精度,这就会给我们造成不必要的麻烦,我们还需要将其转换成字符串后返回给前端使用。
为了能够统一进行处理以上问题,我们可以在 SpringBoot 中自定义对象映射器,这样就能够简化代码开发。下面我们还是通过代码来说明具体的实现细节吧,在博客的最后,会提供源代码的下载。
一、搭建工程
搭建一个 SpringBoot 的 web 工程,工程结果如下:
首先看一下 pom 文件,主要引入了 lombok 包和 mybatis-plus-core 包。
引入 lombok 包的目的是可以简化实体对象的创建,只需要编写属性,不需要写对应的 get 和 set 方法,另外 lombok 也提供了日志记录的功能,只要在具体的类上面使用 @Slf4j 注解,就可以使用 log.info 、log.error 等相关方法进行记录日志,非常方便。
引入 mybatis-plus-core 包的目的,是使用其内置的雪花算法生成 19 位数字,作为实体对象的 id 主键。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<!--引入 lombok 简化对象的创建,以及简化日志记录-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--引入 Mybatis plus core 使用其内置的雪花算法为对象实体生成主键-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
对于 application.yml 的配置,这里只配置了启动端口为 8081
server:
port: 8081
创建了一个 Employee 的实体类,用于承载数据。
package com.jobs.demo.entity;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class Employee implements Serializable {
private Long id;
private String name;
//建议日期时间,使用 LocalDateTime,不要使用 Date
private LocalDateTime createTime;
}
为了方便演示,这里在 resources 的 static 目录下放置了静态页面,使用 Vue 和 ElementUI 进行开发,静态页面的具体细节不在这里展示,可以在本博客的最后面下载源代码进行查看。由于 SpringBoot 默认不支持静态资源的访问,因此必须配置静态资源的目录和访问路径,这里创建了一个 WebMvcConfig 的配置类进行配置和解决静态资源的访问,具体细节如下:
package com.jobs.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//设置静态资源目录,以及访问地址映射
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
}
为了接口返回结果的统一,自定义了一个 ResultVO 类,用于承载最终的结果数据,转换成 json 返回给前端。
package com.jobs.demo.common;
import lombok.Data;
@Data
public class ResultVO<T> {
//状态码,1 表示成功,0 表示失败
private Integer status;
//状态消息
private String msg;
//返回的数据对象
private T data;
//快捷返回成功
public static <T> ResultVO<T> success(T data) {
ResultVO<T> result = new ResultVO<>();
result.status = 1;
result.data = data;
result.msg = "success";
return result;
}
//快捷返回失败
public static <T> ResultVO<T> error(Integer status, String msg) {
ResultVO<T> result = new ResultVO<>();
result.status = status;
result.msg = msg;
return result;
}
}
最后我们创建一个 EmployeeController 类,编写一个接口
package com.jobs.demo.controller;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.jobs.demo.common.ResultVO;
import com.jobs.demo.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@Slf4j
@RequestMapping("/emp")
@RestController
public class EmployeeController {
@GetMapping("/{name}")
public ResultVO getEmployee(@PathVariable String name) {
//Mybatis plus core 内置的类,自带雪花算法函数。
DefaultIdentifierGenerator dig = new DefaultIdentifierGenerator();
Long id = dig.nextId(new Object());
//引入 lombok 包,在任何类上面使用 @Slf4j 注解后,就可以很方便的使用 log 对象的方法记录日志
log.info("后台生成的员工id为:" + id);
Employee emp = new Employee();
emp.setId(id);
emp.setName(name);
emp.setCreateTime(LocalDateTime.now());
return ResultVO.success(emp);
}
}
二、验证项目搭建成果
启动 SpringBoot 程序,打开浏览器访问 localhost:8081/index.html 输入员工姓名,获取结果如下图所示:
从上图中执行效果可以发现:
- Employee 对象的 id 属性是 Long 类型,使用雪花算法生成的 19 位数字作为值,后端接口转换成 json 是没有问题的,但是前端页面的 js 由于自身处理的精度不够,导致获取数据后展示的数字不正确,这是一个比较严重的问题,我们需要将 id 转换成字符串返回前端使用,才能解决此问题。
- Employee 对象的 createtime 是 LocalDateTime 日期时间类型,转换成 json 后,变成了数组,前端使用起来不方便,我们需要转换成中国人习惯使用的标准格式才比较方便阅读和使用。
下面我们就在 SpringBoot 中自定义对象映射器,来统一解决这个问题。
三、自定义对象映射器
只需要 2 步即可实现:
1 首先创建一个自定义类 JacksonObjectMapper 代码如下:
package com.jobs.demo.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//遇到未知属性时,不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在时,进行兼容性处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class,
new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class,
new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册模块,添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
上面将 BigInteger 和 Long 类型自动转换成 String 类型,另外对 3 中日期时间类型进行了序列化和反序列化的格式处理。
2 在上面创建的 WebMvcConfig 类中添加配置信息
完整的 WebMvcConfig 类的代码如下所示:
package com.jobs.demo.config;
import com.jobs.demo.common.JacksonObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//设置静态资源目录,以及访问地址映射
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
//扩展mvc框架的消息转换器
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建消息转换器对象
MappingJackson2HttpMessageConverter mc = new MappingJackson2HttpMessageConverter();
//设置自定义对象转换器,springboot 底层使用 Jackson 将 Java 对象转为 json
mc.setObjectMapper(new JacksonObjectMapper());
//将自定义的消息转换器对象添加到 mvc 框架的转换器集合中,顺序要靠前,否则不会生效
converters.add(0, mc);
}
}
3 再进行测试验证结果
最后我们重新运行 SpringBoot 程序,访问 localhost:8081/index.html 输入员工姓名,获取结果如下图所示:
本博客的源代码下载地址:https://files.cnblogs.com/files/blogs/699532/springboot_object_mapper.zip