Spring Boot4/Spring7升级场景下替代AbstractMappingJacksonResponseBodyAdvice实现请求驱动Jackson字段过滤的方案咨询(含Jackson3扩展疑问)
Spring Boot4/Spring7升级场景下替代AbstractMappingJacksonResponseBodyAdvice实现请求驱动Jackson字段过滤的方案咨询(含Jackson3扩展疑问)
核心方案1:利用ResponseBodyAdvice的determineWriteHints直接传递Jackson过滤配置
Spring 7+(配合Jackson 3.x)专门为请求级别的Jackson序列化配置提供了标准扩展能力,完全可以替代即将废弃的AbstractMappingJacksonResponseBodyAdvice和MappingJacksonValue。核心思路是通过ResponseBodyAdvice的determineWriteHints方法,基于请求参数动态生成Jackson过滤规则,并通过Spring提供的JacksonJsonWriteHints传递给转换器,实现请求驱动的字段过滤。
完整实现代码
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; import org.springframework.http.converter.json.JacksonJsonWriteHints; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import com.fasterxml.jackson.databind.ser.BeanPropertyFilter; import com.fasterxml.jackson.databind.ser.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.SimpleFilterProvider; import org.springframework.util.StringUtils; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; @RestControllerAdvice public class PartialResponseBodyAdvice implements ResponseBodyAdvice<Object> { private static final String PARTIAL_RESPONSE_FILTER = "PARTIAL_RESPONSE_FILTER"; private static final String INCLUDE_PARAM_NAME = "include"; // 你的请求参数名 @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 仅对Jackson JSON转换器生效 return JacksonJsonHttpMessageConverter.class.isAssignableFrom(converterType); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 无需修改body本身时直接返回原对象 return body; } @Override public WriteHints determineWriteHints(MethodParameter returnType, MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) { BeanPropertyFilter filter = buildFilterFromRequest(request); if (filter == null) { // 无过滤参数时返回默认提示,Jackson将使用全局序列化规则 return WriteHints.empty(); } // 构建请求级别的FilterProvider SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false); filterProvider.addFilter(PARTIAL_RESPONSE_FILTER, filter); // 封装为Jackson专属的WriteHints返回,供转换器使用 return JacksonJsonWriteHints.builder() .filterProvider(filterProvider) .build(); } // 复用你的请求参数解析逻辑 private BeanPropertyFilter buildFilterFromRequest(ServerHttpRequest request) { String includeFields = request.getURI().getQueryParams().getFirst(INCLUDE_PARAM_NAME); if (StringUtils.isBlank(includeFields)) { return null; } Set<String> allowedFields = Arrays.stream(includeFields.split(",")) .map(String::trim) .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); // Jackson 3.x提供的内置过滤规则:仅保留指定字段 return SimpleBeanPropertyFilter.filterOutAllExcept(allowedFields); } }
配套DTO配置
确保需要过滤的DTO类上标注对应的@JsonFilter注解:
import com.fasterxml.jackson.annotation.JsonFilter; @JsonFilter(PARTIAL_RESPONSE_FILTER) public class YourBusinessDTO { // 业务字段定义 }
方案2:基于ScopedValue的请求上下文传递(适合复杂场景)
如果你的过滤逻辑需要在beforeBodyWrite中额外处理body内容(比如根据返回对象类型动态调整过滤规则),可以使用Spring 7引入的ScopedValue实现请求级别的上下文传递,替代传统的RequestContextHolder(更适配虚拟线程)。
实现示例
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.Optional; @RestControllerAdvice public class PartialResponseBodyAdvice implements ResponseBodyAdvice<Object> { // 定义请求级别的ScopedValue,存储当前请求的FilterProvider private static final ScopedValue<SimpleFilterProvider> REQUEST_FILTER_PROVIDER = ScopedValue.newInstance(); private static final String PARTIAL_RESPONSE_FILTER = "PARTIAL_RESPONSE_FILTER"; private static final String INCLUDE_PARAM_NAME = "include"; @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return JacksonJsonHttpMessageConverter.class.isAssignableFrom(converterType); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { BeanPropertyFilter filter = buildFilterFromRequest(request); if (filter != null) { SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false); filterProvider.addFilter(PARTIAL_RESPONSE_FILTER, filter); // 将过滤规则存入ScopedValue,作用域覆盖当前请求的序列化流程 REQUEST_FILTER_PROVIDER.set(filterProvider); } return body; } @Override public WriteHints determineWriteHints(MethodParameter returnType, MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) { // 从ScopedValue中获取当前请求的过滤规则 Optional<SimpleFilterProvider> filterProviderOpt = REQUEST_FILTER_PROVIDER.getIfPresent(); if (filterProviderOpt.isEmpty()) { return WriteHints.empty(); } return JacksonJsonWriteHints.builder() .filterProvider(filterProviderOpt.get()) .build(); } // 复用请求参数解析逻辑(同方案1) private BeanPropertyFilter buildFilterFromRequest(ServerHttpRequest request) { // 同方案1的实现 } }
关键注意事项(适配Spring7/Jackson3)
- Jackson 3.x API兼容:
SimpleBeanPropertyFilter的静态方法(如filterOutAllExcept)依然可用,若自定义过滤逻辑,需注意BeanPropertyFilter接口的方法签名变化(替代了Jackson 2.x中的部分方法)。 JacksonJsonWriteHints的定位:这是Spring 7为Jackson 3.x专门设计的请求级配置载体,完全替代了MappingJacksonValue的作用,支持传递FilterProvider、序列化特性等Jackson专属配置。- 虚拟线程兼容性:优先使用
ScopedValue替代ThreadLocal(RequestContextHolder底层依赖),避免虚拟线程环境下的上下文传递问题。 - 默认行为一致性:当请求中无
include参数时,返回WriteHints.empty(),Jackson将使用全局序列化规则(返回所有字段),与你原有的逻辑完全兼容。
为什么不推荐直接修改JacksonJsonHttpMessageConverter?
直接修改Converter的全局配置会导致线程安全问题,而通过ResponseBodyAdvice传递的配置是请求级别的隔离,每个请求的过滤规则独立,完全符合你的场景需求。同时,determineWriteHints是Spring 7官方推荐的请求级序列化配置扩展点,符合Spring的设计演进方向。




