qq_43751299 2021-05-26 23:41 采纳率: 50%
浏览 443
已采纳

springboot使用aop打印异步请求的日志报空指针

这是我的aop日志配置

package com.wtblog.service.config;

import com.wtblog.utils.ResultEntity;
import lombok.extern.slf4j.Slf4j;
import nl.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@Aspect
@Component
@Slf4j
public class WebLogAspect {

    /**
     * 进入方法时间戳
     */
    private Long startTime;
    /**
     * 方法结束时间戳(计时)
     */
    private Long endTime;

    public WebLogAspect() {
    }

    /**
     * 定义请求日志切入点,其切入点表达式有多种匹配方式,这里是指定路径
     */
    @Pointcut("execution(* com.wtblog.service..*.*(..))")
    public void webLogPointcut() {
    }


    @Around("webLogPointcut()&&args(..,bindingResult)")
    public Object  doAround(ProceedingJoinPoint joinPoint, BindingResult bindingResult) throws Throwable {
        Object result = null;

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取请求头中的User-Agent
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        //打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("请求开始时间:{}", LocalDateTime.now());
        log.info("请求Url : {}", request.getRequestURL().toString());
        log.info("请求方式 : {}", request.getMethod());
        log.info("请求ip : {}", request.getRemoteAddr());
        log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
        // 系统信息
        log.info("浏览器:{}", userAgent.getBrowser().toString());
        log.info("浏览器版本:{}", userAgent.getBrowserVersion());
        log.info("操作系统: {}", userAgent.getOperatingSystem().toString());

        List<ObjectError> ls = bindingResult.getAllErrors();
        if (ls != null && ls.size() > 0) {
            // 保存异常日志记录
            log.error("发生异常时间:{}", LocalDateTime.now());
            log.error("抛出异常:{}", ls.get(0).getDefaultMessage());
            return ResultEntity.failure().message(ls.get(0).getDefaultMessage());
        }
        result = joinPoint.proceed();
        return result;
    }


    /**
     * 前置通知:
     * 1. 在执行目标方法之前执行,比如请求接口之前的登录验证;
     * 2. 在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLogPointcut()")
    public void doBefore(JoinPoint joinPoint) {

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取请求头中的User-Agent
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        //打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("请求开始时间:{}", LocalDateTime.now());
        log.info("请求Url : {}", request.getRequestURL().toString());
        log.info("请求方式 : {}", request.getMethod());
        log.info("请求ip : {}", request.getRemoteAddr());
        log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
        // 系统信息
        log.info("浏览器:{}", userAgent.getBrowser().toString());
        log.info("浏览器版本:{}", userAgent.getBrowserVersion());
        log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
    }

    /**
     * 返回通知:
     * 1. 在目标方法【正常结束之后】执行
     * 1. 在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        endTime = System.currentTimeMillis();
        log.info("请求结束时间:{}", LocalDateTime.now());
        log.info("请求耗时:{}ms", (endTime - startTime));
        // 处理完请求,返回内容
        log.info("请求返回 : {}", ret);
    }

    /**
     * 异常通知:
     * 1. 在目标方法【非正常结束后】,发生异常或者抛出异常时执行
     * 1. 在异常通知中设置异常信息,并将其保存
     *
     * @param throwable
     */
    @AfterThrowing(value = "webLogPointcut()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        // 保存异常日志记录
        log.error("发生异常时间:{}", LocalDateTime.now());
        log.error("抛出异常:{}", throwable.getMessage());
    }
}

这是异步请求调用

@CrossOrigin
@RestController
@RequestMapping("/servicebloguser/blog-user")
@EnableAsync
public class BlogUserController {

    @Autowired
    private BlogUserService blogUserService;


    @ApiOperation(value = "用户使用邮箱注册,进行验证,获取验证码")
    @GetMapping("user/get/verification/code/by/email")
    public ResultEntity userGetVerificationCodeByEmail(@RequestParam(value = "email") String email) {
        try {
            blogUserService.userGetVerificationCodeByEmail(email);
            return ResultEntity.success();
        } catch (WTException e) {
            e.printStackTrace();
            return ResultEntity.failure().code(e.getCode()).message(e.getMsg());
        } catch (Exception e) {
            e.printStackTrace();
            return ResultEntity.failure();
        }
    }

}
package com.wtblog.service.servicebloguser.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wtblog.exception.WTException;
import com.wtblog.service.servicebloguser.entity.BlogRegistInfoVO;
import com.wtblog.service.servicebloguser.entity.BlogUser;
import com.wtblog.service.servicebloguser.mapper.BlogUserMapper;
import com.wtblog.service.servicebloguser.service.BlogUserService;
import com.wtblog.service.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.regex.Pattern;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author WT-X
 */
@Service
public class BlogUserServiceImpl extends ServiceImpl<BlogUserMapper, BlogUser> implements BlogUserService {

    @Autowired
    private JavaMailSenderImpl javaMailSender;

    

