FastJson、Jackson使用AOP切面进行日志打印异常

FastJson、Jackson使用AOP切面进行日志打印异常

一、概述

1、问题详情

使用FastJson、Jackson进行日志打印时分别包如下错误:

源码

java 复制代码
//fastjon
log.info("\nRequest Info :{} \n", JSON.toJSONString(requestInfo));
//jackson
log.info("\nRequest Info :{} \n", new ObjectMapper().writeValueAsString(requestInfo));
  • Fastjson错误信息

    bash 复制代码
    java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
    	at org.apache.catalina.connector.Request.getAsyncContext(Request.java:1812)
    	at org.apache.catalina.connector.RequestFacade.getAsyncContext(RequestFacade.java:1068)
  • Jackson错误信息:

    bash 复制代码
    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.util.Collections$3 and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: icu.chiou.qvideo.aop.AccessLogAspect$RequestInfo["requestParams"]->java.util.HashMap["request"]->org.apache.catalina.connector.RequestFacade["parameterNames"])

2、问题原因

猜测:

这些错误的原因可能是由于在日志打印过程中,引入了 Web 请求的相关对象,而这些对象无法直接被序列化为 JSON。

查找:

  • 调用getAsyncContext方法出现了问题,表示当前这个request对象不是异步模式的,所以不能调用getAsyncContext这个方法,这个request的异步模式这是servlet3中的一个新特性,可以使用注解或者配置xml的方式进行开启,一般用于异步请求,这个request的异步模式的使用场景,fastjson序列化出错,出错的原因是fastjson调用了getAsyncContext方法,由于request不是异步模式,所以报错了,那么结果就是fastjson序列化出错了。
  • jackson异常信息表明 jackson 无法找到适合序列化 java.util.Collections$3 这个类的方法。这种异常通常出现在对象中包含了无法序列化的属性或者属性类型,导致 Jackson 无法将整个对象序列化为 JSON 字符串。在你的异常信息中,可以看到异常发生在 AccessLogAspectRequestInfo 对象的 requestParams 属性中,其中包含了一个 java.util.HashMap 对象,该对象中的属性为 request,类型为 org.apache.catalina.connector.RequestFacade,进而包含了 parameterNames 属性,类型为 java.util.Collections$3,而 Jackson 无法对这个属性进行序列化。

二、解决

1、问题定位

看一下异常信息,按照打印的栈信息来看:

  • RequestFacade对象中的getAsyncContext方法被调用了,但是工程里面并没用用到RequestFacade对象,且其没有重载方法
  • 查看RequestFacade类的源码过后发现RequestFacade其实是HttpServletRequest的一个具体实现
  • 那么问题就定位到了,fastjson把HttpServletRequest序列化了,只要把方法上的HttpServletRequest 参数去掉就可以了.

2、解决办法

把进行JSON转字符串序列化对象内的request过滤即可:

java 复制代码
private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
    //参数名
    String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
    //参数值
    Object[] paramValues = joinPoint.getArgs();

    return buildRequestParam(paramNames, paramValues);
}

private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
    Map<String, Object> requestParams = new HashMap<>();
    for (int i = 0; i < paramNames.length; i++) {
        Object value = paramValues[i];
		
        //添加这个
        if (value instanceof ServletRequest || value instanceof ServletResponse) {
            continue;
        }

        //如果是文件对象
        if (value instanceof MultipartFile) {
            MultipartFile file = (MultipartFile) value;
            value = file.getOriginalFilename();  //获取文件名
        }

        requestParams.put(paramNames[i], value);
    }

    return requestParams;
}

3、效果实现

源代码和拓展:

需要控制台格式化的输出JSON只需要调用:writerWithDefaultPrettyPrinter()

java 复制代码
log.info("\nRequest Info :{} \n", new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(requestInfo));
log.info("\nRequest Info :{} \n", JSON.toJSONString(requestInfo));
相关推荐
Jayden9 天前
自定义注解 + AOP + Redisson:优雅实现分布式锁(增强版)
分布式·分布式锁·aop·自定义注解实现分布式锁
逆风飞翔的小叔15 天前
【微服务】深入解析spring aop原理
spring aop·aop·spring aop原理·spring aop底层源码·aop原理
带刺的坐椅1 个月前
无耳科技 Solon v3.0.7 发布(2025农历新年版)
java·spring·mvc·solon·aop
带刺的坐椅1 个月前
[Java] Solon 框架的三大核心组件之一插件扩展体系
java·ioc·solon·plugin·aop·handler
S-X-S2 个月前
AOP实现操作日志记录
java·junit·aop
暮色里de白雪檐2 个月前
基于 Slf4j 和 AOP 的自动化方法执行时间日志记录方案
logback·日志·slf4j·aop·日志打印
A尘埃2 个月前
权限注解+AOP切面+额外工具(UUID生成id,JWT,Servlet客户端,字符串String工具类,Redis序列化,ip工具类)
redis·servlet·注解·aop·序列化
自律的kkk2 个月前
SpringBoot中使用AOP切面编程实现登录拦截
java·spring boot·aop·切面编程·登录拦截
不修×蝙蝠3 个月前
Spring(一)---IOC(控制权反转)
java·spring·ioc·aop·属性注入·spring框架·创建对象
葵续浅笑3 个月前
Spring之我见 - 从IOC谈到AOP实现原理
java·spring·ioc·aop