概述
一般业务中一个接口可能会有不同用户返回不同字段的需求;
一般方案用@JsonInclude(JsonInclude.Include.NON_NULL)
然后代码控制对应的属性值set null
;
但是这样复用性不高,springboot
中使用的默认序列化包是jackson
,那么自然就想到了可以拦截jackson
的序列化,对部分类做一个序列化属性过滤.
我的jackson
版本:2.13.3
springboot
版本:2.7.4
逻辑概述:
通过com.fasterxml.jackson.annotation.JsonFilter
注解,自定义过滤器
在jackson
序列化的时候如果使用了该过滤器
则进入过滤判定
该方案会替换jackson
下objectMapper
中的FilterProvider
,debug
了下源码,默认是空的
方案二
通过jackson
提供的自定义序列化,手动控制属性的序列化;
相对于方案一在细节上会繁琐一些,但是没有替换jackson
中objectMapper
的默认配置
demo
//自定义注解
package com.xxx.common.xxx.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExceptFilter {
//别名
String aliasName() default "";
//过滤策略
String strategy() default "";
}
//自定义是否序列化接口,实际实现大家根据自己业务需求实现
package com.xxx.common.xxx.strategy;
import com.fasterxml.jackson.databind.JavaType;
public interface CustomExceptFilterStrategy {
//序列化是否过滤;true:序列化;false:不序列化
boolean filter(String fieldName, JavaType type);
}
//通过jackson提供的配置扩展入口,注册上过滤器
package com.xxx.xxx.xxx.config;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.xxx.xxx.xxx.annotation.CustomExceptFilter;
import com.xxx.xxx.xxx.strategy.CustomExceptFilterStrategy;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.util.Map;
@Configuration
@AutoConfigureBefore(Jackson2ObjectMapperBuilder.class)
@ConditionalOnClass(Jackson2ObjectMapperBuilderCustomizer.class)
public class JacksonSerPropertyFilterConfig implements InitializingBean {
//序列化策略实现缓存map
private static Map<String, CustomExceptFilterStrategy> customExceptFilterStrategyMap;
@Bean
public Jackson2ObjectMapperBuilderCustomizer customExceptFilterJackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();
FilterProvider filters = simpleFilterProvider
.addFilter("customBusinessExceptFilter", new CustomBusinessExceptFilter());
jacksonObjectMapperBuilder.filters(filters);
};
}
@Override
public void afterPropertiesSet() {
//缓存策略
customExceptFilterStrategyMap = SpringUtil.getBeansOfType(CustomExceptFilterStrategy.class);
}
public static class CustomBusinessExceptFilter extends SimpleBeanPropertyFilter {
@Override
protected boolean include(BeanPropertyWriter writer) {
return filter(writer.getName(), writer.getType(), writer.getAnnotation(CustomExceptFilter.class));
}
@Override
protected boolean include(PropertyWriter writer) {
return filter(writer.getName(), writer.getType(), writer.getAnnotation(CustomExceptFilter.class));
}
/**
* 是否序列化
*/
private boolean filter(String fieldName, JavaType type, CustomExceptFilter annotation){
if(null == customExceptFilterStrategyMap || null == annotation) return true;
//这是个自定义接口,返回true/false的,true代表需要序列化
//注解中的strategy和CustomExceptFilterStrategy接口的实现类bean name对应上就行
CustomExceptFilterStrategy customExceptFilterStrategy = customExceptFilterStrategyMap.get(annotation.strategy());
if(null == customExceptFilterStrategy) return true;
String aliasName = annotation.aliasName();
if(StrUtil.isNotBlank(aliasName)){
fieldName = aliasName;
}
return customExceptFilterStrategy.filter(fieldName, type);
}
}
}
//使用过滤器
package com.xxx.xxx.vo;
import com.xxx.xxx.xxx.annotation.CustomExceptFilter;
import com.xxx.xxx.xxx.constant.BusinessProFilterStrategyConstant;
import com.fasterxml.jackson.annotation.JsonFilter;
import lombok.Data;
//声明上自己的过滤器
@JsonFilter("customBusinessExceptFilter")
@Data
public class TaskVO {
//声明该字段具体使用哪种策略过滤
@CustomExceptFilter(strategy = BusinessProFilterStrategyConstant.xxx)
private xxx xxxInfo
}
方案二
其实就是利用@JsonSerialize
注解,对指定属性自定义序列化;
缺点:每个属性都要主动调用对应的序列化方法;
@Data
@JsonSerialize(using = CustomerSerial.class)
public class TestVO {
private xxx testInfo;
}
package com.xxx.xxx.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
* spring-web默认的序列化是jackson
* 相关的序列化器bean也是托管在spring容器中--单例
* org.springframework.http.converter.json.SpringHandlerInstantiator#serializerInstance(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
*/
@Slf4j
@Component
public class CustomerSerial extends StdSerializer<TestVO> {
@Resource
private RemoteUserService remoteUserService;
public CustomerSerial() {
super(TestVO.class);
}
@Override
public void serialize(TestVO value, JsonGenerator gen, SerializerProvider provider) throws IOException {
//开始大括号
gen.writeStartObject();
//动态权限判断
testSerialAuthority(value, gen);
//结束大括号
gen.writeEndObject();
}
private void testSerialAuthority(TestVO value, JsonGenerator gen) throws IOException {
//filter有权限查看的属性,将过滤后的属性转成map
Map<String, Object> propertyMap = filterNotVisibleProperty(value.getTestInfo());
//无权限查看,整个对象都不序列化
if(MapUtils.isEmpty(propertyMap)) return;
//序列化,对象中部分有权限查看的属性字段
gen.writeObjectField("testInfo", propertyMap);
}
}