解决AOP日志切面UT010034: Stream not in async mode问题

2020-11-3 / 0评 / Java

最近做了个功能,原来功能都是使用的restful模式接口,不牵涉Excel的数据导出功能,前两天团队的一小伙伴因管理后台需要个导出数据Excel的功能,导致我的日志切面功能无法解析HttpResponse的返回值。

java.lang.IllegalStateException: UT010034: Stream not in async mode

错误码如下:

2020-11-03 11:32:38.325 ERROR [XNIO-2 task-1] cn.xxx.core.exception.handler.GlobalExceptionHandler: 服务具体异常:
java.lang.IllegalStateException: UT010034: Stream not in async mode
at io.undertow.servlet.spec.ServletOutputStreamImpl.isReady(ServletOutputStreamImpl.java:756) ~[undertow-servlet-1.4.26.Final.jar:1.4.26.Final]
at com.alibaba.fastjson.serializer.ASMSerializer_6_ServletOutputStreamImpl.write(Unknown Source) ~[na:na]
at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:360) ~[fastjson-1.2.73.jar:na]
at com.alibaba.fastjson.serializer.ASMSerializer_5_StatHttpServletResponseWrapper.write(Unknown Source) ~[na:na]
at com.alibaba.fastjson.serializer.ObjectArrayCodec.write(ObjectArrayCodec.java:103) ~[fastjson-1.2.73.jar:na]
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:312) ~[fastjson-1.2.73.jar:na]
at com.alibaba.fastjson.JSON.toJSONString(JSON.java:769) ~[fastjson-1.2.73.jar:na]
at com.alibaba.fastjson.JSON.toJSONString(JSON.java:707) ~[fastjson-1.2.73.jar:na]
at com.alibaba.fastjson.JSON.toJSONString(JSON.java:672) ~[fastjson-1.2.73.jar:na]
at cn.xxx.shopping.config.aspect.ActionAspect.before(ActionAspect.java:66) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:626) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:55) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:100) ~[spring-aop-5.0.12.RELEASE.jar:5.0.12.RELEASE]
java.lang.IllegalStateException: UT010034: Stream not in async mode
at cn.xxx.shopping.config.aspect.ActionAspect.around(ActionAspect.java:90) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at io.undertow.servlet.spec.ServletOutputStreamImpl.isReady(ServletOutputStreamImpl.java:756)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) 
......


网上一通google后,解决方法如下:

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 系统日志切面
 *
 * @author houxr
 */
@Component
@Aspect
@Slf4j
public class ActionAspect {
    /**
     * pointcut  all of the action all methods.
     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
     */
    @Pointcut(value = "execution(* cn.xxxxx.shopping.api..*.*Controller..*(..))")
    public void aspect() {
    }

    /**
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        log.info("the  request details:");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //log 请求内容
        log.info("请求url:  {}", request.getRequestURL().toString());
        log.info("请求方法:  {}", request.getMethod());
        String className = joinPoint.getSignature().getDeclaringTypeName();

        log.info("执行的类名: {}", "(" + className.substring(className.lastIndexOf(".") + 1) + ".java)");
        log.info("执行的方法名: {}", joinPoint.getSignature().getName());
       
        // ----start-切面入参过滤处理------
        // 去除Request或者Response对象
        Object[] joinPointArgs = joinPoint.getArgs();
        Stream<?> stream = ArrayUtils.isEmpty(joinPointArgs) ? Stream.empty() : Arrays.stream(joinPointArgs);
        List<Object> logArgs = stream.filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                .collect(Collectors.toList());
        log.info("参数: {}", JSON.toJSONString(logArgs));
         // ---end-切面入参过滤处理----
        
        //log 方法参数
        Enumeration<String> enu = request.getParameterNames();
        while (enu.hasMoreElements()) {
            String paraName = enu.nextElement();
            log.info(paraName + ":    {}", request.getParameter(paraName));
        }
        try {
            log.info("当前登录用户ID={}", UserHolder.getUserId());
        } catch (Exception e) {
            log.error("获取登录用户异常", e);
        }
    }

    @Around("aspect()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        Object[] args = jp.getArgs();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String method = request.getMethod().toLowerCase();

        try {
            //对于业务抛出异常的记录就处理
            long start = System.currentTimeMillis();
            Object rvt = jp.proceed(args);
            long end = System.currentTimeMillis();
            log.info("-----------------------------------");
            log.info("执行时间为[{}]ms", (end - start));
            log.info("-----------------------------------");

            if ("options".equals(method)) {
                return rvt;
            }
            //记录请求日志
            return rvt;
        } catch (Exception e) {
            log.error("记录请求日志error", e);
            //继续抛出异常
            throw e;
        }

    }

    /**
     * 配置后置通知,使用在方法aspect()上注册的切入点
     */
    @After(value = "aspect()")
    public void after() {

    }
}

问题复现: 原来日志切面代码是没有处理response的处理流过程。

/**
 * 系统日志切面
 *
 * @author houxr
 */
@Component
@Aspect
@Slf4j
public class ActionAspect {
    /**
     * pointcut  all of the action all methods.
     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
     */
    @Pointcut(value = "execution(* cn.xxxxx.shopping.api..*.*Controller..*(..))")
    public void aspect() {
    }

    /**
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        log.info("the  request details:");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //log 请求内容
        log.info("请求url:  {}", request.getRequestURL().toString());
        log.info("请求方法:  {}", request.getMethod());
        String className = joinPoint.getSignature().getDeclaringTypeName();

        log.info("执行的类名: {}", "(" + className.substring(className.lastIndexOf(".") + 1) + ".java)");
        log.info("执行的方法名: {}", joinPoint.getSignature().getName());
        
        // -----有问题代码行------
        log.info("参数: {}", JSON.toJSONString( joinPoint.getArgs()));
        // ---------------------
        
        //log 方法参数
        Enumeration<String> enu = request.getParameterNames();
        while (enu.hasMoreElements()) {
            String paraName = enu.nextElement();
            log.info(paraName + ":    {}", request.getParameter(paraName));
        }
        try {
            log.info("当前登录用户ID={}", UserHolder.getUserId());
        } catch (Exception e) {
            log.error("获取登录用户异常", e);
        }
    }

    @Around("aspect()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        Object[] args = jp.getArgs();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String method = request.getMethod().toLowerCase();

        try {
            //对于业务抛出异常的记录就处理
            long start = System.currentTimeMillis();
            Object rvt = jp.proceed(args);
            long end = System.currentTimeMillis();
            log.info("-----------------------------------");
            log.info("执行时间为[{}]ms", (end - start));
            log.info("-----------------------------------");

            if ("options".equals(method)) {
                return rvt;
            }
            //记录请求日志
            return rvt;
        } catch (Exception e) {
            log.error("记录请求日志error", e);
            //继续抛出异常
            throw e;
        }

    }

    /**
     * 配置后置通知,使用在方法aspect()上注册的切入点
     */
    @After(value = "aspect()")
    public void after() {

    }
}




本文共计 13694 字,感谢您的耐心浏览与评论。

声明:土豆丝不辣|版权所有,违者必究|如未注明,均为原创|转载请注明原文链接说明出处

0条回应:“解决AOP日志切面UT010034: Stream not in async mode问题”