当前位置: 首页 > news >正文

Spring AOP —— 详解、实现原理、简单demo

目录

一、Spring AOP 是什么?

二、学习AOP 有什么作用?

三、AOP 的组成

3.1、切面(Aspect)

3.2、切点(Pointcut)

3.3、通知(Advice)

3.4、连接点

四、实现 Spring AOP 一个简单demo

4.1、添加 Spring AOP  框架依赖

4.2、定义切面

4.3、定义切点

4.3.1、切点表达式说明

4.4、定义通知

4.5、实现原理

​编辑  

五、Spring AOP 实现原理

5.1、原理概述

5.2、织入

5.3、动态代理

5.4、面试题:DK 和 CGLIB 实现的区别


一、Spring AOP 是什么?


AOP 就是面向切面的编程, 是一种思想,是对某一类事情的集中处理。

例如登陆权限的检验(检测当前用户是否登录),在学习 AOP 前,我们会将这样一个检测机制封装成一个方法,在需要检测的地方调用该方法即可,但想象以下,首先,如果这个方法有 1000 个地方要进行调用, 那么你去一个一个写调用函数很累,其次,一旦这个登陆检查方法需要修改,比如增加一个参数,那么,你就需要修改 1000 方法调用的参数... 但如果你会 AOP 后,我们只需要在某处配置一下,就可以实现用户的登录检测,不需要每一个方法中都写登录检验的调用方法啦! 

AOP 是一种思想,而 Spring AOP 是一个框架,提供了对 AOP 思想的实现(类似于 IoC 和 DI 的关系)。

二、学习AOP 有什么作用?


例如刚刚我们所讲到的登录检测机制,这一个方法一旦需要调用的地方多了,不仅写起来麻烦,维护起来成本也是很高的,所以,对于这种功能统一,且使用地方较多的功能,就可以考虑 AOP 来进行统一的处理!

例如以下常见的使用场景:

  • 统一登录检测机制。
  • 统一方法的执行时间统计。
  • 统一的返回格式设置。
  • 统一异常处理。
  • 事务的开始和提交。

Ps: AOP 是对某一功能进行的统一处理,大大降低了代码维护的成本,所以可以说 AOP 是 OOP 的补充和完善~

三、AOP 的组成


3.1、切面(Aspect)

切面,在程序中就是对某一功能进行统一处理的, 这个类里包含了很多方法,这些方法就是由 切点通知 组成。

3.2、切点(Pointcut)

用来进行主动拦截的规则(配置)。

这里拦截的是什么,过程是什么样的呢?就是对用户向服务器发送的请求进行拦截,检测用户的操作是否符合预期,发现问题并统一处理的过程,如下图:

 

3.3、通知(Advice)

通知就是 AOP 的具体执行动作。具体的,在程序中被拦截后会触发一个具体的动作,就是通知中具体实现的业务代码

在 Spring 中,可以在方法上使用以注解,设置方法为通知方法,被拦截后满足条件就会调用通知方法:

  • 前置通知(@Before):执行 目标方法(被拦截的方法)之前执行的方法。
  • 后置通知(@After):执行了目标方法之后执行的方法。
  • 返回之后通知(@AfterReturning):目标方法执行了返回数据(return)时,执行的方法。
  • 抛异常后通知(@AfterThrowing):在执行目标方法出现异常时,执行的方法。
  • 环绕通知(@Around):在目标方法执行的周期范围内(执行之前,执行中,执行后)都可以执行的方法(Ps:如果已经有了前置和后置通知,再使用环绕通知,那么周期范围就在前置通知之前 ~ 后置通知之后)。

3.4、连接点

会触发 AOP 规则的所有的点(所以请求)。

四、实现 Spring AOP 一个简单demo


4.1、添加 Spring AOP  框架依赖

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

Ps:

1.创建 Spring Boot 项目时是没有 Spring AOP 框架可以选择的。

2.添加  Spring AOP 框架可以去中央仓库,值得注意的是要选择 Spring Boot 对应的 AOP ,而不是 Spring 对应的 AOP。

3.最好选择 Spring Boot 对应版本的  AOP ,以上就是 2.7.9版本。

