SpringBoot Aop切面

添加aop依赖

org.springframework.boot
spring-boot-starter-aop

定义切面,交给springboot 管理

非注解方式定义切点

  package com.example.demo.aspect;
  
  import org.aspectj.lang.JoinPoint;
  import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.annotation.*;
  import org.springframework.stereotype.Component;
  
  /**
   * @Description TODO
   * @Author coderOx
   * @Date 2023/1/6 21:13
   */
  @Aspect //注解类
  @Component //交给spring管理
  public class MyAspect {
  
      //切点信息
      //value为切点的路径名称,controller包下的所有类的所有方法包括参数
      @Pointcut(value = "execution(* com.example.demo.controller.*.*(..))")
      public void pointCut(){};
  
      //执行切点之前
      @Before(value = "pointCut()")//value为切点方法
      public void before(JoinPoint joinPoint){
          System.out.println("切点执行前"+joinPoint);
      }
  
      //执行切点之后
      @After(value = "pointCut()")//value为切点方法
      public void after(JoinPoint joinPoint){
          System.out.println("切点执行后 "+joinPoint );
      }
  
   //环绕切点执行
      @Around(value = "pointCut()")//value为切点方法
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          long begin = System.currentTimeMillis();
  
          System.out.println("环绕前" + joinPoint);
          //执行切点方法并且获取切点的数据,一定要写,否则切点方法无法执行
          Object o = joinPoint.proceed();
  
          System.out.println("环绕后");
  
          System.out.printf("环绕执行时间:--->{%d}",System.currentTimeMillis()-begin);
  
          //返回执行切点方法的数据,一定要写,否则方法返回值会丢失
          return o;
      }
      //切点方法返回数据之后执行
      //value为切点方法,o为此方法afterReturning的参数o值为被执行方法的返回值
      @AfterReturning(value = "pointCut()",returning = "o")
      public void afterReturning(JoinPoint joinPoint,Object o){
          System.out.println("返回值执行后"+joinPoint);
          System.out.println("afterReturning-value:"+joinPoint);
      }
      //value为切点方法,throw为异常返回信息
      @AfterThrowing(value = "pointCut()",throwing = "e")
      public void afterThrow(JoinPoint joinPoint,Exception e){
          System.out.println("异常抛出后"+joinPoint);
          System.out.println("afterThrow-e.message():"+e.getMessage());
      }
  
  }

TestController代码

  package com.example.demo.controller; 
  import com.sun.org.glassfish.gmbal.ParameterNames;
  import org.springframework.web.bind.annotation.PathVariable;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController; 
  import javax.websocket.server.PathParam; 
  /**
   * @Description TODO
   * @Author coderOx
   * @Date 2023/1/6 21:15
   */
  @RestController
  public class TestController {
      @RequestMapping("/test/{arg}")
      public String test(@PathVariable("arg") String arg){
          System.out.println("test被执行参数:"+arg);
          return "test + arg:"+arg;
      }
  
      @RequestMapping("/test2/{num}")
      public String test2(@PathVariable("num") Integer num){
          int i = 1/num;
          return "test2";
      }
  }

执行http://localhost:8080/test/aaa结果 image.png
执行http://localhost:8080/test2/0结果 image.png

注解方式定义切点

定义注解

   /**
    *   @Target({ElementType.PARAMETER, ElementType.METHOD})
    *  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    *
    *   取值(ElementType)有:
    *
    *     1.CONSTRUCTOR:用于描述构造器
    *     2.FIELD:用于描述域
    *     3.LOCAL_VARIABLE:用于描述局部变量
    *     4.METHOD:用于描述方法
    *     5.PACKAGE:用于描述包
    *     6.PARAMETER:用于描述参数
    *     7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
    *
    * @Retention(RetentionPolicy.RUNTIME)  解释:@Retention()  定义了该Annotation(注释)被保留的时间长短
    * 取值(RetentionPoicy)有:
    *
    *     1.SOURCE:在源文件中有效(即源文件保留)
    *     2.CLASS:在class文件中有效(即class保留)
    *     3.RUNTIME:在运行时有效(即运行时保留)
    *
    *
    * 自定义注解: @interface  自定义注解
    *
    *  定义注解格式:
    *   public @interface 注解名 {定义体}
    *
    *   注解参数的可支持数据类型:
    *
    *     1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    *     2.String类型
    *     3.Class类型
    *     4.enum类型
    *     5.Annotation类型
    *     6.以上所有类型的数组
    *
    *   Annotation(注释)类型里面的参数该怎么设定:
    *   第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
    *   第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和
    *   String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
    *   第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。
    *
    */
       package com.example.demo.aspect; 
       import java.lang.annotation.ElementType;
       import java.lang.annotation.Retention;
       import java.lang.annotation.RetentionPolicy;
       import java.lang.annotation.Target;
       
       @Target(value = ElementType.METHOD)
       @Retention(value = RetentionPolicy.RUNTIME)
       public @interface PointCutAnnotation {
           String methodName() default "";
       }

