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

Spring Boot请求作用域配置实现:基于请求参数动态配置Jackson序列化器

刚好之前处理过类似的需求,Spring Boot里要实现请求作用域的Jackson配置,核心就是要让Jackson的配置逻辑跟着请求走,而不是在启动时一次性初始化。下面给你两种实用的方案,你可以根据自己的场景来选:

方案一:请求作用域ObjectMapper + 拦截器动态配置

这种方案直接创建一个请求作用域的ObjectMapper实例,然后通过拦截器在每次请求进来时,根据请求参数修改它的配置。

1. 定义请求作用域的ObjectMapper

首先,我们需要把ObjectMapper声明为请求作用域的Bean,并且必须设置代理模式——因为单例Bean(比如Controller、Service)注入请求作用域Bean时,Spring需要生成代理来延迟获取当前请求的实例,否则会启动报错。

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedObjectMapper extends ObjectMapper {
    // 这里可以先做一些基础的默认配置
    public RequestScopedObjectMapper() {
        super();
        // 示例:默认日期格式
        this.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        // 默认不序列化null值
        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
}

2. 编写拦截器动态修改配置

创建一个拦截器,注入上面的请求作用域ObjectMapper,在请求到达Controller之前,读取请求参数并调整Jackson配置:

@Component
public class JacksonConfigInterceptor implements HandlerInterceptor {

    private final RequestScopedObjectMapper objectMapper;

    // 构造注入(Spring 4.3+支持)
    public JacksonConfigInterceptor(RequestScopedObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 示例1:根据请求参数修改日期格式
        String dateFormatParam = request.getParameter("dateFormat");
        if (StringUtils.hasText(dateFormatParam)) {
            objectMapper.setDateFormat(new SimpleDateFormat(dateFormatParam));
        }

        // 示例2:根据参数决定是否序列化null值
        String serializeNullsParam = request.getParameter("serializeNulls");
        if ("true".equalsIgnoreCase(serializeNullsParam)) {
            objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        } else {
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        }

        // 还可以添加更多自定义配置,比如修改序列化器、过滤器等
        return true;
    }
}

3. 注册拦截器

把拦截器注册到Spring MVC的拦截器链中,确保它能处理所有请求:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final JacksonConfigInterceptor jacksonConfigInterceptor;

    public WebMvcConfig(JacksonConfigInterceptor jacksonConfigInterceptor) {
        this.jacksonConfigInterceptor = jacksonConfigInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jacksonConfigInterceptor)
                .addPathPatterns("/**"); // 匹配所有请求路径
    }
}
方案二:请求作用域的Jackson2ObjectMapperBuilderCustomizer

如果你想复用Spring Boot自带的Jackson自动配置逻辑,不想完全自定义ObjectMapper,可以用这种方案——创建一个请求作用域的Jackson2ObjectMapperBuilderCustomizer,Spring会在每次请求创建ObjectMapper时自动调用它的自定义逻辑。

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedJacksonCustomizer implements Jackson2ObjectMapperBuilderCustomizer {

    private final HttpServletRequest request;

    // 直接注入当前请求对象(请求作用域Bean可以直接注入HttpServletRequest)
    public RequestScopedJacksonCustomizer(HttpServletRequest request) {
        this.request = request;
    }

    @Override
    public void customize(Jackson2ObjectMapperBuilder builder) {
        // 读取请求参数并修改Builder配置
        String dateFormat = request.getParameter("dateFormat");
        if (StringUtils.hasText(dateFormat)) {
            builder.dateFormat(new SimpleDateFormat(dateFormat));
        }

        String serializeNulls = request.getParameter("serializeNulls");
        if ("true".equalsIgnoreCase(serializeNulls)) {
            builder.serializationInclusion(JsonInclude.Include.ALWAYS);
        } else {
            builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        }

        // 支持更多Builder配置,比如builder.modules()添加自定义模块等
    }
}
注意事项
  • 线程安全:两种方案都是每个请求对应一个独立的ObjectMapper实例(或配置后的实例),所以不用担心多线程下的配置冲突问题。
  • 请求参数的获取:如果你的配置参数是在请求体中,拦截器的preHandle方法里还没读取请求体,这时候可以改用RequestBodyAdvice来在读取请求体前完成配置,或者把参数放在URL、请求Header里。
  • 代理模式:再次强调,请求作用域Bean注入到单例Bean时,必须设置proxyMode = ScopedProxyMode.TARGET_CLASS(或INTERFACES,如果Bean是接口类型),否则Spring会在启动时尝试创建请求作用域Bean,导致报错。

内容的提问来源于stack exchange,提问作者Abdou Rayes

火山引擎 最新活动