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

相关推荐
奋斗的小方21 分钟前
Springboot基础篇(3):Bean管理
java·spring boot·后端
扣丁梦想家2 小时前
《Spring Boot + MySQL高性能应用实战:性能优化技巧与最佳实践》
spring boot·后端·mysql
正宗咸豆花2 小时前
【PromptCoder】使用 package.json 生成 cursorrules
人工智能·json·prompt·个人开发
Code额2 小时前
Springboot 自动化装配的原理
java·spring boot
夏天的味道٥2 小时前
36. Spring Boot 2.1.3.RELEASE 中实现监控信息可视化并添加邮件报警功能
java·spring boot·后端
bobz9652 小时前
ipsec vpn over kube-ovn eip
后端
程序员清风3 小时前
程序员最大的悲哀是什么?
java·后端·面试
计算机学长felix3 小时前
基于SpringBoot的“洪涝灾害应急信息管理系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
小杨4044 小时前
springboot框架四个基础核心三(actuator)
spring boot·后端·架构
yuhaiqiang4 小时前
分布式系统容错必杀技:从架构到代码,深度剖析分布式重试组件
后端