Spring系列(八) --- 详述 SpringAOP--面向切面编程的相关概念及基本操作

AOP 其实就是针对程序中的某一个类或者某一个功能做统一的处理, 如针对登录功能在前后端之间可以做一些验证操作, 验证用户名或者密码是否正确.

♞ 相关概念

  • 切面: AOP 主要是针对的某一个功能进行的操作或者定义, 而这个功能就称之为是一个切面, 如用户登录功能, 这就是一个切面;
  • 切点: 切点是切面中的一个方法, 或者说是定义 AOP 拦截的规则; 如果切面是一个数据库的话, 那么切点就是数据库中的表;
  • 连接点: 所有可能触发 AOP 的都可以称之为连接点; 主要用来匹配切面中的多个点, 如果说切点是表的话, 那么连接点就相当于是表中的一行行的数据;
  • 通知: 规定 AOP 执行的时机和执行的方法.

举个例子: 针对用户的登录功能进行校验, 这个用户功能就是一个切面, 可能用户登录的验证需要在多个页面进行校验, 那么这多个页面就相当于是连接点; 当调用用户的登录验证方法时, 整个验证方法就是一个切点, 而方法体就是通知.
在这里插入图片描述

♞♞ 具体操作步骤

第一步: 添加 SpringAOP 依赖;

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.7.4</version>
        </dependency>

第二步: 添加切面和切点;

@Aspect  // 当前是一个切面
@Component
public class UserAspect {
    
    
    // 设置拦截规则, Pointcut 就是一个切点
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
    
    
    }
}

*代表着在 UserController 中的所有方法都要拦截, 后面的 * (…) 指的是可以添加拦截条件, 如 (String, Integer) 两种类型就代表只拦截 UserController 里面的 (String, Integer) 类型的方法.


第三步: 定义通知 Advice, 描述拦截执行的时机和具体的方法实现;

@Aspect  // 当前是一个切面
@Component
public class UserAspect {
    
    
    // 设置拦截规则
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
    
    
    }

    /**
     * 定义 pointcut 切点的前置通知
     * 在执行目标方法之前执行的方法就是前置通知
     */
    @Before("pointcut()")
    public void doBefore() {
    
    
        System.out.println("前置通知: 执行了前置通知!");
    }

    /**
     * 针对 pointcut 切点的后置通知
     */
    @After("pointcut()")
    public void doAfter() {
    
    
        System.out.println("后置通知: 执行了后置通知!");
    }

    /**
     * AfterReturning
     * 在后置通知之前执行的
     */
    @AfterReturning("pointcut()")
    public void doAfterReturning() {
    
    
        System.out.println("执行了 AfterReturning 方法!");
    }

    /**
     * AfterThrowing: 目标方法抛出异常后调用
     * 只有报了异常之后才会执行, 执行在 方法之后,
     * 执行了 AfterThrowing 后, 将不会执行 AfterReturning
     */
    @AfterThrowing("pointcut()")
    public void doAfterThrowing() {
    
    
        System.out.println("执行了 AfterThrowing 方法!");
    }

    /**
     * 环绕通知(将所有通知和方法全部包围起来了)
     */
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
    
    
        // Spring 中的时间统计对象
        StopWatch stopWatch = new StopWatch();
        Object result = null;
        System.out.println("环绕通知: 前置方法!");
        try {
    
    
            stopWatch.start(); // 统计方法的执行时间: 开启计时
            // 执行目标方法, 以及目标方法所对应的相应的通知
            result = joinPoint.proceed();
            stopWatch.stop(); // 统计方法的执行时间: 停止计时
        } catch (Throwable e) {
    
    
            e.printStackTrace();
        }
        System.out.println("环绕通知: 后置方法!");
        System.out.println(joinPoint.getSignature().getName() + " 方法执行花费的时间: " +
                stopWatch.getTotalTimeMillis() + "ms");
        return result;
    }
}

♞♞♞ 关于 AspectJ 表达式

在这里插入图片描述
如:

  • execution(* com.example.demo.User.*(. .)) : 匹配 User 类下的所有方法;
  • execution(* com.example.demo.User+.*(. .)) : 匹配该类的子类及该类的所有方法;
  • execution(* com.example.* .*(. .)) : 匹配 com.example 包下的所有类的所有方法;
  • execution(* com.example. .* .*(. .)) : 匹配 com.example 包下及子孙包下所有类的所有方法;
  • execution(* addUser(String, int)) : 匹配 addUser 方法, 且第一个参数类型是 String, 第二个参数类型是 int.

猜你喜欢

转载自blog.csdn.net/Onion_521257/article/details/129901276