You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring AOP对象序列化递归问题:全局解决方案咨询

解决Spring AOP日志中Jackson序列化循环引用问题

针对你遇到的Jackson序列化循环引用问题,尤其是无法修改Spring框架自带类的情况,可以通过以下几种全局方案解决:

方案一:定制全局ObjectMapper配置

通过Spring配置类修改全局ObjectMapper的序列化规则,禁用循环引用报错,同时可配置忽略空值等优化:

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        return new Jackson2ObjectMapperBuilder()
                // 禁用循环引用导致的序列化失败
                .featuresToDisable(SerializationFeature.FAIL_ON_SELF_REFERENCES)
                // 忽略空值属性,减少日志冗余
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                // 禁用空Bean序列化报错(可选)
                .failOnEmptyBeans(false);
    }
}

这种方案会影响整个应用的Jackson序列化行为,适合希望统一处理循环引用的场景。

方案二:为AOP日志单独创建ObjectMapper

如果不想修改全局序列化规则,可以在AOP切面中单独实例化一个用于日志的ObjectMapper,仅处理日志场景的循环引用:

@Aspect
@Component
@AllArgsConstructor
public class MyAspect{
    // 专门用于日志序列化的ObjectMapper,隔离业务逻辑配置
    private final ObjectMapper logMapper = new ObjectMapper()
            .configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false)
            .setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);

    @Pointcut("within(com.mypackage..*)")
    protected void all() {}

    @Before("all()")
    public void before(final JoinPoint joinPoint) {
        final Class<?> classData = joinPoint.getTarget().getClass();
        final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        final Map<Object, Object> parameters = this.getParameters(joinPoint);
        try {
            log.info(
                "Class {} Method {} Parameters: {}",
                classData.getSimpleName(),
                signature.getName(),
                this.logMapper.writeValueAsString(parameters)
            );
        }
        catch (final JsonProcessingException e) {
            log.error("Error in JSON mapper", e);
        }
    }

    @AfterReturning(value = "all()", returning = "returning")
    public void after(final JoinPoint joinPoint, final Object returning) {
        final Class<?> classData = joinPoint.getTarget().getClass();
        final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        try {
            log.info(
                "Class {} Method {} Returning {}",
                classData.getSimpleName(),
                signature.getName(),
                this.logMapper.writeValueAsString(returning)
            );
        }
        catch (final JsonProcessingException e) {
            log.error("Error in JSON mapper", e);
        }
    }

    // 补充参数映射方法
    private Map<Object, Object> getParameters(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] paramValues = joinPoint.getArgs();
        Map<Object, Object> params = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            params.put(paramNames[i], paramValues[i]);
        }
        return params;
    }
}

这种方案不会影响业务代码的序列化逻辑,仅作用于AOP日志场景,灵活性更高。

方案三:自定义循环引用处理器

如果需要更精细的控制(比如遇到循环时输出对象标识而非跳过),可以为ObjectMapper添加自定义循环引用处理器:

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
        // 添加循环引用处理逻辑
        mapper.addHandler(new SerializationProblemHandler() {
            @Override
            public boolean handleCyclicReference(SerializerProvider provider, JsonGenerator gen, Object value, JsonSerializer<?> serializer, PropertyWriter writer) throws IOException {
                // 遇到循环引用时,输出对象的类名+内存地址标识
                gen.writeString(value.getClass().getSimpleName() + "@" + System.identityHashCode(value));
                return true;
            }
        });
        return mapper;
    }
}

这种方式可以避免序列化失败,同时保留循环对象的基本标识信息,便于日志排查。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:35:40