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

网友投稿 366 2022-07-25


目录两种解决方案字典注解定义字典序列化与返序列化器的实现字典序列化与反序列工具类字典转换服务类字典缓存服务

两种解决方案

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

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

目标:

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

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

字典注解定义

/**

* 字典类型字段自动生成Desc描述字段

*/

@Inherited

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@JacksonAnnotationsInside

@JsonSerialize(using = DictDescSerializer.class)

@JsonDeserialize(using = DictDescDeserializer.class)

public @interface DictDesc {

/**

* 枚举类型的class

* 取值:getValue, getCode, getStatus, name

* 描述:getDesc

*

* @return 字典类型

*/

Class extends Enum extends DictEnum>>[] enumType() default {};

/**

* 字典类型分组

*

* @return 字典类型

*/

String[] dictType() default {};

/**

* 字典转换失败时默认值

*

* @return String 默认zlbNKGCFb值

*/

String defaultValue() default "";

/**

* 是否抛出异常,默认不抛出异常,返回默认值

*

* @return true 转换失败则抛出异常,false 异常返回默认值

*/

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;

/**

* 字典类型返序列化器

*

* @author liujunguang1

* @version V1.0

* @date 2022/5/20 21:08

*/

@Slf4j

@NoArgsConstructor

public class DictDescDeserializer extends JsonDeserializer 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;

}

/**

* 将数据类型转换为目标类型

*

* @param value 字符串值

* @return 目标类型值

* @throws IOException

*/

public Object getValue(String value) throws IOException {

return converter.convert(value, this.rawClass);

}

/**

* 设置字典会限制

*

* @param result 字典value

* @param currentName 当前属性名称

* @param currentValue 当前对象

*/

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;

/**

* 字典序列化器

*

* @author liujunguang1

* @version V1.0

* @date 2022/5/20 20:48

*/

@Slf4j

@NoArgsConstructor

public class DictDescSerializer extends JsonSerializer implements ContextualSerializer {

/**

* 生成序列化字段后缀

*/

private static final String LABEL_SUFFIX = "Desc";

/**

* 字典配置信息

*/

private DictDesc dict;

/**

* 构造方法

*

* @param 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;

}

/**

* Method that can be called to ask implementation to serialize

* values of type this serializer handles.

*

* @param value Value to serialize; can not be null.

* @param gen Generator used to output resulting Json content

* @param provider Provider that can be used to get serializers for

* http:// serializing Objects value contains, if any.

*/

@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;

/**

* 字典转换工具类

*

* @author liujunguang1

* @version V1.0

* @date 2022/5/20 23:19

*/

@Slf4j

public class DictDescSerializerUtils {

/**

* 获取字典信息

*

* @param dict 字典对象

* @param value 字典值

* @return

*/

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();

}

}

/**

* 获取枚举类型的描述信息

*

* @param dict 字典

* @param value 值

* @return 枚举desc字段

*/

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));

}

/**

* 获取字典中的值

*

* @param dict 字典注解

* @param value 属性值

* @return 字典名称

*/

public static String getDictDesc(DictDesc dict, String field, Object value) {

if (ArrayUtils.isEmpty(dict.dictType())) {

throw new DictException(DictExceptionEnum.REQUEST_DICT_TYPE, field);

}

List 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();

http:// }

}

throw new DictException(DictExceptionEnum.UNKNOWN_DICT_VALUE, field, Arrays.toString(dict.dictType()));

}

}

字典转换服务类

/**

* 字典服务类

*

* @author liujunguang1

* @version V1.0

* @date 2022/5/20 16:03

*/

public interface SysDictService {

/**

* 根据字典类型code获取字典列表

*

* @param dictTypeCodes 字典类型code

* @return List

*/

public List getDictByDictTypeCode(String... dictTypeCodes);

}

服务实现类:

/**

* 系统字典服务实现类

*

* @author liujunguang1

* @version V1.0

* @date 2022/5/20 16:13

*/

@Service

public class SysDictServiceImpl implements SysDictService {

@Resource

private SystemContextServiceApi systemContextServiceApi;

@Resource

private SysDictCache sysDictCache;

/**

* 根据字典类型编码获取字典数据

*

* @param dictTypeCodes 字典类型编码值

* @return List

*/

@Override

public List getDictByDictTypeCode(String... dictTypeCodes) {

List dictTypeCache = sysDictCache.getDictTypeCache(dictTypeCodes);

if (CollectionUtils.isNotEmpty(dictTypeCache)) {

return dictTypeCache;

}

return systemContextServiceApi.getDictByDictTypeCode(dictTypeCodes).getData();

}

}

字典缓存服务

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

/**

* 字典缓存服务

*

* @version V1.0

* @date 2022/5/19 12:13

*/

@Slf4j

@Service

public class SysDictCache {

@Resource

private RedisService redisService;

/**

* 获取字典类型缓存

*

* @param dictTypes 字典类型

* @return 字典列表

*/

public List getDictTypeCache(String... dictTypes) {

if (Objects.isNull(redisService)) {

log.info("redisService 为空,不使用字典缓存");

return null;

}

List> dictValues = redisService.getMultiCacheMapValue(CommonConstant.DICT_CACHE_KEY, Arrays.asList(dictTypes));

if (CollectionUtils.isEmpty(dictValues)) {

return null;

}

List result = new ArrayList<>();

dictValues.stream().filter(Objects::nonNull).forEach(result::addAll);

log.debug("查询字典缓存,dictTypes:{}, 结果:{}", dictTypes, result);

return result;

}

/**

* 清空字典类型缓存

*

* @param dictTypes 字典类型

*/

public void cleanDictTypeCache(String... dictTypes) {

if (Objects.isNull(redisService)) {

return;

}

redisService.deleteCacheMapValue(CommonConstant.DICT_CACHE_KEY, dictTypes);

log.info("清除字典缓存,dictTypes:{}", StringUtils.join(dictTypes));

}

/**

* 添加缓存

*

* @param sysDictList 系统字典列表

*/

public void putDictTypeCache(List sysDictList) {

if (Objects.isNull(redisService) || CollectionUtils.isEmpty(sysDictList)) {

return;

}

Map> collect = sysDictList.stream().collect(Collectors.groupingBy(SysDict::getTypeCode));

for (Map.Entry> entry : collect.entrySet()) {

redisService.setCacheMapValue(CommonConstant.DICT_CACHE_KEY, entry.getKey(), entry.getValue());

log.info("设置字典缓存,dictType:{},结果:{}", entry.getKey(), entry.getValue());

}

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring依赖注入(DI)两种方式的示例详解
下一篇:SpringBoot详细讲解多个配置文件的配置流程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~