java 使用注解自动转换字典信息(自定义注解转换字典)

您所在的位置:网站首页 数据字典转换成m文件 java 使用注解自动转换字典信息(自定义注解转换字典)

java 使用注解自动转换字典信息(自定义注解转换字典)

2023-11-27 19:10| 来源: 网络整理| 查看: 265

java 使用注解自动转换字典信息

需求:项目开发过程中经常会将一些常量定义到字典中,但是页面展示需要将字典值转换成字典名称; 之前:提供字典查询接口,前段查询之后再回显; 现在:直接在需要转换的字段添加注解,自动添加转换后的字段

采用解决方案:在ResponseBody转换成json的时候处理添加自定义注解的字段; 对象属性上添加注入如:

// 自定义jackson 属性序列化注解,支持配置字典组名称或者使用枚举类型 @Dict private String status;

使用Jackson 将对象转换成Json字符串时,会自动添加 statusDesc 字段;

{ "name": "zhagnsan", "status": "enable", // 这个字段为字典转换自动生成的 "statusDesc": "启动" } 1. 自定义Jackson2ObjectMapperBuilder

自定义Jackson2ObjectMapperBuilder 修改jackson默认解析行为,添加自定义注解字段处理规则;

@Bean public Jackson2ObjectMapperBuilder objectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier()); builder.modules(simpleModule); return builder; } 2. 添加自定义注解处理类DictSerializerModifier

思路:继承Jdk8BeanSerializerModifier处理类,重写modifySerializer方法,实现自定义注解处理;

jackson中:ContextualSerializer说明

/** * Add-on interface that {@link JsonSerializer}s can implement to get a callback * that can be used to create contextual instances of serializer to use for * handling properties of supported type. This can be useful * for serializers that can be configured by annotations, or should otherwise * have differing behavior depending on what kind of property is being serialized. *

* Note that in cases where serializer needs both contextualization and * resolution -- that is, implements both this interface and {@link ResolvableSerializer} * -- resolution via {@link ResolvableSerializer} occurs first, and contextual * resolution (via this interface) later on. */ public interface ContextualSerializer{ public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException; }

大概意思:JsonSerializer 附加接口,主要用于序列化上下文实例,对于有注解的属性非常实用;特别注意需要配合ResolvableSerializer接口一起使用用于解析有注解的字段;

