Spring AOP

目录

[1. 什么是Spring AOP](#1. 什么是Spring AOP)

[2. AOP的组成](#2. AOP的组成)

[2.1 切面(Aspect)](#2.1 切面(Aspect))

[2.2 连接点(Join Point)](#2.2 连接点(Join Point))

[2.3 切点(Pointcut)](#2.3 切点(Pointcut))

[2.4 通知(Advice)](#2.4 通知(Advice))

[3. Spring AOP的实现](#3. Spring AOP的实现)

[3.1 添加Spring AOP框架支持](#3.1 添加Spring AOP框架支持)

[3.2 定义切面和切点以及通知](#3.2 定义切面和切点以及通知)

[4. Spring AOP的实现原理](#4. Spring AOP的实现原理)


1. 什么是Spring AOP

AOP(Aspect Oriented Programming):面向切面回答,它是⼀种思想,它是对某⼀类事情的集中处理。比如用户登录权限的效验,没学 AOP 之前,我们所有需要判断用户登录的页面(中的方法),都要各自实现或调用用户验证的方法,然而有了 AOP 之后,我们只需要在某⼀处配置⼀下,所有需要判断用户登录页面(中的方法)就全部可以实现用户登录验证了,不再需要每个方法中都写相同的用户登录验证了。

AOP是一种思想,而Spring AOP是一个框架,提供了对AOP思想的实现.就相当于IOC和DI类似.

AOP除了统一的登录判断之外,AOP还就可以实现:

2. AOP的组成

1. 切面

2. 切点

3. 连接点

3. 通知

2.1 切面(Aspect)

切面由切点和通知组成,即包含了横切逻辑的定义,页包括了连接点的定义.

切面是包含:通知,切点和切面的类,相当于AOP实现的某个功能的集合.

通俗的说,在程序中就是处理某个具体问题的一个类,里面包含了许多方法,这些方法就是切点和通知.

2.2 连接点(Join Point)

应用执行过程中能够插⼊切面的⼀个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

通俗的来说: 有可能触发拦截规则(AOP规则)的所有点(就是所有的请求)

2.3 切点(Pointcut)

切点就是一种规则,用来匹配连接点,当连接点符合切点的规则的时候就会给这个连接点进行添加通知.

通俗的来说:切点的作用:进行主动拦截的规则,承上启下,匹配连接点,添加通知

2.4 通知(Advice)

切面也是有目标的 ------它必须完成的工作。在 AOP 术语中,切面的工作被称之为通知。

通俗的来说:主动进行拦截,拦截成功执行的方案就是通知. === AOP具体的处理动作

通知的执行,可以分为五种.

    1. 前置通知: @Before
    1. 后置通知: @After
    1. 返回之后通知: @AfterReturning
    1. 抛异常后通知: @AfterThrowing
    1. 环绕通知: @Around

3. Spring AOP的实现

接下来我们使⽤ Spring AOP 来实现⼀下 AOP 的功能,完成的目标是拦截所有 UserController 里面的方法,每次调用UserController 中任意⼀个方法时,都执行相应的通知事件。Spring AOP 的实现步骤如下:

3.1 添加Spring AOP框架支持

XML 复制代码
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 定义切面和切点以及通知

我们创建一个类,给这个类进行添加@Aspect(表示这是一个切面)@Component(表示这个类随着Spring的启动而启动)

  1. 创建切面
  1. 构造切点

以上我们定义了拦截的规则,表示我们将拦截com.example.demo.controller.UserController这个类中的所有方法

切断点的表达式说明:

AspectJ 支持三种通配符

* :匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)

.. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使用。

  • :表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如 com.cad.Car+ ,表示继承该类的所有子类包括本身

execution(* com.cad.demo.User.*(..)) :匹配 User 类里的所有方法。

execution(* com.cad.demo.User+.*(..)) :匹配该类的子类包括该类的所有⽅法。

execution(* com.cad.*.*(..)) :匹配 com.cad 包下的所有类的所有方法。

execution(* com.cad..*.*(..)) :匹配 com.cad 包下、子孙包下所有类的所有方法。

execution(* addUser(String, int)) :匹配 addUser 方法法,且第⼀个参数类型是 String,第⼆个参数类型是 int。

  1. 定义通知

以上是定义了两个简单的通知,我们平常使用的是环绕通知

完整代码:

java 复制代码
package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: YAO
 * Date: 2023-07-13
 * Time: 19:18
 */
// 1. 定义切面
@Aspect     // 表示当前类是一个切面
@Component  // 当前类随着Spring的启动而启动
// 两个注解缺一不可
public class UserAspect {
    // 2.定义切点
    // 使用注解@Pointcut 加 AspectJ 表达式语法 定义拦截规则
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    //拦截这个目录下的所有方法 com.example.demo.controller.UserController.
    public void pointCut(){
    }

    // 4.定义通知
    // 通知一定要对应相应的岚切规则,一个拦截规则可以执行多个通知
    // 4.1 执行前通知
    // @before()参数:针对拦截规则的方法名
    @Before("pointCut()")
    public void doBefore(){
        System.out.println("执行前置通知");
    }

    // 4.2 执行后通知
    @After("pointCut()")
    public void doAfter(){
        System.out.println("执行后置通知");
    }

    // 4.3 环绕通知
    @Around("pointCut()")
    // 环绕通知必须要有返回值,交给框架进行下一步操作
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object object = null;
        System.out.println("Around ⽅法开始执⾏");

        try {
            object = joinPoint.proceed();
        } catch (Throwable e ) {
            e.printStackTrace();
        }
        System.out.println("Around ⽅法结束执⾏");
        return object;
    }
}

浏览器进行发送请求

控制台进行打印

4. Spring AOP的实现原理

Spring AOP 是构建在动态代理的基础上的,因此Spring对AOP的支持局限于方法级别的拦截.

那么什么是动态代理呢?

动态代理:就不进行直接访问,通过代理的方式进行访问

举例: 访问国外网站,国内无法直接进行访问,我们可以通过添加代理的方式进行访问,使用国外的服务器进行转发请求进行访问.

AOP就是构建在动态代理的基础之上的,就是通过使用切点设置拦截规则,使得用户不能直接访问,而是必须进行访问切点这个代理才能,进行验证之后进行访问.

动态代理在Spring AOP中有两种:

1.JDK Proxy (默认情况下实现接口的类使用)

2.CGLIB (没有实现接口的类会基于这种方式生成代理类)

织入: 代理的生成时机.

Spring AOP生成代理的时机是在运行,就是切面在应用运行的某一时刻被织入,一般请开你改下,在织入切面的时候,AOP容器回味目标对象动态的创建一个代理对象.

JDK 和 CGLIB 实现的区别

  1. JDK 实现,要求被代理类必须实现接口,之后是通过 InvocationHandler 及 Proxy,在运行时动态的在内存中生成了代理类对象,该代理对象是通过实现同样的接口实现(类似静态代理接口实现的方式),只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。

  2. CGLIB 实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象。

  3. Spring中默认使用jdk动态代理 SpringBoot进行了改进默认使用CGLIB

相关推荐
大白的编程日记.24 分钟前
【C++笔记】C++编译器拷贝优化和内存管理
java·开发语言·c++·笔记
EPSDA2 小时前
Java的IO流(二)
java·开发语言
菜♕卷3 小时前
SpringCloud-07 GateWay01 网关技术
spring·spring cloud·junit
zzlyyds3 小时前
SpringBoot---------Actuator监控
java·spring boot·spring·actuator
vitobo3 小时前
Java的JDBC编程
java·开发语言
呆萌小新@渊洁3 小时前
后端接收数组,集合类数据
android·java·开发语言
A_cot4 小时前
深入了解 Maven 和 Redis
java·redis·maven
liuyang-neu4 小时前
力扣中等 33.搜索旋转排序数组
java·数据结构·算法·leetcode
爱吃烤鸡翅的酸菜鱼4 小时前
java(3)数组的定义与使用
java·开发语言·idea·intellij idea
ganjiee00075 小时前
力扣(leetcode)每日一题 2414 最长的字母序连续子字符串的长度
java·算法·leetcode