在接口响应时,有很多情况需要做数据转换,比如数据脱敏,null值给默认,数据类型转换...,那怎么避免在代码里写过多的 if 判断和处理逻辑呢?
Java 接口响应数据都是使用jackson作为序列化框架的,其中有一个对象
NopAnnotationIntrospector:字面意思 “注释内省器”
在jackson自定义builder的时候可以添加进去
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder
.annotationIntrospector(new ResultDataAnnotationIntrospector()) // 自定义内省器(只能加一个,后边加的会覆盖前边的)
.serializerByType(Long.class, new NumberToStringSerializer()) // 大数字转成字符串,避免前端js接口丢失精度
.serializerByType(BigDecimal.class, new NumberToStringSerializer()) // 浮点类型转成字符串,避免前端js接口丢失精度,统一小数位数
.serializerByType(LocalDateTime.class, localDateTimeserializer()) // 日期格式化输出
.serializerByType(Boolean.class, new BooleanToNumberSerializer()) // Boolean类型输出为数字
.serializerByType(IEnum.class, new EnumToNumberSerializer()) // 把枚举类输出为自定义的数据
.serializationInclusion(JsonInclude.Include.NON_NULL) // 自动去掉null值
;
}
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.util.EnumResolver;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
// 内省器
@Slf4j
public class ResultDataAnnotationIntrospector extends NopAnnotationIntrospector {
// 自定义的多个处理不同注解的内省器
private final List<AbstractResultValueIntrospector> introspectorList = new ArrayList<>();
// 空值时的处理
private final NullValueAnnotationIntrospector nullIntrospector = new NullValueAnnotationIntrospector();
// 循环处理一个属性上的多个注解
// 执行顺序是 按照List的添加先后排列顺序执行序列化
public ResultDataAnnotationIntrospector() {
super();
// introspectorList.add(new NullValueAnnotationIntrospector());
introspectorList.add(new MoneyAnnotationIntrospector());
introspectorList.add(new MaskingAnnotationIntrospector());
}
// “反序列化”时的处理
// 如果参数接收对象属性是枚举,但是传参是数字,jackson默认是枚举的下标数字,
// 但是系统里有自定义的Code值,所以转换成自己想要的数字匹配
@Override
public Object findDeserializer(Annotated am) {
if (am instanceof AnnotatedMethod) {
AnnotatedMethod annotatedMethod = (AnnotatedMethod) am;
if (annotatedMethod.getParameterCount() == 1) {
AnnotatedParameter parameter = annotatedMethod.getParameter(0);
JavaType type = parameter.getType();
if (type.isEnumType()) {
EnumResolver enumResolver = EnumResolver.constructUnsafe(type.getRawClass(), this);
return new IntegerToEnumDeserializer(enumResolver, false);
}
}
}
return super.findDeserializer(am);
}
// 序列化“非空值”时的处理
@Override
public Object findSerializer(Annotated am) {
Object serializer = null;
for (AbstractResultValueIntrospector introspector : introspectorList) {
Object newSerializer = introspector.findSerializer(am, (AbstractResultSerializer) serializer);
if (newSerializer != null) {
serializer = newSerializer;
}
}
return serializer;
}
// 序列化“空值”时的处理
@Override
public Object findNullSerializer(Annotated am) {
// 首先处理空值
Object serializer = nullIntrospector.findNullSerializer(am, null);
for (AbstractResultValueIntrospector introspector : introspectorList) {
// 处理完空值之后,后续的就不再是对空值的序列化了,所以使用“findSerializer”
Object newSerializer = introspector.findSerializer(am, (AbstractResultSerializer) serializer);
if (newSerializer != null) {
serializer = newSerializer;
}
}
return serializer;
}
}
import com.fasterxml.jackson.databind.introspect.Annotated;
import lombok.Getter;
// 抽象内省器
@Getter
public abstract class AbstractResultValueIntrospector {
public Object findSerializer(Annotated am, AbstractResultSerializer serializer) {
return null;
}
public Object findNullSerializer(Annotated am, AbstractResultSerializer serializer) {
return null;
}
}
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
// 抽象结果序列化处理器
public abstract class AbstractResultSerializer<T> extends StdScalarSerializer<T> {
// 属性上多个注解,链式处理序列化
protected final AbstractResultSerializer preSerializer;
protected AbstractResultSerializer(Class<T> t, AbstractResultSerializer preSerializer) {
super(t);
this.preSerializer = preSerializer;
}
protected AbstractResultSerializer(Class<?> t, boolean dummy, AbstractResultSerializer preSerializer) {
super(t, dummy);
this.preSerializer = preSerializer;
}
// 抽象方法,只处理值
protected abstract T serializeValue(T value);
}
示例:空值处理
// 处理空值序列化
@Slf4j
public class NullValueAnnotationIntrospector extends AbstractResultValueIntrospector {
@Override
public Object findNullSerializer(Annotated am, AbstractResultSerializer serializer) {
BooleanNullValue nullValue = am.getAnnotation(BooleanNullValue.class);
if (nullValue != null) {
return new NullValueSerializer<>(Boolean.class, nullValue.value(), nullValue.defaultNull(), serializer);
}
StringNullValue stringNullValue = am.getAnnotation(StringNullValue.class);
if (stringNullValue != null) {
return new NullValueSerializer<>(String.class, stringNullValue.value(), stringNullValue.defaultNull(), serializer);
}
NumberNullValue numberNullValue = am.getAnnotation(NumberNullValue.class);
if (numberNullValue != null) {
boolean defaultNull = numberNullValue.value().length == 0;
Long defaultValue = defaultNull ? null : numberNullValue.value()[0];
return new NullValueSerializer<>(Number.class, defaultValue, defaultNull, serializer);
}
return null;
}
}
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class NullValueSerializer<T> extends AbstractResultSerializer<T> {
private final T defaultValue;
private final Boolean defaultNull;
public NullValueSerializer(Class<T> tClass, T defaultValue, boolean defaultNull, AbstractResultSerializer serializer) {
super(tClass, serializer);
this.defaultValue = defaultValue;
this.defaultNull = defaultNull;
}
@Override
public T serializeValue(T value) {
if (ObjectUtil.isEmpty(value)) {
return this.defaultNull ? null : this.defaultValue;
}
return value;
}
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (preSerializer != null) {
value = (T) preSerializer.serializeValue(value);
}
value = serializeValue(value);
gen.writeObject(value);
}
}
处理数据也可以使用切面做,但是有几个缺点:
- 数据处理后,类型必须要跟对象的属性类型一样
- 如果数据有多层,就需要自己写循环迭代处理,比较麻烦
那么使用jackson内省器,在组装json的时候处理想要的数据,就会避免使用切面时遇到的问题