java 使用注解自动转换字典信息(自定义注解转换字典)
文章目录java 使用注解自动转换字典信息1. 自定义Jackson2ObjectMapperBuilder2. 添加自定义注解处理类DictSerializerModifier3. 自定义注解4. 使用自定义注解:参考文档:java 使用注解自动转换字典信息需求:项目开发过程中经常会将一些常量定义到字典中,但是页面展示需要将字典值转换成字典名称;之前:提供字典查询接口,前段查询之后再回显;现在:
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.
*<p>
* 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} 字典操作对象;
* <pre>
* @Bean
* public Jackson2ObjectMapperBuilder objectMapperBuilder() {
* Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
* SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
* builder.modules(simpleModule);
* return builder;
* }
* </pre>
*
* @author milin
* @version V1.0
* @date 2021/12/15 17:39
*/
@Slf4j
public class DictSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> 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<Object> {
/**
* 生成序列化字段后缀
*/
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 <b>not</b> 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<? extends Enum<?>> 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<? extends Enum<?>> 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<? extends Enum<?>> 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<String, Class<? extends Enum<?>>> enumMap = new HashMap<>();
/**
* 枚举下拉列表缓存
*/
private static final Map<String, List<DictionaryResponseDTO>> 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<? extends Enum<?>> enumClass) {
enumMap.put(type, enumClass);
}
/**
* 查询枚举类型的下拉列表
*
* @param type 请求枚举类型
* @return 结果信息。转换失败会返回null
*/
public static List<DictionaryResponseDTO> queryEnumDictionary(String type) {
if (StringUtils.isBlank(type) || !enumMap.containsKey(type)) {
return null;
}
if (enumCache.containsKey(type)) {
return enumCache.get(type);
}
List<DictionaryResponseDTO> dictList = null;
Class<? extends Enum<?>> 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<? extends Enum<?>> 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)));
}
}
更多推荐
所有评论(0)