You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot4/Spring7升级场景下替代AbstractMappingJacksonResponseBodyAdvice实现请求驱动Jackson字段过滤的方案咨询(含Jackson3扩展疑问)

Spring Boot4/Spring7升级场景下替代AbstractMappingJacksonResponseBodyAdvice实现请求驱动Jackson字段过滤的方案咨询(含Jackson3扩展疑问)

核心方案1:利用ResponseBodyAdvicedetermineWriteHints直接传递Jackson过滤配置

Spring 7+(配合Jackson 3.x)专门为请求级别的Jackson序列化配置提供了标准扩展能力,完全可以替代即将废弃的AbstractMappingJacksonResponseBodyAdviceMappingJacksonValue。核心思路是通过ResponseBodyAdvicedetermineWriteHints方法,基于请求参数动态生成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)

  1. Jackson 3.x API兼容SimpleBeanPropertyFilter的静态方法(如filterOutAllExcept)依然可用,若自定义过滤逻辑,需注意BeanPropertyFilter接口的方法签名变化(替代了Jackson 2.x中的部分方法)。
  2. JacksonJsonWriteHints的定位:这是Spring 7为Jackson 3.x专门设计的请求级配置载体,完全替代了MappingJacksonValue的作用,支持传递FilterProvider、序列化特性等Jackson专属配置。
  3. 虚拟线程兼容性:优先使用ScopedValue替代ThreadLocalRequestContextHolder底层依赖),避免虚拟线程环境下的上下文传递问题。
  4. 默认行为一致性:当请求中无include参数时,返回WriteHints.empty(),Jackson将使用全局序列化规则(返回所有字段),与你原有的逻辑完全兼容。

为什么不推荐直接修改JacksonJsonHttpMessageConverter

直接修改Converter的全局配置会导致线程安全问题,而通过ResponseBodyAdvice传递的配置是请求级别的隔离,每个请求的过滤规则独立,完全符合你的场景需求。同时,determineWriteHints是Spring 7官方推荐的请求级序列化配置扩展点,符合Spring的设计演进方向。

火山引擎 最新活动