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
相关产品推荐
相关产品推荐

