前言
项目中经常需要后端将字典类型字段值的中文名称返回给前端。通过sql中关联字典表或者自定义函数不仅影响性能还不能使用mybatisplus自带的查询方法,所以推荐使用自定义注解、Json序列化器,Spring的缓存功能实现自动转换字典类型字段。以下实现SpringBoot版本为2.6.13。
一、自定义字典注解
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.common.utils.DictSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
定义一个自定义注解,用于标识需要进行字典自动翻译的字段。
**/
@Target({ElementType.FIELD}) //表示它只能应用在类的字段上。
@Retention(RetentionPolicy.RUNTIME) //确保注解在运行时可用。
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializer.class)//指定使用DictSerializer来处理被注解字段的序列化,不加@Dict注解的字段不会被DictSerializer处理。
public @interface Dict {
/**
* 字典代码
*/
String type() default "";
/**
* 字段后缀
*/
String suffix() default "Name";
}
二、自定义Json序列化器
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.SneakyThrows;
import java.io.IOException;
import java.util.Objects;
/**
@Dict注解中引入了JsonSerializer,加了@Dict注解的字段的序列化会被DictSerializer处理。不能在DictSerializer 类上加@JsonComponent注解或者自定义配置类 JacksonConfig来注册Json序列化器,使用注解或配置类注册序列化器会使其全局生效,任何字段的序列化会被DictSerializer处理。
**/
public class DictSerializer extends JsonSerializer<Object> {
@SneakyThrows
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
// 写入原字段的值
gen.writeObject(value);
if (Objects.nonNull(value)) {
// 获取数据字典项服务实例
// 不使用全局变量和在构造函数中获取Bean,延迟加载Bean,避免构造函数初始化空指针问题
DictService dictService = SpringContextHolder.getBean(SysDictItemService.class);
// 序列化字段名称
String fieldName = gen.getOutputContext().getCurrentName();
// 字典项注解对象
Dict dictAnno = gen.getCurrentValue().getClass()
.getDeclaredField(fieldName)
.getAnnotation(Dict.class);
String dictItemName = dictService.getDictItemName(dictAnno.type(), value);
// 写入新字段名称
gen.writeFieldName(fieldName + dictAnno.suffix());
// 写入新字段的值
gen.writeString(dictItemName);
}
}
}
三、Spring上下文工具类
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext ctx) {
context = ctx;
}
}
三、字典服务层(含缓存)
1.SpringBoot启动类上加@EnableCaching注解开启缓存功能
2.根据字典代码和字典项代码查询字典项名称
/**
* @Cacheable为SpringBoot自带的缓存注解,使用字典代码+字典项代码作为缓存的key,使用该注解会自动
* 缓存getDictItemName方法的返回值。
*/
@Cacheable(value = "dictCache", key = "#dictCode+':'+#code")
@Override
public String getDictItemName(String dictCode, Object code) {
List<SysDictItem> itemList = ......;
String value = null;
if(CollectionUtils.isNotEmpty(itemList)){
value = itemList.get(0).getItemName();
}
return Optional.ofNullable(value).orElse(code.toString());
}
3.新增或修改字典项
/**
* @CachePut注解是SpringBoot自带的缓存注解,使用该注解在更新字典时会自动更新字典缓存,注意此处
* 的key要与getDictItemName方法@Cacheable注解中的key一致,saveOrUpdateDictItem方法必须要返回要
* 缓存的内容,即字典项名称。
*/
@CachePut(value = "dictCache", key = "#dictItem.dictCode+':'+#dictItem.itemCode")
@Override
public String saveOrUpdateDictItem(SysDictItem dictItem) {
String itemName = null;
int con;
if(StringUtils.isBlank(dictItem.getItemId())){
//新增
con = baseMapper.insert(dictItem);
} else {
//修改
con = baseMapper.updateById(dictItem);
}
if(con > 0){
itemName = dictItem.getItemName();
}
return itemName;
}
参考资料:
1.百度DeepSeek-R1满血版搜索结果
2.csdn收藏(Springboot)中的https://blog.csdn.net/hangbingbihai/article/details/145452376?spm=1001.2014.3001.5506
https://blog.csdn.net/demo_yo/article/details/129157902?spm=1001.2014.3001.5506