Java分析讲解序列化与字典功能的序列化

2022-11-13 09:11:01 序列 字典 化与

两种解决方案

  • 前端查询字典数据然后前端转码
  • 后端查询字典值,然后再转码返回给前段。

本文及时针对方案2 进行的改进

目标:

在需要返回给前段的字段上添加指定的注解例如:@DictDesc 则根据该字段定义的值结合注解配置生成 xxxDesc字段并自动赋值为注解属性值所对应的字典描述;

具体使用的技术涉及到jackson序列化与反序列化,其他JSON工具包也类型的效果;

字典注解定义


@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@jsonSerialize(using = DictDescSerializer.class)
@JsonDeserialize(using = DictDescDeserializer.class)
public @interface DictDesc {
    
    Class<? extends Enum<? extends DictEnum>>[] enumType() default {};
    
    String[] dictType() default {};
    
    String defaultValue() default "";
    
    boolean throwException() default false;
}

该注解中定义了解析该注解需要序列化器与返序列化器:

@JsonSerialize(using = DictDescSerializer.class)

@JsonDeserialize(using = DictDescDeserializer.class)

字典序列化与返序列化器的实现

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.service.impl.DictDescSerializerUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@NoArgsConstructor
public class DictDescDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
    
    private static final String LABEL_SUFFIX = "Desc";
    
    private Class<?> rawClass;
    
    private ConversionService converter;
    
    private Method writeMethod;
    
    private DictDesc dict;
    public DictDescDeserializer(DictDesc dict, BeanProperty property) {
        this.dict = dict;
        this.rawClass = property.getType().getRawClass();
        this.converter = new DefaultConversionService();
        Class<?> targetClass = property.getMember().getDeclarinGClass();
        String writeField = property.getName() + LABEL_SUFFIX;
        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, writeField);
        this.writeMethod = Objects.isNull(propertyDescriptor) ? null : propertyDescriptor.getWriteMethod();
        if (Objects.isNull(this.writeMethod)) {
            log.info("类:{},字典属性:{},没有写入方法:{},不设置值!", targetClass.getName(), property.getName(), writeField);
        }
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        DictDesc dict = property.getAnnotation(DictDesc.class);
        if (dict != null) {
            return new DictDescDeserializer(dict, property);
        }
        return this;
    }
    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        Object result = this.getValue(p.getText());
        this.setDictDesc(result, p.getCurrentName(), p.getCurrentValue());
        return result;
    }
    
    public Object getValue(String value) throws IOException {
        return converter.convert(value, this.rawClass);
    }
    
    private void setDictDesc(Object result, String currentName, Object currentValue) {
        try {
            if (this.writeMethod != null) {
                writeMethod.invoke(currentValue, DictDescSerializerUtils.getDesc(this.dict, currentName, result));
            }
        } catch (Exception e) {
            log.error("类:{},字典属性:{},回显异常:{}", currentValue.getClass(), currentName, e.getMessage(), e);
        }
    }
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.service.impl.DictDescSerializerUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;

@Slf4j
@NoArgsConstructor
public class DictDescSerializer extends JsonSerializer<Object> implements ContextualSerializer {
    
    private static final String LABEL_SUFFIX = "Desc";
    
    private DictDesc dict;
    
    public DictDescSerializer(DictDesc dict) {
        this.dict = dict;
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        DictDesc dict = property.getAnnotation(DictDesc.class);
        if (dict != null) {
            return new DictDescSerializer(dict);
        }
        return this;
    }
    
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        provider.defaultSerializeValue(value, gen);
        if (dict != null) {
            String fieldName = gen.getOutputContext().getCurrentName();
            // 添加转换之后的字段:xxxDesc
            gen.writeStringField(fieldName.concat(LABEL_SUFFIX), DictDescSerializerUtils.getDesc(dict, fieldName, value));
        }
    }
}

字典序列化与反序列工具类

import cn.hutool.extra.spring.SpringUtil;
import com.aimilin.common.core.pojo.system.SysDict;
import com.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.annotation.DictEnum;
import com.aimilin.common.dict.exception.DictException;
import com.aimilin.common.dict.exception.enums.DictExceptionEnum;
import com.aimilin.common.dict.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@Slf4j
public class DictDescSerializerUtils {
    
    public static String getDesc(DictDesc dict, String field, Object value) {
        if (ArrayUtils.isEmpty(dict.dictType()) && ArrayUtils.isEmpty(dict.enumType())) {
            throw new DictException(DictExceptionEnum.REQUEST_DICT_TYPE, field);
        }
        try {
            if (value == null) {
                throw new DictException(DictExceptionEnum.REQUEST_NOT_NULL, field);
            }
            if (ArrayUtils.isNotEmpty(dict.enumType())) {
                return getEnumDesc(dict, field, value);
            }
            return getDictDesc(dict, field, value);
        } catch (Exception e) {
            log.error("字典转换异常, field:{}, enumType:{}, dictType:{}, 值:{}, 异常:{}",
                    field, dict.enumType(), dict.dictType(), value, e.getMessage(), e);
            if (dict.throwException()) {
                throw e instanceof DictException ? (DictException) e : new DictException(DictExceptionEnum.DICT_EXCEPTION, e);
            }
            return dict.defaultValue();
        }
    }
    
