前言
在我们开发过程中,我们从前端页面接收的数据字典一般都是key(大多数为数字),但我们在页面显示的时候,想用其value值。如果我们每使用一次就要去写一些重复的代码去查询,这样会使我们的代码非常冗余,那有什么办法可以让我们查询出key的同时,将value值也查询出来,此时我们可以使用自定义注解的方式去做字段映射。
一、添加依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.1</version>
</dependency>
二、创建自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDict {
String dicCode();
String dicText() default "";
String dictTable() default "";
}
三、创建JSON序列化配置
便于在序列化Java对象时为带有特定注解(如TestDict)的字段应用自定义序列化逻辑。
/**
* JSON序列化配置
* @author van
*/
@Configuration
public class JacksonConfig {
/**
* JacksonConfig配置一个自定义的ObjectMapper,
* 通过JacksonSerializerModifier扩展Jackson的序列化功能,
* 保证带TestDict注解的字段在序列化时触发自定义逻辑(由testFiler处理)。
*/
@Bean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
//初始化一个ObjectMapper,用于处理JSON序列化/反序列化,取消XML对JSON的支持。
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
//注册JacksonSerializerModifier,
//使所有使用该ObjectMapper的序列化操作都会应用JacksonSerializerModifier的逻辑
//(即为带有TestDict注解的字段绑定testFiler序列化器)。
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new JacksonSerializerModifier()));
return objectMapper;
}
}
四、创建JSON序列化处理器
将包含自定义序列化器的作用字段列表返回给Jackson,继续序列化整个对象。
/**
* JSON序列化处理器
* @author van
*/
public class JacksonSerializerModifier extends BeanSerializerModifier {
/**
*
* @param config Jackson的序列化配置,包含序列化规则。
* @param beanDesc 当前序列化的Java Bean的元信息(如类名、字段、注解)。
* @param beanProperties 当前Bean的字段列表,每个BeanPropertyWriter表示一个字段的序列化信息。
* @return 返回修改后的字段列表。
*/
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter beanProperty : beanProperties) {
// 数据字典翻译
//获取当前字段上的@TestDict注解。
TestDict testDict = beanProperty.getAnnotation(TestDict.class);
if (testDict != null){
TestFiler testFiler = new TestFiler(beanProperty.getName(), testDict.dicCode(), testDict.dictTable(), testDict.dicText());
//为当前字段绑定testFiler序列化器
beanProperty.assignSerializer(testFiler);
}
}
return beanProperties;
}
}
五、创建自定义序列化器
自定义序列化器,负责在序列化Java对象时处理带有@TestDict注解的字段。它输出字段的原始值(如title: "1"),并附加一个翻译后的字段(如title_dictText: "启用")。
@Data
public class TestFiler extends JsonSerializer<Object> {
private static final String DICT_APPEND = "_dictText";
private String fieldName;
private String dicCode;
private String dictTable;
private String dicText;
public TestFiler(String fieldName, String dicCode, String dictTable, String dicText){
this.fieldName = fieldName;
this.dicCode = dicCode;
this.dictTable = dictTable;
this.dicText = dicText;
}
/**
* 控制字段在JSON中的输出中,包括原始值和翻译值
* @param o 待序列化的字段值(如title的值"1")。
* @param jsonGenerator Jackson的JSON生成器,用于读取JSON内容
* @param serializerProvider 序列化上下文,提供配置信息(本方法未使用)
* @throws IOException
*/
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//获取映射字段的值
String key = o.toString();
//将原始字段值写入JSON
jsonGenerator.writeObject(o);
if (StringUtils.isNotBlank(key)){
//对指定的字段进行映射(这里可以去数据库里面通过key,去查询字典表中对应的value)
String value = "这里映射对应的字段的意思";
//将映射的字段和字段值序列化(输出翻译后的字段值)
jsonGenerator.writeObjectField(fieldName + DICT_APPEND , value);
}
}
}
六、进行测试验证
返回实体类(使用自定义注解,查看返回什么样的样式)
public class SurveyClientListRespDTO implements Serializable {
private String id;
@TestDict(dicCode = "123")
private String title;
}
创建接口(查看自定义注解的映射)
@RestController
@RequestMapping("/api/survey/survey")
public class SurveyController extends BaseController {
@PostMapping("/test")
public List<SurveyClientListRespDTO> minePaging() {
List<SurveyClientListRespDTO> list = new ArrayList<>();
SurveyClientListRespDTO surveyClientListRespDTO = new SurveyClientListRespDTO();
//查看1111会映射出来对应的value
surveyClientListRespDTO.setTitle("1111");
surveyClientListRespDTO.setId("第一个内容");
SurveyClientListRespDTO surveyClientListRespDTO1 = new SurveyClientListRespDTO();
surveyClientListRespDTO.setTitle("2222");
surveyClientListRespDTO.setId("第二个内容");
SurveyClientListRespDTO surveyClientListRespDTO2 = new SurveyClientListRespDTO();
surveyClientListRespDTO.setTitle("3333");
surveyClientListRespDTO.setId("第三个内容");
list.add(surveyClientListRespDTO);
list.add(surveyClientListRespDTO1);
list.add(surveyClientListRespDTO2);
return list;
}
}
七、返回结果
八、自定义注解的进阶使用方法
(1)调整自定义序列化器
@Data
@Log4j2
public class TestFiler extends JsonSerializer<Object> {
private static final String DICT_APPEND = "_dictText";
private String fieldName;
private String dicCode;
private String dictTable;
private String dicText;
public TestFiler(String fieldName, String dicCode, String dictTable, String dicText){
this.fieldName = fieldName;
this.dicCode = dicCode;
this.dictTable = dictTable;
this.dicText = dicText;
}
/**
* 获取字典业务类
*/
private SysDicValueService sysDicValueService = SpringUtils.getBean("sysDicValueServiceImpl", SysDicValueService.class);
/**
* 控制字段在JSON中的输出中,包括原始值和翻译值
* @param o 待序列化的字段值(如title的值"1")。
* @param jsonGenerator Jackson的JSON生成器,用于读取JSON内容
* @param serializerProvider 序列化上下文,提供配置信息(本方法未使用)
* @throws IOException
*/
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//获取映射字段的值
String key = o.toString();
//将原始字段值写入JSON
jsonGenerator.writeObject(o);
if (StringUtils.isNotBlank(key)){
//对指定的字段进行映射(这里可以去数据库里面通过key,去查询字典表中对应的value)
String value = "";
if (StringUtils.isEmpty(dictTable)) {
value = "这里映射对应的字段的意思";
} else {
value = sysDicValueService.findTableText(dictTable, dicText, dicCode, key);
}
//将映射的字段和字段值序列化(输出翻译后的字段值)
jsonGenerator.writeObjectField(fieldName + DICT_APPEND , value);
}
}
}
(2)创建字典表业务类
@Service
public class SysDicValueServiceImpl extends ServiceImpl<SysDicValueMapper, SysDicValue> implements SysDicValueService, ExcelDictService {
@Override
public String findTableText(String dicTable, String dicText, String dicCode, String value){
// 查询新数据
String text = baseMapper.findTableText(dicTable, dicText, dicCode, value);
if(StringUtils.isBlank(text)){
text = "";
}
return text;
}
}
public interface SysDicValueMapper extends BaseMapper<SysDicValue> {
/**
* 查找数据字典
* @param dicTable
* @param dicText
* @param dicCode
* @param value
* @return
*/
String findTableText(@Param("dicTable") String dicTable,
@Param("dicText") String dicText,
@Param("dicCode") String dicCode,
@Param("value") String value);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yf.system.modules.dict.mapper.SysDicValueMapper">
<select id="findTableText" resultType="String">
SELECT ${dicText} FROM ${dicTable} WHERE ${dicCode}='${value}' LIMIT 1
</select>
</mapper>
(3)进行测试验证
返回实体类
@Data
@ApiModel(value="问卷学员端列表响应类", description="问卷学员端列表响应类")
public class SurveyClientListRespDTO implements Serializable {
@ApiModelProperty(value = "ID", required=true)
private String id;
@ApiModelProperty(value = "问卷标题", required=true)
@TestDict(dicCode = "123")
private String title;
@TestDict(dicText = "title", dictTable = "el_sys_dic", dicCode = "id")
private String test;
}
创建接口(查看自定义注解的映射)
@RestController
@RequestMapping("/api/survey/survey")
public class SurveyController extends BaseController {
public List<SurveyClientListRespDTO> minePaging() {
List<SurveyClientListRespDTO> list = new ArrayList<>();
SurveyClientListRespDTO surveyClientListRespDTO = new SurveyClientListRespDTO();
//查看1111会映射出来对应的value
surveyClientListRespDTO.setTitle("1111");
surveyClientListRespDTO.setId("第一个内容");
//去对应表里面查询对应id的数据,返回id对应的title信息
surveyClientListRespDTO.setTest("1924786752627765250");
SurveyClientListRespDTO surveyClientListRespDTO1 = new SurveyClientListRespDTO();
surveyClientListRespDTO1.setTitle("2222");
surveyClientListRespDTO1.setId("第二个内容");
surveyClientListRespDTO1.setTest("1925359481378607106");
SurveyClientListRespDTO surveyClientListRespDTO2 = new SurveyClientListRespDTO();
surveyClientListRespDTO2.setTitle("3333");
surveyClientListRespDTO2.setId("第三个内容");
surveyClientListRespDTO2.setTest("1924770834002571266");
list.add(surveyClientListRespDTO);
list.add(surveyClientListRespDTO1);
list.add(surveyClientListRespDTO2);
return list;
}
}
数据库对应的数据
(4)测试结果
测试用的id的数据都是自己写死的,可以根据自己业务场景使用;
(@TestDict(dicText = "title", dictTable = "el_sys_dic", dicCode = "id"))
返回结果的test的值就是通过查询el_sys_dic表中id对应的数据,映射的值是数据中对应的title信息