Spring Boot如何为不同的控制器使用不同的ObjectMapper进行序列化?

后端开发 2026-07-09

背景:这是一个较旧的Spring Boot 2.4.5项目。新增了一个与智能代理API相关的模块。

我们希望统一 com.xxx.controller.agent 目录中所有控制器的API返回的JSON格式,但不影响旧控制器的JSON返回值。

class Data{ 
    private BigDecimal d = new BigDecimal("1.23000")
}
// agent controller return json
 => {d: '1.23'}
// old controller return json
 => {d: 1.23}

我该如何实现?

解决方案

构建一个序列化器,去除尾随的零,并将 BigDecimal 输出为普通字符串:

package com.xxx.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;

public class AgentBigDecimalSerializer extends JsonSerializer<BigDecimal> 
{
    @Override
    public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException 
    {
        if (value != null)
            gen.writeString(value.stripTrailingZeros().toPlainString());

        else gen.writeNull();
    }
}

使用 ResponseBodyAdvice 仅拦截来自 com.xxx.controller.agent 包的响应,这样就能在JSON被写入之前动态注入一个专门的 ObjectMaper,并用你的序列化器对其进行配置:

package com.xxx.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.math.BigDecimal;

@RestControllerAdvice(basePackages = "com.xxx.controller.agent")
public class AgentResponseAdvice implements ResponseBodyAdvice<Object> 
{
    private final MappingJackson2HttpMessageConverter agentConverter;

    public AgentResponseAdvice() 
    {
        // Create an isolated ObjectMapper instance for agent controllers
        ObjectMapper agentMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();

        module.addSerializer(BigDecimal.class, new AgentBigDecimalSerializer());
        agentMapper.registerModule(module);

        this.agentConverter = new MappingJackson2HttpMessageConverter(agentMapper);
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) 
    {
        // Only intercept if Spring is planning to use Jackson for the response
        return MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    public Object beforeBodyWrite(Object body, 
        MethodParameter returnType, 
        MediaType selectedContentType,
        Class<? extends HttpMessageConverter<?>> selectedConverterType,
        ServerHttpRequest request, ServerHttpResponse response) 
    {
        if (body == null)
            return null;

        // Force Spring to use your converter instead of the global one
        try {
            agentConverter.write(body, selectedContentType, response);
        } catch (Exception e) {
            throw new RuntimeException("Failed to serialize agent API response", e);
        }

        // Return null to tell Spring that response has already been handled
        return null;
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章