本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2024-11(1)

笔记:springboot添加拦截器

发布于2020-11-19 20:30     阅读(563)     评论(0)     点赞(5)     收藏(5)


AOP的相关概念

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • Pointcut(切点):每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点–数据库的记录,切点–查询条件。一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点范围。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。另外,有些AOP框架是允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知
  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,通知分为如下几种:
    • 前置通知(before):在执行业务代码前做些操作,比如获取连接对象
    • 后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
    • 异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务
    • 返回通知(afterReturning),在执行业务代码后无异常,会执行的操作
    • 环绕通知(around),环绕通知=前置+目标方法执行+后置通知;ProceedingJoinPoint只支持环绕通知
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):织入就是将增强添加到对目标类具体连接点上的过程。织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。

23点59:在AOP中切面就是与业务逻辑独立,但又垂直存在于业务逻辑的代码结构中的通用功能组合;切面与业务逻辑相交的点就是切点;连接点就是把业务逻辑离散化后的关键节点;切点属于连接点,是连接点的子集;Advice(增强)就是切面在切点上要执行的功能增加的具体操作;在切点上可以把要完成增强操作的目标对象(Target)连接到切面里,这个连接的方式就叫织入。

AspectJ切入点指示符

  • execution:用于匹配方法执行的连接点;比较常用,具体可以参考:https://blog.csdn.net/zz210891470/article/details/54175107

  • within:用于匹配指定类型内的方法执行;within()和execution()函数不同的是,within()所指定的连接点最小范围只能是类,而execution()所指定的连接点可以大到包,小到方法入参。 所以从某种意义上讲,execution()函数功能涵盖了within()函数的功能

  • this:用于指定AOP代理必须是指定类型的实例,用于匹配该对象的所有连接点。当使用Spring AOP的时候,只能匹配方法执行的连接点。下面是个例子:
    this(com.lcq.service.AdviceManager)//匹配实现了AdviceManager接口的代理对象的所有连接点,在Spring中只是方法执行的连接点

  • target:用于限定目标对象必须是指定类型的实例,用于匹配该对象的所有连接点。当使用Spring AOP的时候,只能匹配方法执行的连接点。下面是个例子:
    target(com.lcq.servcie.AdviceManager)//匹配实现了AdviceManager接口的目标对象的所有连接点,在Spring中只是方法执行的连接点

  • args:用于对连接点的参数类型进行限制,要求参数的类型时指定类型的实例。同样,当使用Spring AOP的时候,只能匹配方法执行的连接点。下面是个例子:
    args(java.io.Serializable)//匹配只接受一个参数,且参数类型是Serializable的所有连接点,在Spring中只是方法执行的连接点
    注意,这个例子与使用execution(* (java.io.Serializable))定义的切点不同,args版本只匹配运行时动态传入参数值是Serializable类型的情形,而execution版本则匹配方法签名只包含一个Serializable类型的形参的方法。单独使用时报错的话,加上没带@的指示符一起使用,例如:
    within(com.lcq.demo.controller.
    ) && args(java.lang.String)

  • @within:用于匹配所以持有指定注解类型内的方法;在接口上声明的对它不起作用,注解写在方法上是不会执行的。

  • @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;在接口上声明的对它不起作用, 注解写在方法上是不会执行的。;单独使用时报错的话,加上没带@的指示符一起使用,例如:
    within(com.lcq.demo.controller.*) && @target(com.lcq.demo.annotaion.ApiControllerLog)

  • @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;类跟方法都可以

  • @annotation:用于匹配当前执行方法持有指定注解的方法;注解写在类上是不会执行。

  • bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

  • reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。

