spring boot 拦截的方式

  1. 过滤器filter:
    可以获取http、http请求和响应,但无法获取与spring框架相关的信息,如哪个control处理,哪个方法处理,有哪些参数,这些都是无法获取的。主要用于内容上的过滤,敏感字替换成*等,也可用于非登入状态的非法请求过滤。
  2. 拦截器interceptor:
    除了获取http、http请求和响应对象,还可以获取请求的类名、方法名,但拦截器无法获取请求参数的值,从DispatcherServlet类源码分析。主要用于对公共的一些拦截获取,例如请求的IP 地址,IP黑白名单里的过过滤,非登入状态的接口请求拦截。
  3. 切面拦截Aspect:
    能获取到方法请求的参数,方法名,以及方法返回的json数据,更多的是用于数据的处理,比如对操作进行记录,修改,新建,查询,审批等操作记录进行处理统计。对返回的json中的一些特殊数据,比如字典值替换成对应的数据,避免前端转化,等等。

执行顺序

正常情况:过滤器、拦截器、切片,
异常报错:切片、ControllerAdvice注解类、拦截器、过滤器

切片的使用:

  1. 相关注解

1.1 @Pointcut 注解:

指定一个切点,定义需要拦截的东西,这里介绍两个常   用的表达式:一个是使
 用 execution(),另一个是使用 annotation()。
 execution表达式:
 以 execution(* com.mutest.controller..*.*(..))) 表达式为例:
 第一个 * 号的位置:表示返回值类型,* 表示所有类型。包名:表示需要拦 
 截的包名,后面的两个句*斜体样式*点表示当前包和当前包的所有子包,在本例中
 指 com.mutest.controller包、子包下所有类的方法。
 第二个 * *号的位置:表示类名,** 表示所有类。
 (..):*这个星号表示方法名*,* 表示所有的方法,后面括弧里面表示方法的参
 数,两个句点表示任何参数。
 annotation() 表达式:
 annotation() 方式是针对某个注解来定义切点,其中注解包括GetMapping等

(2)@Around注解:

用于修饰Around增强处理,Around增强处理非常强大,表现在:
 @Around可以自由选择增强动作与目标方法的执行顺序,也就是说可以在增强动作
 前后,甚至过程中执行目标方法。这个特性的实现在于,调用
 ProceedingJoinPoint参数的procedd()方法才会执行目标方法。
 @Around可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值。

(3)@Before 注解:

指定的方法在切面切入目标方法之前执行,可以做一些 Log 处理,也可以做一些
 信息的统计,可以通过参数JointPoint 来获取一些有用的信息,可以用它来获取
 一个签名,利用签名可以获取请求的包名、方法名,包括参数(通过joinPoint.
 getArgs() 获取)等。

(4)@After注解:

和before注解相对应的注解,同样可以进行一些日志处理等

(5)@AfterReturning 注解:

和 @After 有些类似,区别在于 @AfterReturning 注解可以用来捕获切入方
 法执行完之后的返回值,对返回值进行业务逻辑上的增强处理。对返回的json字
 符串进行处理。

(6)@@AfterThrowing注解:

当被切方法执行过程中抛出异常时,会进入 @AfterThrowing 注解的方法中执行,
在该方法中可以做一些异常的处理逻辑。

示例

import com.alibaba.fastjson.JSONObject;
import com.cmhit.crm.constants.FunctionEnum;
import com.cmhit.crm.service.OperateLogService;
import com.cmhit.crm.utils.JsonUtil;
import com.cmhit.crm.vo.operlog.OperateLogCreateReqVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Slf4j
@Aspect
@Component
public class OperateLogAspect {
   
   //操作日志service
   @Autowired 
   private OperateLogService operateLogService;
  //操作日志实体
   private OperateLogCreateReqVO operateLog = new OperateLogCreateReqVO();

   // 定义一个切入点
   @Pointcut("execution(* com.cmhit.crm.controller.*.*(..))")
   public void operlog(){
      //这里面不要写代码,不会执行的
   }

   // 前置通知
   @Before(value = "operlog()")
   public void before(JoinPoint jp) {
       //方法名获取
       String name = jp.getSignature().getName();
       //方法参数获取
       Object[] args = jp.getArgs();
       //设置操作日志
       setOperateLogType(name,args);
       log.debug("{}方法开始执行...开始设置设置操作日志的操作类型",name);
   }

   // 后置通知
   @After(value = "operlog()")
   public void after(JoinPoint jp) {
       String name = jp.getSignature().getName();
       log.debug("{}方法执行结束...",name);
   }

   // 返回通知
   @AfterReturning(value = "operlog()")
   public void afterReturning(JoinPoint jp) {
       String name = jp.getSignature().getName();
       if (Objects.nonNull(operateLog.getSourceId())) {
           operateLog.setOperationResult("操作成功!");
           operateLogService.insertOperateLog(operateLog);
       }
       log.debug("{}方法执行成功",name);
   }

   // 异常通知
   @AfterThrowing(value = "operlog()", throwing = "e")
   public void afterThrowing(JoinPoint jp, Exception e) {
       String name = jp.getSignature().getName();
       if (Objects.nonNull(operateLog.getSourceId())){
           operateLog.setOperationResult(e.getMessage());
           operateLogService.insertOperateLog(operateLog);
       }
       log.debug("{}方法抛异常,异常是{}",name , e.getMessage());
   }

   // 环绕通知
   @Around("operlog()")
   public Object around(ProceedingJoinPoint pjp) throws Throwable {
       String name = pjp.getSignature().getName();
       // 统计方法执行时间
      long start = System.currentTimeMillis();
      Object result = pjp.proceed();
      long end = System.currentTimeMillis();
      System.out.println(name + "方法执行时间为:" + (end - start) + " ms");
       return result;
   }
   private void setOperateLogType(String name,Object[] args){
     //公司的业务逻辑,这里建立使用自己的
    }

   }
   //参数处理方法
   private void dealGetinfoArgs(Object[] args){
    //自己写逻辑吧
   }
  

}