在接口响应时,有很多情况需要做数据转换,比如数据脱敏,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<>();
// 循环处理一个属性上的多个注解
// 执行顺序是 按照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 = null;
for (AbstractResultValueIntrospector introspector : introspectorList) {
Object newSerializer = introspector.findNullSerializer(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的时候处理想要的数据,就会避免使用切面时遇到的问题
============================================================
注解获取
自定义注解
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomAnnotation{
String value();
}
introspectorList.add(new DictValueAnnotationIntrospector());
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.xc.cloud.datahandler.ser.AbstractResultSerializer;
import com.xc.cloud.datahandler.ser.DictValueSerializer;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DictValueAnnotationIntrospector extends AbstractResultValueIntrospector {
@Override
public Object findSerializer(Annotated am, AbstractResultSerializer serializer) {
CustomAnnotation annotation = am.getAnnotation(CustomAnnotation.class);
if (annotation != null) {
return new DictValueSerializer(am, annotation, serializer);
}
return null;
}
}
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Map;
@Slf4j
public class DictValueSerializer extends AbstractResultSerializer<String> {
private final CustomAnnotation anno;
private final Annotated am;
public DictValueSerializer(Annotated am, CustomAnnotation anno, AbstractResultSerializer serializer) {
super(String.class, serializer);
this.anno = anno;
this.am = am;
}
@Override
public String serializeValue(String value) {
// if (ObjectUtil.isEmpty(value)) {
// return value;
// }
return value;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (preSerializer != null) {
value = (String) preSerializer.serializeValue(value);
}
value = serializeValue(value);
gen.writeString(value);
if (ObjectUtil.isNotEmpty(value)) {
try {
String code = anno.value();
String fieldName = StrUtil.getGeneralField(am.getName());
if("userType".equals(code) && "0".equals(value)){
gen.writeStringField(fieldName + "Text", "管理员");
}
Map<String, 实体> dictMap = CustomAnnotationUtils.getDictChildrenMap(code);
ZdBaseDict dict = dictMap.get(value);
if (dict != null) {
gen.writeStringField(fieldName + "Text", dict.getCodeValue());
}
} catch (Exception e) {
log.error("SerializeError->", e);
}
}
}
}