一 基础概念
1 切面(Aspect)

  面向切面编程则是指,对于一个我们已经封装好的类,我们可以在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的方法里面添加(织入)一些新的代码,对原有的方法代码进行一次增强处理。而那些增强部分的代码,就被称之为切面,常见的有日志处理、事务处理、权限认证等等。

2 切入点(PointCut)

  要对哪些类中的哪些方法进行增强,进行切割,指的是被增强的方法。即要切哪些东西。

3 连接点(JoinPoint)

  我们知道了要切哪些方法后,剩下的就是什么时候切,在原方法的哪一个执行阶段加入增加代码,这个就是连接点。如方法调用前,方法调用后,发生异常时等等。

4 通知(Advice)

  通知被织入方法,该如何被增强。定义切面的具体实现。@Pointcut规则中指明的方法即为切入点,@Before、@After是连接点,而下面的代码就是对应通知。

5 目标对象(Target Object)

  被一个或多个切面所通知的对象,即为目标对象。

6 AOP代理对象(AOP Proxy Object)

  AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。

7 织入(Weaving)

  将切面切入到目标方法之中,使目标方法得到增强的过程被称之为织入。

二 使用方法
1 引入依赖
<!--AOP-->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>
2 自定义注解
package com.swust.exam.annotation;
  
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  
  /**
   * 环绕通知的注解
   */
  @Target({ElementType.METHOD})
  @Retention(RetentionPolicy.RUNTIME)
  public @interface AroundAspect {
      int type() default 1;
  }
3 定义切面和切入点
package com.swust.exam.component;
  
  import com.swust.exam.annotation.AroundAspect;
  import lombok.Data;
  import org.springframework.stereotype.Component;
  
  @Data
  @Component //component注解必须要有
  public class MyTest {
      @AroundAspect(type = 2)
      public void log() {
          System.err.println("输出日志");
      }
  }
4 定义通知
package com.swust.exam.annotation;
  
  import com.alibaba.fastjson.JSONArray;
  import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.annotation.*;
  import org.springframework.stereotype.Component;
  
  /**
   * 1、定义切入点:对要拦截的方法进行定义与限制,如包、类
   * 2、定义通知:编写通知要执行的代码
   */
  @Aspect
  @Component
  public class TestAspect {
      /**
       * 环绕通知
       */
      @Pointcut("@annotation(com.swust.exam.annotation.AroundAspect)")
      private void around(){}
      @Around("around()")
      private Object testAop(ProceedingJoinPoint point) throws Throwable {
          System.out.println("======AopAspectJ执行环绕通知开始=========");
          Object obj = point.proceed();
          Object[] args = point.getArgs();
          //方法名
          String methodName = point.getSignature().getName();
          //对象
          Object target = point.getTarget();
          //类名
          String className = target.getClass().getName();
          System.out.println("类:" + className + ";方法:" + methodName + ";参数:" + JSONArray.toJSONString(args));
          System.out.println("======AopAspectJ执行环绕通知结束=========");
          return obj;
      }
  
      /**
       * 前置通知
       */
      @Before("@annotation(aroundAspect)")
      public void beforeAdvance(AroundAspect aroundAspect){
          System.out.println("======AopAspectJ执行前置通知=========");
      }
  
      /**
       * 后置通知(在目标方法执行后调用,若目标方法出现异常,则不执行)
       */
      @AfterReturning("@annotation(aroundAspect)")
      private void afterRunningAdvance(AroundAspect aroundAspect){
          System.out.println("type=" + aroundAspect.type());
          System.out.println("======AopAspectJ执行后置通知=========");
      }
  
      /**
       * 最终通知(在目标方法执行后调用,无论目标方法是否出现异常,都会执行)
       */
      @Pointcut("@annotation(com.swust.exam.annotation.AroundAspect)")
      private void after(){}
      @After("@annotation(aroundAspect)")
      public void afterAdvance(AroundAspect aroundAspect){
          System.out.println("======AopAspectJ执行最终通知=========");
      }
  
      /**
       * 异常通知:目标方法抛出异常时执行
       */
      @AfterThrowing("@annotation(aroundAspect)")
      public void afterThrowingAdvance(AroundAspect aroundAspect) {
          System.out.println("======AopAspectJ执行异常通知=========");
      }
  }
三 注意事项
  • @Before、@Around、@After之类的注解,需要分别定义对应的自定义注解,不要只使用一个自定义注解来完成@Before、@Around、@After,最终会全部在目标方法前执行。