更改切面中切点的注解值,其他同非注解方式

  package com.example.demo.aspect; 
  import org.aspectj.lang.JoinPoint;
  import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.annotation.*;
  import org.springframework.stereotype.Component; 
  /**
   * @Description TODO
   * @Author coderOx
   * @Date 2023/1/6 21:13
   */
  @Aspect //注解类
  @Component //交给spring管理
  public class MyAspect { 
      //切点信息
      //value为切点的路径名称,controller包下的所有类的所有方法包括参数
  //    @Pointcut(value = "execution(* com.example.demo.controller.*.*(..))")
      @Pointcut(value = "@annotation(com.example.demo.aspect.PointCutAnnotation)")
      public void pointCut(){};
      //执行切点之前
      @Before(value = "pointCut()")//value为切点方法
      public void before(JoinPoint joinPoint){
          System.out.println("切点执行前"+joinPoint);
      }
      //执行切点之后
      @After(value = "pointCut()")//value为切点方法
      public void after(JoinPoint joinPoint){
          System.out.println("切点执行后 "+joinPoint );
      }
      //环绕切点执行
      @Around(value = "pointCut()")//value为切点方法
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          long begin = System.currentTimeMillis();
          System.out.println("环绕前" + joinPoint);
          //执行切点方法并且获取切点的数据,一定要写,否则切点方法无法执行
          Object o = joinPoint.proceed(); 
          System.out.println("环绕后"); 
          System.out.printf("环绕执行时间:--->{%d}",System.currentTimeMillis()-begin);  //返回执行切点方法的数据,一定要写,否则方法返回值会丢失
          return o;
      }
      //切点方法返回数据之后执行
      //value为切点方法,o为此方法afterReturning的参数o值为被执行方法的返回值
      @AfterReturning(value = "pointCut()",returning = "o")
      public void afterReturning(JoinPoint joinPoint,Object o){
          System.out.println("返回值执行后"+joinPoint);
          System.out.println("afterReturning-value:"+joinPoint);
      }
      //value为切点方法,throw为异常返回信息
      @AfterThrowing(value = "pointCut()",throwing = "e")
      public void afterThrow(JoinPoint joinPoint,Exception e){
          System.out.println("异常抛出后"+joinPoint);
          System.out.println("afterThrow-e.message():"+e.getMessage());
      } 
  }

可以在指定通知指定切点,作为参数使用,比如@Before


package com.example.demo.aspect; 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; 
/**
 * @Description TODO
 * @Author coderOx
 * @Date 2023/1/6 21:13
 */
@Aspect //注解类
@Component //交给spring管理
public class MyAspect { 
    //切点信息
    //value为切点的路径名称,controller包下的所有类的所有方法包括参数
//    @Pointcut(value = "execution(* com.example.demo.controller.*.*(..))")
    @Pointcut(value = "@annotation(com.example.demo.aspect.PointCutAnnotation)")
    public void pointCut(){}; 
    //执行切点之前
    @Before(value = "@annotation(pointCutAnnotation)")//value为切点的类,可以作为参数在方法中使用
    public void before(JoinPoint joinPoint,PointCutAnnotation pointCutAnnotation){
        System.out.println("切点执行前"+joinPoint);
        System.out.println("切点的注解值:"+pointCutAnnotation.methodName());
    } 
    //执行切点之后
    @After(value = "pointCut()")//value为切点方法
    public void after(JoinPoint joinPoint){
        System.out.println("切点执行后 "+joinPoint );
    } 
    //环绕切点执行
    @Around(value = "pointCut()")//value为切点方法
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long begin = System.currentTimeMillis(); 
        System.out.println("环绕前" + joinPoint);
        //执行切点方法并且获取切点的数据,一定要写,否则切点方法无法执行
        Object o = joinPoint.proceed(); 
        System.out.println("环绕后"); 
        System.out.printf("环绕执行时间:--->{%d}",System.currentTimeMillis()-begin); 
        //返回执行切点方法的数据,一定要写,否则方法返回值会丢失
        return o;
    }
    //切点方法返回数据之后执行
    //value为切点方法,o为此方法afterReturning的参数o值为被执行方法的返回值
    @AfterReturning(value = "pointCut()",returning = "o")
    public void afterReturning(JoinPoint joinPoint,Object o){
        System.out.println("返回值执行后"+joinPoint);
        System.out.println("afterReturning-value:"+joinPoint);
    }
    //value为切点方法,throw为异常返回信息
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void afterThrow(JoinPoint joinPoint,Exception e){
        System.out.println("异常抛出后"+joinPoint);
        System.out.println("afterThrow-e.message():"+e.getMessage());
    } 
}

controller类
image.png
执行http://localhost:8080/test/aaa结果
image.png