创建拦截器

  1. 创建一个切面:新建一个包interceptor用来存放切面类,然后再在里面创建切面AuthAspect, 内容如下:

     package com.lcq.demo.interceptor;
     
     import com.lcq.demo.util.Tool;
     import org.aspectj.lang.JoinPoint;
     import org.aspectj.lang.ProceedingJoinPoint;
     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.beans.factory.annotation.Autowired;
     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 java.util.HashMap;
     import java.util.Map;
     
     //定义这是一个切面类
     @Aspect
     //将该类交由spring管理
     @Component
     public class AuthAspect {
     
         @Autowired
         private Tool tool;
     
         //方法带有@RequestMapping切点
         @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
         public void requestMapping() {
    
     }
     //方法带有@PostMapping切点
     @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
     public void postMapping(){
    
     }
     //方法带有@GetMapping切点
     @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
     public void getMapping(){
    
     }
     //方法带有@ApiControllerLog
     @Pointcut("@annotation(com.lcq.demo.annotaion.ApiControllerLog)")
     public void apiMapping(){
    
     }
    
     @Around("postMapping()")
     public Object authAndDecryptPost(ProceedingJoinPoint joinPoint) {
         //获取请求参数
         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
         Map<String, Object> requestMap = tool.getParamFromView(request);
    
         //获取请求头
         String requestHeader = request.getHeader("requestHeader");
    
         Object[] args = joinPoint.getArgs();
         System.out.println(args);
         args[0] = requestMap;
         //返回参数
         Object resultObject = null;
         try {
             resultObject = joinPoint.proceed(joinPoint.getArgs());
         } catch (Throwable throwable) {
             Map<String,Object> resultMap = new HashMap<>();
             resultMap.put("isSuccess" , 0);
             resultObject = resultMap;
         }
    
         return resultObject;
     }
    
     @Around("apiMapping()")
     public Object authAndDecryptApi(ProceedingJoinPoint joinPoint) {
         //获取请求参数
         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
         Map<String, Object> requestMap = tool.getParamFromView(request);
    
         //获取请求头
         String requestHeader = request.getHeader("requestHeader");
    
         Object[] args = joinPoint.getArgs();
         System.out.println(args);
         args[0] = requestMap;
         //返回参数
         Object resultObject = null;
         try {
             resultObject = joinPoint.proceed(joinPoint.getArgs());
         } catch (Throwable throwable) {
             Map<String,Object> resultMap = new HashMap<>();
             resultMap.put("isSuccess" , 0);
             resultObject = resultMap;
         }
    
         return resultObject;
     }
     }
    
      /**
          * 获取前端参数
          */
         @SuppressWarnings("rawtypes")
         public Map<String, Object> getParamFromView(HttpServletRequest request){
             //处理前端请求参数
             Enumeration paramNames = request.getParameterNames();
             Map<String, Object> map = new TreeMap<>();
             while ((paramNames != null) && paramNames.hasMoreElements()) {
                 String paramName = (String) paramNames.nextElement();
                 String[] values = request.getParameterValues(paramName);
                 if ((values == null) || (values.length == 0)) {
                     // Do nothing, no values found at all.
                 } else if (values.length > 1) {
                     map.put(paramName, values);
                 } else if(StringUtils.isNoneBlank(values[0])){
                     map.put(paramName, values[0]);
                 }
             }
     
             return map;
         }
    

    获取到请求参数后,就可以在切面里做一些参数判断或校验。
    使用@Pointcut("@annotation()")来定义切点,常用的可以选择RequestMapping、PostMapping、GetMapping;只要在要拦截的方法上加上对应的注释
    也可以使用自定义注解来标识。

  2. 创建自定义注解:创建annotaion包,并在里面创建自定义注解,如ApiControllerLog,类内容如:

     package com.lcq.demo.annotaion;
     
     import java.lang.annotation.*;
     
     /**
      * 自定义注解 拦截ApiController日志
      */
     @Target({ElementType.METHOD, ElementType.TYPE})
     @Retention(RetentionPolicy.RUNTIME)
     @Documented
     public @interface ApiControllerLog {
     	String description()  default "";
     }
    

控制层被拦截方法: 在这里插入图片描述
效果:
在这里插入图片描述

在这里插入图片描述



所属网站分类: 技术文章 > 博客

作者:你不要惹我

链接:http://www.javaheidong.com/blog/article/787/a543f2657f022a10cc2d/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

5 0
收藏该文
已收藏

评论内容:(最多支持255个字符)