解决问题:java返回对象中关于枚举、字典数据的自动转化。
实现思路:
1、通过自定义注解 对需要转化的字段进行标记,注解中可定义枚举类型,若没有定义枚举则从数据字典获取。
2、自定义对象的BeanSerializerModifier,对做了标记的字段设置自定义的JsonSerializer。
3、自定义JsonSerializer的实现。
4、自定义MappingJackson2HttpMessageConverter,并设置自定义的BeanSerializerModifier为默认处理方式。
5、将自定义的MappingJackson2HttpMessageConverter加入到HttpMessageConverters中,可以通过重写WebMvcConfigurationSupport.extendMessageConverters的方式实现。
以下是具体的代码实现,有些地方需要用户根据实际情况自己实现,比如从字典获取数据等。
自定义注解标记
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dict {
static enum Void {}
Class<? extends Enum<?>> enumType() default Void.class;
/**
* 默认值,获取不到字典则使用默认值
*
* @return ""
*/
String defaultValue() default "";
}
自定义BeanSerializerModifier,以下代码中DictConstants.getDictCacheKey(valueStr)只是自定义key,需要自己实现。
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.hg.dcm.commons.core.HGBusinessException;
import com.hg.dcm.commons.core.SpringContextUtil;
import com.hg.energy.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
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 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 字典描述
*/
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 provider Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) 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);
}
/**
* 获取字典信息
* TODO
*
* @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
RedisService redis = SpringContextUtil.getBean(RedisService.class);
if (redis.exists(key)) {
return redis.get(key);
}
// 数据库字典操作类
//redis.setEx(key, desc, 1, TimeUnit.HOURS);
return "字典数据";
} 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 HGBusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(KEY_METHODS)));
}
Method descMethod = this.getMethod(et, DESC_METHODS);
if (descMethod == null) {
throw new HGBusinessException(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 HGBusinessException(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;
}
}
}
自定义MappingJackson2HttpMessageConverter
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
builder.modules(simpleModule);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
将自定义的MappingJackson2HttpMessageConverter加入到HttpMessageConverters中
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,mappingJackson2HttpMessageConverter());
}
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
builder.modules(simpleModule);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
}