4.2、定义切面

使用 @Aspect 注解修饰类,告诉框架是一个切面类。

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;@Aspect //告诉框架我是一个切面类
@Component
public class UserAspect {}

4.3、定义切点

使用 @Pointcut 修饰一个方法,它不需要由方法体。方法名就是起到一个标识的作用,标识通知方法具体指的是哪一个切点(切点可能有多个)。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect //告诉框架我是一个切面类
@Component
public class UserAspect {/*** 切点:配置拦截规则*/@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut() {}}

4.3.1、切点表达式说明

AspectJ ⽀持三种通配符,如下:

  • * :匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)
  • .. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使⽤。
  • + :表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如 com.cad.Car+ ,表示继承该类的 所有⼦类包括本身

切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法如下(注意使用空格进行分割):

execution(<修饰符> <返回类型> <包.类.⽅法(参数)> <异常>)

其中,修饰符和异常可以省略,具体含义如下:

 

4.4、定义通知

使用通知方法中的五个注解,其中前置通知、后置通知、环绕通知最常用,那么以下代码我将用这三个注解来举例:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect //告诉框架我是一个切面类
@Component
public class UserAspect {/*** 切点:配置拦截规则*/@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut() {}/*** 前置通知*/@Before("pointcut()")public void beforeAdvice() {System.out.println("执行了前置通知");}/*** 后置通知*/@After("pointcut()")public void aftereAdvice() {System.out.println("执行了后置通知");}/*** 环绕通知* @param joinPoint* @return* @throws Throwable*/@Around("pointcut()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("进入了环绕通知~");Object obj = null;obj = joinPoint.proceed();System.out.println("退出了环绕通知~");return obj;}}

执行结果如下:

4.5、实现原理

  

五、Spring AOP 实现原理


5.1、原理概述

Spring 的切面是代理类实现的,包裹了目标对象,也就是说,用户只能先通过代理类,进行校验,如果没有问题才会进一步访问到目标对象。

5.2、织入

织入简单理解就是代理生成的时机,一般情况下,在 Spring AOP 动态代理的植入时机是程序的运行期。

5.3、动态代理

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

Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。默认情况下,实现了接口的类,使用 AOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。

JDK 和 CGLIB 底层都是基于反射实现的。、

5.4、面试题:DK 和 CGLIB 实现的区别

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

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

http://www.lryc.cn/news/30889.html

相关文章:

  • (蓝桥真题)异或数列(博弈)
  • 4万字数字政府建设总体规划方案WORD
  • CCF/CSP 201709-2公共钥匙盒100分
  • 【OC】Blocks模式
  • 软件设计师教程(七)计算机系统知识-操作系统知识
  • 蓝桥杯2023/3/2
  • 【IoT】创业成功不可或缺的两个因素:能力和趋势
  • 2020蓝桥杯真题日期格式 C语言/C++
  • 总时差与自由时差
  • LeetCode两个数组的交集-跳跃游戏- 最长有效括号
  • mysql普通索引与唯一索引怎么选择
  • JavaWeb开发(三)3.5——Java的反射机制
  • Python每日一练(20230305)
  • SpringBoot三种方法实现定时发送邮件的案例
  • opengl、opengl es、webgl介绍与opengl开发入门
  • Vue3之组件间传值
  • Windows10下使用CMake编译ITK5.2.1步骤
  • 字符串模式匹配,经典KMP算法你还不会?我可不允许你不会!
  • C++操作redis(实现连接池、分布式锁)
  • 硬件基础专题-01电阻篇
  • 【JAVA程序设计】(C00112)基于Springboot+Thymeleaf的在线购物商城——有文档
  • shell基础(5)算数计算:运算语法、自增自减
  • virtio设备input节点
  • 《计算机网络:自顶向下方法》学习笔记——第一章:计算机网络和因特网
  • PDF 解析格式化输出 API 数据接口
  • RL笔记:基于策略迭代求CliffWaking-v0最优解(python实现)
  • 350. 两个数组的交集 II
  • Android仿微信选择图片
  • python+嵌入式——串口通信篇(收发解包)
  • 剖析G1 垃圾回收器