    public static String getEnumDesc(DictDesc dict, String field, Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        for (Class<? extends Enum<? extends DictEnum>> dictEnum : dict.enumType()) {
            Method getCode = dictEnum.getMethod("getCode");
            Method getMessage = dictEnum.getMethod("getMessage");
            for (Enum<? extends DictEnum> e : dictEnum.getEnumConstants()) {
                if (value.equals(getCode.invoke(e))) {
                    return Objects.toString(getMessage.invoke(e));
                }
            }
        }
        throw new DictException(DictExceptionEnum.UNKNOWN_ENUM_DICT_VALUE,
                String.fORMat("Field:%s, EnumType: %s, Value: %s", field, Arrays.toString(dict.enumType()), value));
    }
    
    public static String getDictDesc(DictDesc dict, String field, Object value) {
        if (ArrayUtils.isEmpty(dict.dictType())) {
            throw new DictException(DictExceptionEnum.REQUEST_DICT_TYPE, field);
        }
        List<SysDict> sysDictList = SpringUtil.getBean(SysDictService.class).getDictByDictTypeCode(dict.dictType());
        if (CollectionUtils.isEmpty(sysDictList)) {
            throw new DictException(DictExceptionEnum.NO_DICT_DATA, field, Arrays.toString(dict.dictType()));
        }
        for (SysDict sysDict : sysDictList) {
            if (StringUtils.equals(sysDict.getCode(), Objects.toString(value))) {
                return sysDict.getValue();
            }
        }
        throw new DictException(DictExceptionEnum.UNKNOWN_DICT_VALUE, field, Arrays.toString(dict.dictType()));
    }
}

字典转换服务类


public interface SysDictService {
    
    public List<SysDict> getDictByDictTypeCode(String... dictTypeCodes);
}

服务实现类:


@Service
public class SysDictServiceImpl implements SysDictService {
    @Resource
    private SystemContextServiceapi systemContextServiceApi;
    @Resource
    private SysDictCache sysDictCache;
    
    @Override
    public List<SysDict> getDictByDictTypeCode(String... dictTypeCodes) {
        List<SysDict> dictTypeCache = sysDictCache.getDictTypeCache(dictTypeCodes);
        if (CollectionUtils.isNotEmpty(dictTypeCache)) {
            return dictTypeCache;
        }
        return systemContextServiceApi.getDictByDictTypeCode(dictTypeCodes).getData();
    }
}

字典缓存服务

可以修改为使用本地缓存方式


@Slf4j
@Service
public class SysDictCache {
    @Resource
    private RedisService redisService;
    
    public List<SysDict> getDictTypeCache(String... dictTypes) {
        if (Objects.isNull(redisService)) {
            log.info("redisService 为空,不使用字典缓存");
            return null;
        }
        List<List<SysDict>> dictValues = redisService.getMultiCacheMapValue(CommonConstant.DICT_CACHE_KEY, Arrays.asList(dictTypes));
        if (CollectionUtils.isEmpty(dictValues)) {
            return null;
        }
        List<SysDict> result = new ArrayList<>();
        dictValues.stream().filter(Objects::nonNull).forEach(result::addAll);
        log.debug("查询字典缓存,dictTypes:{}, 结果:{}", dictTypes, result);
        return result;
    }
    
    public void cleanDictTypeCache(String... dictTypes) {
        if (Objects.isNull(redisService)) {
            return;
        }
        redisService.deleteCacheMapValue(CommonConstant.DICT_CACHE_KEY, dictTypes);
        log.info("清除字典缓存,dictTypes:{}", StringUtils.join(dictTypes));
    }
    
    public void putDictTypeCache(List<SysDict> sysDictList) {
        if (Objects.isNull(redisService) || CollectionUtils.isEmpty(sysDictList)) {
            return;
        }
        Map<String, List<SysDict>> collect = sysDictList.stream().collect(Collectors.groupingBy(SysDict::getTypeCode));
        for (Map.Entry<String, List<SysDict>> entry : collect.entrySet()) {
            redisService.setCacheMapValue(CommonConstant.DICT_CACHE_KEY, entry.geTKEy(), entry.getValue());
            log.info("设置字典缓存,dictType:{},结果:{}", entry.getKey(), entry.getValue());
        }
    }
}

到此这篇关于Java分析讲解序列化与字典功能的序列化的文章就介绍到这了,更多相关Java序列化内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章