Spring Boot2.0之十 使用自定义注解、Json序列化器实现自动转换字典类型字段

前言

项目中经常需要后端将字典类型字段值的中文名称返回给前端。通过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

相关推荐
余槐i30 分钟前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8261 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林2 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne3 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF3 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li4 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
省钱兄--zs4 小时前
深圳24小时自助健身房软硬件解决方案实战指南与开发流程详解
java·数据仓库·spring boot·小程序·架构
upala4 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna4 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
码事漫谈4 小时前
因为太想讨好你,所以AI会骗你
后端