WQhuanm
Spring AOP的使用

Spring AOP的使用

一,AOP基础概念

  1. 连接点(Join Point):可以认为ioc容器的所有类(不包括切面类)的所有方法执行(运行时的方法)都是JoinPoint,他们都能被增强
  2. 切点(Poincut):定义了切入哪些连接点,即增强哪些方法(通过表达式或注解声明这些方法)
  3. 通知(Advice):定义了如何切入(before,after,around…)
  4. 切面(Aspect):由@Aspect定义的类即为切面类,切面类声明了如何用切点和通知来增强连接点
  5. 织入(Weaving):AOP的创建代理对象的方式(编译期织入、类加载期织入、运行期织入)

二,AOP的使用

1. aop环境配置

  1. pom.xml
    1
    2
    3
    4
    5
    <!--aop 切面-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
  2. Application启动类开启注解
    1
    @EnableAspectJAutoProxy(exposeProxy = true)//启用AOP自动代理,允许暴露代理类

2. 切面的声明

基本步骤就是声明为切面类,定义切点表达式,编写advice通知方法

  1. 切点表达式(指明增强的方法)

    1. execution表达式
      • execution(* com.wqhuanm...(..)) 任意返回类型,com.wqhuanm的包及其子包,任意类 ,任意方法 ,(..)任意参数
      • execution(public int com.wqhuanm.MyserviceImpl.*(String name, ..)) public且返回为int,指定类的任意第一个参数为string的方法
    2. @annotation注解
      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Pointcut("@annotation(com.learn.annotation.SystemLog)")//增强使用了这个注解的方法
      public void pt() {
      }

      @Target({ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface SystemLog {
      String BusinessName();// 接口调用该注解时,声明其相应功能,如@SystemLog(BusinessName = "更新个人信息")
      }
  2. 连接点JoinPoint

    • JoinPoint:用于获取目标对象/代理对象/方法签名/参数
    • ProceedingJoinPoint:JoinPoint的子接口,提供proceed():执行目标方法
  3. advice通知写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Aspect//声明为切面类
    @Component
    public class LogAspect {
    @Pointcut("execution(* com.wqhuanm.learn..*.*(..))")
    public void logPt(){}//函数名用于声明该切点的名字

    @Before("logPt()")
    public void beforePrint(JoinPoint jp){
    System.out.println("before print: "+jp.getSignature().getName());
    }

    @Around("logPt()")
    public void aroudPrint(ProceedingJoinPoint jp){
    Object ans=null;
    System.out.println("Before");
    try {
    ans= jp.proceed();
    System.out.println("AfterReturning");
    } catch (Throwable e) {
    System.out.println(e.getMessage());
    System.out.println("AfterThrowing");
    }finally {
    System.out.println("After");
    }
    }
    }

三,多个AOP的执行顺序

  1. AOP的优先级由他们的order值决定,order值越低,优先级越高。spring aop(包括事务)的优先级默认都是Integer.MAX_VALUE(即优先级最低)

  2. 如果切面间优先级相同,则由他们注册到Spring的顺序决定谁优先级高(越先注册越高),事务之类的基础设施切面的注册往往早于自定义切面的注册时机

  3. 多个切面可以看成是以方法执行为圆心,优先级越高的切面在外环,越低的在内环。而切面的执行可看成一个箭头穿过圆环得到的顺序(优先级越高,越先开始,但是越晚结束)

  4. 优先级的定义

    • 自定义切面可以在切面类上注解@Order(value)来设定order值(直接作用于方法上无效)
    • Spring的事务优先级可在启动类上设置@EnableTransactionManagement(order = value)来设置order值
  5. 事务与自定义切面同时使用可能存在的问题

    1. 在均为设定优先级时,事务切面比自定义切面早注册,在外环
    2. 如果事务内容抛出异常,会先被自定义切面捕获,如果切面处理异常后没有把异常抛出,则事务切面接受不到异常,不会执行回滚
    3. 解决思路
      • 要么提高自定义切面优先级,让事务处理后再到切面catch异常
      • 要么自定义切面要抛出异常

附录:使用AspectJ

只需引入下面依赖

1
2
3
4
5
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>

参考文章

彻底征服 Spring AOP 之 理论篇

本文作者:WQhuanm
本文链接:https://wqhuanm.github.io/Issue_Blog/2025/04/01/25_Spring.AOP的使用/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可