    // 用户使用邮箱注册,进行验证,获取验证码
    @Async
    @Override
    public void userGetVerificationCodeByEmail(String email) {
        // 校验email数据格式
        String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
        if (email == null || email == "" || !Pattern.compile(regEx1).matcher(email).matches()) {
            throw new WTException(20001, "邮件有误,请确认后填写~");
        }
        // 生成验证码
        String emailServiceCode = UUID.randomUUID().toString().replace("-", "").substring(0, 4);
        // 设置发件者信息
        SimpleMailMessage message = new SimpleMailMessage();
        // 主题
        message.setSubject("注册验证码");
        // 邮件内容
        message.setText("注册验证码是:" + emailServiceCode);
        // 发件者邮箱
        message.setFrom("12100000@qq.com");
        // 收件者邮箱
        message.setTo(email);
        try {
            javaMailSender.send(message);
        }catch (Exception e){
            throw new WTException(20001, "邮件发送失败发送~");
        }

    }




}

已经测试过了,在屏蔽掉日志打印那块,程序正常进行。。。而且日志本身打印其他没出过问题。。。来个大佬指导一下!

  • 写回答

7条回答 默认 最新

  • Ryder_code 2021-05-27 07:09
    关注

    异步线程调用RequestContextHolder.getRequestAttributes()的问题引起的

    解决方法:

    1.去掉异步调用 

    2. 启动类添加以下代码

        @Bean
        public RequestContextListener requestContextListener(){
            return new RequestContextListener();
        }

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • 关注

     

    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

    HttpServletRequest request = attributes.getRequest();

    调试一下,看看attributes是不是空值

    最好写个if语句判断一下。

    评论
  • qq_43751299 2021-05-26 23:44
    关注

    对了,还有报错信息

     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | DirectJDKLog.java:173 | org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] | Initializing Spring DispatcherServlet 'dispatcherServlet'
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | FrameworkServlet.java:525 | org.springframework.web.servlet.DispatcherServlet | Initializing Servlet 'dispatcherServlet'
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | FrameworkServlet.java:547 | org.springframework.web.servlet.DispatcherServlet | Completed initialization in 7 ms
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:98 | com.wtblog.service.config.WebLogAspect | 请求开始时间:2021-05-26T23:32:19.632
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:99 | com.wtblog.service.config.WebLogAspect | 请求Url : http://localhost:8001/servicebloguser/blog-user/user/get/verification/code/by/email
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:100 | com.wtblog.service.config.WebLogAspect | 请求方式 : GET
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:101 | com.wtblog.service.config.WebLogAspect | 请求ip : 0:0:0:0:0:0:0:1
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:102 | com.wtblog.service.config.WebLogAspect | 请求参数 : [1961002626@qq.com]
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:104 | com.wtblog.service.config.WebLogAspect | 浏览器:CHROME9
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:105 | com.wtblog.service.config.WebLogAspect | 浏览器版本:90.0.4430.93
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:106 | com.wtblog.service.config.WebLogAspect | 操作系统: WINDOWS
     | 2021-05-26 23:32:19 | ERROR | task-1 | WebLogAspect.java:136 | com.wtblog.service.config.WebLogAspect | 发生异常时间:2021-05-26T23:32:19.643
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:120 | com.wtblog.service.config.WebLogAspect | 请求结束时间:2021-05-26T23:32:19.643
     | 2021-05-26 23:32:19 | ERROR | task-1 | WebLogAspect.java:137 | com.wtblog.service.config.WebLogAspect | 抛出异常:null
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:121 | com.wtblog.service.config.WebLogAspect | 请求耗时:13ms
     | 2021-05-26 23:32:19 | INFO  | http-nio-8001-exec-1 | WebLogAspect.java:123 | com.wtblog.service.config.WebLogAspect | 请求返回 : ResultEntity(success=true, code=20000, message=成功, data={})
     | 2021-05-26 23:32:19 | ERROR | task-1 | SimpleAsyncUncaughtExceptionHandler.java:39 | org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler | Unexpected exception occurred invoking async method: public void com.wtblog.service.servicebloguser.service.impl.BlogUserServiceImpl.userGetVerificationCodeByEmail(java.lang.String)
    java.lang.NullPointerException: null
    	at com.wtblog.service.config.WebLogAspect.doBefore(WebLogAspect.java:93)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:626)
    	at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44)
    	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:55)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    	at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    
    评论
  • qq_43751299 2021-05-27 08:04
    关注

    评论
  • qq_43751299 2021-05-27 08:09
    关注
    | 2021-05-27 08:06:08 | ERROR | main | TomcatStarter.java:61 | org.springframework.boot.web.embedded.tomcat.TomcatStarter | Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'requestContextListener' defined in com.wtblog.service.ServiceApplication: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.context.request.RequestContextListener]: Factory method 'requestContextListener' threw exception; nested exception is java.lang.NullPointerException
    ...
    Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestContextListener' defined in com.wtblog.service.ServiceApplication: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.context.request.RequestContextListener]: Factory method 'requestContextListener' threw exception; nested exception is java.lang.NullPointerException
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.context.request.RequestContextListener]: Factory method 'requestContextListener' threw exception; nested exception is java.lang.NullPointerException
    Caused by: java.lang.NullPointerException: null
    评论
  • 有问必答小助手 2021-05-27 10:19
    关注

    您好,我是有问必答小助手,您的问题已经有小伙伴解答了,您看下是否解决,可以追评进行沟通哦~

    如果有您比较满意的答案 / 帮您提供解决思路的答案,可以点击【采纳】按钮,给回答的小伙伴一些鼓励哦~~

    ps:问答VIP仅需29元,即可享受5次/月 有问必答服务,了解详情>>>https://vip.csdn.net/askvip?utm_source=1146287632

    评论
  • qq_43751299 2021-05-27 14:43
    关注

    解决思路:遇到的问题有两个,①:日志打印异步操作报空指针,②:异步操作失效

    问题①:是因为
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(),在获取上下文对象时失败了,那就干脆不让他获取!我直接在controller的方法中加一个HttpServletRequest request参数,在controller中时百分百可以获取到!然后在切入点上做文章,重新搞一个切入点,让他对我这个要进行异步操作的方法1对1VIP服务;接着,利用JoinPoint来获取上下文对象(也就是取controller方法中的HttpServletRequest request参数)这样一来就确保百分百成功,不会报空指针了!

    问题②:按照百度的那些说法来就好了,比如必须要有@Async+@EnableAsync,我在实际操作中直接将@Async标注在@service中的方法上了,并没有单独抽出,也成功了!唯一要注意的就是不要在内部调用就ok了~

    贴下代码:

    1.这是日志那块

        @Before(value = "execution(* com.wtblog.service.servicebloguser.controller.BlogUserController.userGetVerificationCodeByEmail(..)) ")
        public void doBefore2(JoinPoint joinPoint) {
            List<Object> args = Arrays.asList(joinPoint.getArgs());
            // 接收到请求,记录请求内容
            HttpServletRequest request = (HttpServletRequest) args.get(1);
            //获取请求头中的User-Agent
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            //打印请求的内容
            startTime = System.currentTimeMillis();
            log.info("请求开始时间:{}", LocalDateTime.now());
            log.info("请求协议 : {}", request.getScheme());
            log.info("请求Url : {}", request.getRequestURL());
            log.info("请求方式 : {}", request.getMethod());
            log.info("请求ip : {}", request.getRemoteAddr());
            log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
            // 系统信息
            log.info("浏览器:{}", userAgent.getBrowser());
            log.info("浏览器版本:{}", userAgent.getBrowserVersion());
            log.info("操作系统: {}", userAgent.getOperatingSystem());
        }

    2.controller那块

        @ApiOperation(value = "用户使用邮箱注册,进行验证,获取验证码")
        @GetMapping("user/get/verification/code/by/email")
        public ResultEntity userGetVerificationCodeByEmail(@RequestParam(value = "email") String email, HttpServletRequest request) {
            try {
                long startTime = System.currentTimeMillis();
                blogUserService.userGetVerificationCodeByEmail(email,startTime);
                return ResultEntity.success();
            } catch (WTException e) {
                e.printStackTrace();
                return ResultEntity.failure().code(e.getCode()).message(e.getMsg());
            } catch (Exception e) {
                e.printStackTrace();
                return ResultEntity.failure();
            }
        }

    3.service那块,就不全贴了,领会意思即可

        // 用户使用邮箱注册,进行验证,获取验证码
        @Async
        @Override
        public void userGetVerificationCodeByEmail(String email,long startTime) {
            ...
            try {
                javaMailSender.send(message);
                long endTime = System.currentTimeMillis();
                log.info("请求结束时间:{}", LocalDateTime.now());
                log.info("请求耗时:{}ms", (endTime - startTime));
            }catch (Exception e){
                throw new WTException(20001, "邮件发送失败发送~");
            }
    
        }

    4.主启动类上

    ...
    @EnableAsync
    public class ServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServiceApplication.class, args);
        }
    
    }





     

    评论
查看更多回答(6条)

报告相同问题?

悬赏问题

  • ¥15 Jupyter Notebook 数学公式不渲染
  • ¥20 ERR_CACHE_MISS 确认重新提交表单
  • ¥20 关于vba使用HTMLfile执行js函数问题
  • ¥60 悬赏求解,通过实时现场摄像头的视频图像识别其他对家打出的麻将牌,识别麻将牌,识别牌墙位置,通过识别对家打出了什么牌
  • ¥15 关于#GPU jetson#的pcie驱动开发问题,如何解决?
  • ¥15 stm32f103zet6 串口5无法收发数据
  • ¥15 关于C语言使用线程队列实现多线程并发
  • ¥15 这个运行,错误在哪里呀,大家看看吧,教导我
  • ¥15 advanceinstaller对话框设置
  • ¥100 正常上网,内部网页无法打开