这是我的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, "邮件发送失败发送~");
}
}
}
已经测试过了,在屏蔽掉日志打印那块,程序正常进行。。。而且日志本身打印其他没出过问题。。。来个大佬指导一下!
异步线程调用RequestContextHolder.getRequestAttributes()的问题引起的
解决方法:
1.去掉异步调用
2. 启动类添加以下代码
@Bean
public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
调试一下,看看attributes是不是空值
最好写个if语句判断一下。
对了,还有报错信息
| 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)
| 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
您好,我是有问必答小助手,您的问题已经有小伙伴解答了,您看下是否解决,可以追评进行沟通哦~
如果有您比较满意的答案 / 帮您提供解决思路的答案,可以点击【采纳】按钮,给回答的小伙伴一些鼓励哦~~
ps:问答VIP仅需29元,即可享受5次/月 有问必答服务,了解详情>>>https://vip.csdn.net/askvip?utm_source=1146287632
解决思路:遇到的问题有两个,①:日志打印异步操作报空指针,②:异步操作失效
问题①:是因为
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);
}
}