# 简介

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

# 实现细节

# 需要的 jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 版本根据需求调整 -->
<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>

# 定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 "";

}

# 注解处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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 来定位属性的对应字典名称。请根据自己的业务逻辑进行修改。