springboot 字典翻译


本文主要介绍两种在项目中用到的字典翻译方式:

一、基于注解和jackson序列化实现字典翻译

添加字典,指定自定义的序列化类

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

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  //让此注解可以被Jackson扫描到
@JsonSerialize(using = DictSerializer.class) //配置处理此注解的序列化处理类
public @interface Dict {

    /**
     * 数据code
     * @return 返回类型: String
     */
    String dicCode();

    /**
     * 数据Text
     * @return 返回类型: String
     */
    String dicText() default "";

    /**
     * 数据字典表
     * @return 返回类型: String
     */
    String dictTable() default "";
}


创建自定义序列化类继承JsonSerialize实现ContextualSerializer,实现返回结果转译:


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.jeeplus.common.utils.RedisUtils;
import com.jeeplus.common.utils.SpringContextHolder;
import com.jeeplus.modules.sys.service.DictTypeService;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {

    private transient String dicCode;
    private transient String dicText;
    private transient String dicTable;
    private transient static RedisUtils redisUtils;
    public transient static String prefix = "dict";

    static {
        redisUtils = SpringContextHolder.getBean(RedisUtils.class);
    }

    protected DictSerializer() {
        super(Object.class);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty) {
        Dict dict = beanProperty.getAnnotation(Dict.class);
        return createContextual(dict.dicCode(), dict.dicText(), dict.dictTable());
    }

    private JsonSerializer<?> createContextual(String dicCode, String dicText, String dictTable) {
        DictSerializer serializer = new DictSerializer();
        serializer.setDicCode(dicCode);
        serializer.setDicText(dicText);
        serializer.setDicTable(dictTable);
        return serializer;
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) {

        String dictCode = getDicCode();
        if (StrUtil.isBlank(dictCode)) {
            return;
        }
        if (Objects.isNull(value) || value.equals("")) {
            value = "";
        }
        try {
            String cacheKey;
            if (StrUtil.isNotEmpty(dicTable)) {
                cacheKey = String.join("::", prefix, String.join("_", dicTable, dictCode, dicText, String.valueOf(value)));
            } else {
                cacheKey = String.join("::", prefix, dictCode);
            }

            Map<Object, Object> cacheDictMap = redisUtils.hGetAll(cacheKey);
            String label;
            if (CollUtil.isEmpty(cacheDictMap)) {
                Map<String, String> dictMap = translateDictValue(dictCode, dicText, dicTable, String.valueOf(value));
                redisUtils.hPutAll(cacheKey, dictMap);
                // 通过数据字典类型和value获取name
                label = dictMap != null ? dictMap.get(value.toString()) : null;
            } else {
                // 通过数据字典类型和value获取name
                label = cacheDictMap.get(value.toString()) != null ? cacheDictMap.get(value.toString()).toString() : null;
            }

            gen.writeObject(value);
            // 在需要转换的字段上添加@Dict注解,注明需要引用的code,后端会在返回值中增加filedName_dictText的key,前端只需要取对应的 filedName_dictText 就可以直接使用
            gen.writeFieldName(gen.getOutputContext().getCurrentName() + "_dictText");
            gen.writeObject(label);

        } catch (Exception e) {
            log.error("错误信息:{}", e.getMessage(), e);
        }
    }


    private Map<String, String> translateDictValue(String code, String text, String table, String value) {
        if (StrUtil.isBlank(code)) {
            return null;
        }

        Map<String, String> map = new HashMap<>();
        if (StrUtil.isNotEmpty(table)) {
            String textValue = SpringContextHolder.getBean(DictTypeService.class).translateDictFromTable(table, text, code, value);
            map.put(value, textValue);
        } else {
            List<Map<String, String>> tmpValue = SpringContextHolder.getBean(DictTypeService.class).translateDict(code);
            if (CollUtil.isNotEmpty(tmpValue)) {
                for (Map<String, String> stringMap : tmpValue) {
                    map.put(stringMap.get("value"), stringMap.get("label"));
                }
            }
        }

        return map;
    }


    public String getDicCode() {
        return dicCode;
    }

    public void setDicCode(String dicCode) {
        this.dicCode = dicCode;
    }

    public String getDicText() {
        return dicText;
    }

    public void setDicText(String dicText) {
        this.dicText = dicText;
    }

    public String getDicTable() {
        return dicTable;
    }

    public void setDicTable(String dicTable) {
        this.dicTable = dicTable;
    }
}

translateDictValue将查询到的字典保存到map中,因为序列化是需要每个对象都进行序列话操作,如果返回的是集合的话,就会进行很多次序列化操作,此时就需要对相同类型的字典进行缓存。

总结: 相比于使用 aop 切面的方式,使用序列化的方式能更好的处理嵌套对象的字典翻译

二、 基于注解和aop实现字典翻译

添加字典

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)
public @interface Dict {

    /**
     * 数据code
     * @return 返回类型: String
     */
    String dicCode();

    /**
     * 数据Text
     * @return 返回类型: String
     */
    String dicText() default "";

    /**
     * 数据字典表
     * @return 返回类型: String
     */
    String dictTable() default "";
}



定义切面类。具体的实现逻辑根据自己的项目实际情况实现就可以,主要是要用反射去获取类中的字段属性



@Aspect
@Component
@Slf4j
public class DictAspect {

    /**
     * 定义切点Pointcut
     */
    @Pointcut("execution(public * com.xxxx.*..*.*Controller.*(..))")
    public void dictPoint() {
    }

    @Around("dictPoint()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long time1 = System.currentTimeMillis();
        Object result = pjp.proceed();
        long time2 = System.currentTimeMillis();
        log.debug("获取JSON数据 耗时:" + (time2 - time1) + "ms");
        long start = System.currentTimeMillis();
        result = this.parseDictText(result);
        long end = System.currentTimeMillis();
        log.debug("注入字典到JSON数据  耗时" + (end - start) + "ms");
        return result;
    }


    private Object parseDictText(Object result) {

       
        return result;
    }


    /**
     * 检测返回结果集中是否包含Dict注解
     *
     * @param records
     * @return
     */
    private Boolean checkHasDict(List<Object> records) {
        if (CollectionUtil.isNotEmpty(records)) {
            for (Field field : getAllFields(records.get(0))) {
                if (!Objects.isNull(field.getAnnotation(Dict.class))) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 获取类的所有属性,包括父类
     *
     * @param object
     * @return
     */
    public static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值