# 简介

在项目中经常有一些类型字段用数字或者字母保存,但是前端展示时,则需要转换为用户能够理解的文字。于是公司内有人写了一个公用的注解,利用 java 的反射机制,修改字段的值或者添加属性来实现字典转换。由于 java 是静态类型语言,类在编译时固定,所以无法动态添加属性。于是他的解决方法是先把对象转为 map 再添加属性,或者在定义对象时先预定义字典的属于名称。我在使用中发现如果转换为 map(也可能是拦截处理的时机不对)则会丢失字段上的其它注解,如 swagger 文档定义、时间格式化等等。预设字典属性名称毕竟又增加了一道工序,操作起来也比较繁琐。于是我就想到了在返回阶段利用 jackson 的序列化步骤来对属性进行字典转换。json 对象类似 map 都是键值对,不用担心增减属性的问题。序列化时其它注解也已经处理完成,不会造成影响。

# 实现细节

# 需要的 jar
<!-- 版本根据需求调整 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.15.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version> 
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.15.2</version>
</dependency>
# 定义注解
public enum DicHandleStrategy {
    replace,
    add;
    private DicHandleStrategy() {
    }
}
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = JsonDicHandle.class)
public @interface JsonDicField {
    /**
     * 业务表名
     * @return
     */
    String tableName() default "";
    /**
     * 业务字段名
     * @return
     */
    String fieldName() default "";
    /**
     * 默认增加一个对应属性名加_dic 的新属性(如 type_dic)* newFieldName 为空的情况下
     * @return
     */
    DicHandleStrategy strategy() default DicHandleStrategy.add;
    /**
     * 字典属性名称(如不指定,并且 strategy 为 add 的情况下则字典名称默认为原属性加_dic)
     * @return
     */
    String newFieldName() default "";
}
# 注解处理
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Timer;
import java.util.TimerTask;
public class SpringContextUtil implements ApplicationContextAware {
	private static ApplicationContext applicationContext;
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringContextUtil.applicationContext = applicationContext;
	}
	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}
	public static Object getBean(String name) throws BeansException {
		return applicationContext.getBean(name);
	}
	public static Object getBean(String name, Class<?> requiredType) throws BeansException {
		return applicationContext.getBean(name, requiredType);
	}
	public static Object getBean(Class<?> requiredType) throws BeansException {
		return applicationContext.getBean(requiredType);
	}
	public static boolean containsBean(String name) {
		return applicationContext.containsBean(name);
	}
	public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
		return applicationContext.isSingleton(name);
	}
	public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
		return applicationContext.getType(name);
	}
	public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
		return applicationContext.getAliases(name);
	}
}
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.ys.szygl.util.SpringContextUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class JsonDicHandle extends JsonSerializer<Object> implements ContextualSerializer {
    final String DIC_FIELD_SUFFIX = "_dic";
    String tableName = "";
    String fieldName = "";
    DicHandleStrategy strategy = DicHandleStrategy.add;
    String newFieldName = "";
    String propertyName = "";
    static final IDicService iDicService = (IDicService)SpringContextUtil.getBean(IDicService.class);
    public JsonDicHandle() {
    }
    public JsonDicHandle(String tableName, String fieldName, DicHandleStrategy strategy, String newFieldName, String propertyName) {
        this.tableName = tableName;
        this.fieldName = fieldName;
        this.strategy = strategy;
        this.newFieldName = newFieldName;
        this.propertyName = propertyName;
    }
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (StringUtils.isBlank(this.newFieldName) && StringUtils.isNotBlank(this.propertyName)) {
            this.newFieldName = this.propertyName + DIC_FIELD_SUFFIX;
        }
        String dicValue = null;
        if (Objects.nonNull(value)) {
            dicValue = this.getDicValue(this.tableName, this.fieldName, String.valueOf(value));
            if (DicHandleStrategy.replace.name().equals(this.strategy.name())) {
                gen.writeObject(dicValue);
            } else {
                gen.writeObject(value);
            }
        } else {
            gen.writeObject(value);
        }
        if (DicHandleStrategy.add.name().equals(this.strategy.name()) && StringUtils.isNotBlank(this.newFieldName)) {
            gen.writeStringField(this.newFieldName, dicValue);
        }
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        JsonDicField annotation = property.getAnnotation(JsonDicField.class);
        if (Objects.nonNull(annotation)) {
            return new JsonDicHandle(annotation.tableName(), annotation.fieldName(), annotation.strategy(),
                    annotation.newFieldName(), property.getName());
        }
        return new JsonDicHandle();
    }
    /**
     * 把值转为字典值
     * @param value
     * @return
     */
    private String getDicValue(String tableName, String fieldName, String value) {
        String dicValue = null;
        if (Objects.nonNull(iDicService)) {
            Map<String, String> dicMap = this.iDicService.getDicMapFromCatch(tableName, fieldName);
            if (MapUtils.isNotEmpty(dicMap)) {
                List<String> dicValues = Arrays.stream(value.split(",")).map(s -> {
                    return Optional.ofNullable(dicMap.get(s)).map(String::valueOf).orElse("");
                }).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(dicValues)) {
                    dicValue = String.join(",", dicValues);
                }
            }
        }
        return dicValue;
    }
}
# 其它

IDicService 是自己的字典缓存服务,利用 tableName 和 fieldName 来定位属性的对应字典名称。请根据自己的业务逻辑进行修改。