先上两段代码

第一段代码
很简单,一个AOP切面,记录前端请求的信息,及接口耗时

package com.lxc.springboot.aspact;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

@Aspect // 标注是一个切面
@Component
public class Aspact {

Logger LOG = LoggerFactory.getLogger(Aspact.class);
// 定义一个切入点,关于切入点如何定义?
@Pointcut("execution(* com.lxc.springboot.controller.*.*(..))")
public void pointFn(){}

// 定义一个通知,在执行pointFn这个方法之前(切入入进去之前),我们需要执行check方法
@Before("pointFn()")
public void check(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Object[] args = joinPoint.getArgs();
Object[] filterArgs = new Object[args.length];
LOG.info("前端 - 请求方法:{}", request.getMethod());
LOG.info("前端 - 请求路径:{}", request.getRequestURL());
LOG.info("前端 - 远程地址:{}", request.getRemoteAddr());
for(int i = 0; i < args.length; i ++) {
if(args[i] instanceof HttpServletRequest
|| args[i] instanceof HttpServletResponse
|| args[i] instanceof MultipartFile
) {
continue;
}
filterArgs[i] = args[i];
}
// 字段过滤
List<String> list = new ArrayList<>() {
{
add("password");
}
};
SimplePropertyPreFilter simplePropertyPreFilter = new SimplePropertyPreFilter();
simplePropertyPreFilter.getExcludes().addAll(list);
LOG.info("前端 - 请求参数:{}", JSON.toJSONString(filterArgs, simplePropertyPreFilter));
}

@Around("pointFn()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 排除字段,敏感字段或太长的字段不显示
// ··· ···
LOG.info("返回结果: {}", JSON.toJSONString(result));
LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
return result;
}
}

第二段代码
对前端传过来的参数做了校验,校验插件是springboot内置的:spring-boot-starter-validation

// 插入/添加 数据
@PostMapping(value = "/updataOrAddUser")
public ComResponse updataOrAddUser(@RequestBody @Valid User user) {
return userService.updataOrAddUserService(user);
}
public class User {
// ··· ···
@NotNull(message = "name不能为空")
public String name;
// ··· ···
}

在controller层,我创建的一个捕获全局异常的类:ControllerExceptionHandler.java, 当参数校验不通过时,会执行添加 @ExceptionHandler (value = {BindException.class})注解的方法

@ControllerAdvice
public class ControllerExceptionHandler {

/**
* @ControllerAdvice 增强型Controller,全局异常处理,详情:请查看之前文章!
* ExceptionHandler 标注异常类型,这里对校验参数的做异常处理,也就是说参数异常会被捕获。
*/
@ExceptionHandler(value = {BindException.class})
@ResponseBody
public ComResponse validExceptionHandler(BindException e) {
// 获取校验失败自定义异常信息
String msg = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
// comResponse 类是对返回数据做统一处理
ComResponse comResponse = new ComResponse<>();
comResponse.setMsg(msg);
comResponse.setSuccess(false);
return comResponse;
}
}

首先明确下,我的目的是,当前端传递的参数校验失败时,会返回给前端一个自定义的错误提示,而不是一大堆英文报错!
如下图,所展示的,很清晰、明了:

记录一个springboot中使用同时使用aop切面和校验异常捕获的问题_spring

在没加AOP切面时,参数校验失败时, 一切正常,返回的也是我想要的样子,但是加了AOP切面,竟然没效果了,参数校验失败,捕获全局异常的类,竟然没捕获到,最后找了半天问题,发现是:定义切点时出现了问题:

@Pointcut("execution(* com.lxc.springboot.controller.*.*(..))")

controller包下的所有类,都会插入一个切点,而全局异常类也是定义在controller包下的:
记录一个springboot中使用同时使用aop切面和校验异常捕获的问题_json_02

这样会导致以一个问题,当参数校验失败,就直接会被插入到全局异常类中的 切面给捕获到,会被环绕通知中的xxx.proceed() 抛出这个异常,所以全局异常捕获的方法不会执行,解决此问题也很简单,把切点改下:

@Pointcut("execution(* com.lxc.springboot.controller.TestController.*(..))")

或者在controller包下在创建一个包,放置 ControllerExceptionHandler.java, 切点如下:

// controller包下的所有类,不包含子包。
@Pointcut("execution(* com.lxc.springboot.controller.*.*(..))")