import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; /** * 字典序列化 * 使用方式: * 在需要字典转换的javabean 属性上添加 @Dict 注解,则在使用Jackson生成json时,会自动添加已转换字典名称的属性 xxxDesc * 1. 需要扫描到 {@link ApplicationContextHolder}; * 2. 需要注入{@link RedisTemplate} 客户端; * 3. 需要注入{@link DictionaryExport} 字典操作对象; * * @Bean * public Jackson2ObjectMapperBuilder objectMapperBuilder() { * Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); * SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier()); * builder.modules(simpleModule); * return builder; * } * * * @author milin * @version V1.0 * @date 2021/12/15 17:39 */ @Slf4j public class DictSerializerModifier extends BeanSerializerModifier { @Override public List changeProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) { for (BeanPropertyWriter beanProperty : beanProperties) { Dict dict = beanProperty.getAnnotation(Dict.class); if (dict != null) { beanProperty.assignSerializer(new DictSerializer(dict)); } } return beanProperties; } /** * 字典自定义序列化 */ static class DictSerializer extends JsonSerializer { /** * 生成序列化字段后缀 */ private static final String LABEL_SUFFIX = "Desc"; /** * 字典配置信息 */ private final Dict dict; /** * 枚举获取key方法 */ private static final String[] KEY_METHODS = {"getValue", "getCode", "getStatus", "name"}; /** * 获取枚举描述方法 */ private static final String[] DESC_METHODS = {"getDesc"}; /** * 构造方法 * * @param dict 字典描述 * @param jsonSerializer 默认序列化对象 */ public DictSerializer(Dict dict) { this.dict = dict; } /** * 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 serializers Provider that can be used to get serializers for * serializing Objects value contains, if any. */ @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { provider.defaultSerializeValue(value, gen); // 添加转换之后的字段:xxxDesc String fieldName = gen.getOutputContext().getCurrentName(); gen.writeStringField(fieldName.concat(LABEL_SUFFIX), value != null ? this.getDesc(dict, value) : null); } /** * 获取字典信息 * * @param dict 字典对象 * @param value 字典值 * @return */ private String getDesc(Dict dict, Object value) { try { // 先查询是否是枚举类型,查到则返回 String enumDesc = this.getEnumDesc(dict, value); if (enumDesc != null) { return enumDesc; } String valueStr = Objects.toString(value); //获取缓存key,可以自定义 String key = DictConstants.getDictCacheKey(valueStr); // Redis 缓存操作类 这里建议优先使用本地缓存, 本地缓存 -> redis -> Db RedisTemplate redis = ApplicationContextHolder.getBean(RedisTemplate.class); if (redis.exists(key)) { return redis.get(key); } // 数据库字典操作类 DictionaryExport dictExport = ApplicationContextHolder.getBean(DictionaryExport.class); String desc = dictExport.selectNameByEnumKey(valueStr, dict.defaultValue()); redis.setEx(key, desc, 1, TimeUnit.HOURS); return desc; } catch (Exception e) { log.error("字典转换:获取字典描述异常,使用默认值:{},key:{}, dict:{}, 异常:{}", dict.defaultValue(), value, dict.enumType(), e.getMessage(), e); return dict.defaultValue(); } } /** * 获取枚举类型的描述信息 * * @param dict 字典 * @param value 值 * @return 枚举desc字段 */ private String getEnumDesc(Dict dict, Object value) throws InvocationTargetException, IllegalAccessException { if (dict == null || value == null) { return null; } Class et = dict.enumType(); if (Dict.Void.class.equals(et)) { return null; } Enum[] enums = et.getEnumConstants(); Method keyMethod = this.getMethod(et, KEY_METHODS); if (keyMethod == null) { // 自定义业务异常 throw new BusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(KEY_METHODS))); } Method descMethod = this.getMethod(et, DESC_METHODS); if (descMethod == null) { throw new BusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(DESC_METHODS))); } for (Enum e : enums) { if (value.equals(keyMethod.invoke(e))) { return Objects.toString(descMethod.invoke(e)); } } log.error("字典转换:通过枚举转换失败,枚举:{},值:{},KeyMethod:{},DescMethod:{}", et.getName(), value, Arrays.toString(KEY_METHODS), Arrays.toString(DESC_METHODS)); throw new BusinessException(String.format("字典转换失败,枚举:%s,值:%s", et.getName(), value)); } /** * 获取读方法 * * @param enumType 枚举类 * @param methodNames 方法名称 * @return Method */ private Method getMethod(Class enumType, String... methodNames) { for (String methodName : methodNames) { try { return enumType.getMethod(methodName); } catch (NoSuchMethodException e) { } } return null; } } } /** *字典常量 */ public class DictConstants { /** * 字典缓存key前缀 */ public static final String JIM_DICT = "dict:enumKey_"; /** * 获取字典缓存Key * @param enumKey * @return */ public static String getDictCacheKey(String enumKey){ return JIM_DICT + enumKey; } }

有了上面的工具类配合自定义注解:@Dict 可以完成字典属性注入

3. 自定义注解 @Inherited @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Dict { static enum Void {} /** * 枚举类型的class * 取值:getValue, getCode, getStatus, name * 描述:getDesc * * @return 字典类型 */ Class enumType() default Void.class; /** * 默认值,获取不到字典则使用默认值 * * @return "" */ String defaultValue() default ""; } 4. 使用自定义注解: @Getter @Setter public class User{ private String name; @Dict private String status; }

使用上述自定义转换器会自动添加 ”statusDesc“ 字段,不需要手动添加字段; 另外也支持:枚举类型的自定义转换。 转换结果:

{ "name": "zhagnsan", "status": "enable", "statusDesc": "启动" }

(可选)在增加一个枚举转字典查询接口即可枚举和字典统一使用,再也不需要因为要回显需要在字典中添加枚举信息;

/** * 枚举类型的字典信息, 将枚举类型转换为字典列表类型 * * @author milin * @version V1.0 * @date 2021/12/16 11:23 */ @Slf4j public class DictEnumUtils { /** * 枚举配置信息 */ private static final Map enumMap = new HashMap(); /** * 枚举下拉列表缓存 */ private static final Map enumCache = new WeakHashMap(); /** * 枚举获取key方法 */ private static final String[] KEY_METHODS = {"getValue", "getCode", "getStatus", "name"}; /** * 获取枚举描述方法 */ private static final String[] DESC_METHODS = {"getDesc"}; /** * 注册枚举类型, * 指定的枚举获取key方法:"getValue", "getCode", "getStatus", "name" * 获取Desc方法:getDesc * * @param type 枚举type * @param enumClass 对应枚举类 */ public static void registerEnum(String type, Class enumClass) { enumMap.put(type, enumClass); } /** * 查询枚举类型的下拉列表 * * @param type 请求枚举类型 * @return 结果信息。转换失败会返回null */ public static List queryEnumDictionary(String type) { if (StringUtils.isBlank(type) || !enumMap.containsKey(type)) { return null; } if (enumCache.containsKey(type)) { return enumCache.get(type); } List dictList = null; Class enumClass = enumMap.get(type); try { Enum[] enums = enumClass.getEnumConstants(); Method keyMethod = getMethod(enumClass, KEY_METHODS); Method descMethod = getMethod(enumClass, DESC_METHODS); dictList = new ArrayList(enums.length); for (Enum e : enums) { dictList.add(convert(type, keyMethod.invoke(e), descMethod.invoke(e), e.ordinal())); } enumCache.put(type, dictList); } catch (Exception e) { log.error("枚举转字典列表,类型:{},枚举:{},异常:{}", type, enumClass.getName(), e.getMessage(), e); } return dictList; } /** * 枚举转换到字典 * * @return DictionaryResponseDTO 这个是自定义字典对象 */ private static DictionaryResponseDTO convert(String type, Object key, Object value, Integer ordinal) { DictionaryResponseDTO vo = new DictionaryResponseDTO(); vo.setName(String.valueOf(value)); vo.setEnumKey(String.valueOf(key)); vo.setValue(ordinal); vo.setType(type); return vo; } /** * 获取读方法 * * @param enumType 枚举类 * @param methodNames 方法名称 * @return Method */ private static Method getMethod(Class enumType, String... methodNames) throws NoSuchMethodException { for (String methodName : methodNames) { try { return enumType.getMethod(methodName); } catch (NoSuchMethodException e) { } } throw new NoSuchMethodException(String.format("枚举转字典列表,枚举:%s,没有找到方法:%s", enumType.getName(), Arrays.toString(methodNames))); } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3