⭐️前面的话⭐️
本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍面向切面编程的思想和相关概念,附加一个小案例。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年5月12日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《无》
💬参考在线编程网站:🌐牛客网🌐力扣🌐acwing
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
📌导航小助手📌
- 1.什么是面向切面编程
- 2.AOP的作用
- 3.理解面向切面编程
- 3.1连接点,切点,切面,通知
- 3.2SpringAOP的实现原理
1.什么是面向切面编程
认识面向切面编程之前,我们先来说说另外两个概念,相信对于学习计算机的小伙伴们,一定是认识的。
面向过程编程,这个熟悉吧,相信很多小伙伴都学习过C语言,C语言就是一门面向过程的语言,重点在于将一个任务按照步骤进行拆解,即分析解决问题的步骤,然后按照步骤将每一步使用函数封装,最后按照顺序一步步执行,这就是面向过程,它突出的重点是过程。
面向对象编程,这个应该也很常见吧,面向对象与面向不同的是,面向对象在解决问题之前,会把问题中的各个事物抽象出来,这些抽象出来的事物就叫做类模板,通过模板将事物实例出来就成了对象,建立对象的目的并不在于为了完成某一个步骤,而是为了方面描述某个某个事物在问题中的行为特征。
干说有点抽象,举个栗子吧,假设在下五子棋,那按照面向过程的思路就是开始游戏,黑子落子,描述画面,白子落子,描述画面,…,最终某方连成五子,获得游戏胜利。
面向对象的思路,先将五子棋中的事物进行抽象,五子棋用到的事物有黑白双方,黑白子棋子,棋盘,规则,先将这些事物抽象成类,每个类负责不同的功能模块,黑白双方类负责描述玩家的行为,也就是我们玩家下棋时候的行为,棋子类描述下五子棋时的棋子,棋盘类充当五子棋中下棋棋盘,规则类就是描述游戏的规则,然后基于这些类,根据需要实例化对象,来完成描述下五子棋这一行为。
如我们需要两个玩家,就实例出两个玩家,每个玩家有自己的信息和动作,在面向对象语言当中,信息使用属性来描述,动作使用方法来描述,当轮到某一玩家下棋时,就调用该玩家对应的方法来充当它这个下棋的动作,下棋过程中会显示玩家信息,通过获取玩家对象的属性就能将信息展现出来。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。
面向过程编程与面向对象编程都是算一种编程的思想,或者说是编程的规范。而对于面向切面过程(Aspect Oriented Programming),简称AOP,它也是一种编程思想。
前面介绍Spring框架的时候,强调过,Spring中有一个核心的模块,就是AOP。
想要理解AOP到底是什么还得了解几个概念,现在只能说它是一种编程思想,它能够在不改原有代码的前提下对其功能进行增强。
就是你代码已经写好了,使用AOP可以在不改动代码的前提下增强功能,如对于一个功能,可以基于AOP完成对该功能执行效率的计算,能够在功能正式执行前或者执行后,添加其他的功能执行,能够在该功能发生异常后,对其异常进行处理。
2.AOP的作用
一句话,在不惊动原始设计的基础上为其进行功能增强。
实现上,就是基于动态代理进行实现。
想象一个场景,我们在做后台系统时,除了登录和注册等几个功能不需要做用户登录验证之外,其他几乎所有页面都需要先验证用户登录的状态,那这个时候我们要怎么处理呢?
如果不使用AOP,我们就需要在每一个Controller层都写一遍验证用户是否已经登录的程序,如果你实现的功能有很多,并且这些功能都需要进行登录验证,那你就需要编写大量重复的代码,非常的麻烦,尽管你可以将登录验证实现的逻辑封装在一个方法中,但是你要在很多地方调用这个方法,还是很麻烦。
如果使用AOP,在进入核心的业务代码之前会做统一的一个拦截,去验证用户是否登录,这样就很方便,仅需做一个拦截工作,再将验证代码一执行即可。
除了登录验证功能之外,还有很多功能也可以使用AOP,比如:
- 统一日志记录与持久化。
- 统一方法执行时间统计。
- 统一数据返回格式。
- 统一处理程序中的异常。
- 统一事务的开启与提交。
也就是说使用 AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP (Object Oriented Programming,面向对象编程)的补充和完善。
3.理解面向切面编程
3.1连接点,切点,切面,通知
想要理解面向切面过程,那先得搞清楚几个概念,我们以实际的例子来进行说明,假设我们需要做一个增强功能,就是测定任意一个一方法的运行时间,当然最容易想到的最粗暴的方式就是在每一个方法当中加上这个测定功能,实现逻辑也很简单,执行业务代码前先获取一个时间戳,执行完毕后再获取一个时间戳,然后一相减,得到的就是程序的运行时间。
但是这样太麻烦了,每个方法中你都写一个实现不得累死,而基于AOP能够不改动任何的源代码,完成该功能的实现。
假设我们有以下的方法需要进行功能增强:
@Controller
public class AddController {
public void adding() {
int a = 1;
for (int i = 1; i <= 1000000; i++) {
a++;
}
System.out.println("增加百万次操作!");
}
public void addPlus() {
//记录执行前程序的时间戳
long start = System.currentTimeMillis();
adding();
//记录完成任务后程序的时间戳
long end = System.currentTimeMillis();
System.out.println("Plus:增加百万次操作!,时间为:" + (end - start) + "ms");
}
public void playing() {
System.out.println("playing ... ...");
}
public void running() {
System.out.println("running ... ...");
}
public void sharing() {
System.out.println("sharing ... ...");
}
}
其中的addPlus方法就是通过手动实现的方式完成执行时间的计算,下面我们来一步一步说明在不改动代码的情况下,完成对其他方法执行时间的统计。
通过手动实现统计的方式肯定是没有问题的,执行效果如下:
基于AOP实现的效果:
通过使用AOP,在不改变任意原代码就可以实现对方法的功能进行增强。
下面来介绍一组概念,这些都是AOP的专用术语:
- 连接点,所谓连接点,就是指程序能够运行到的任意位置,不限于方法,异常,属性,类,对象等,通俗点说,就是程序执行到任意一句代码的位置,对于任意一个位置,就叫做连接点,你所选的位置包含范围越大,则该连接点的粒度也就越大,如直接选择一个类作为连接点,那这个连接点的粒度就比较大,选择方法作为连接点,这个连接点的粒度就越小,粒度选择的越小,对功能的增强就越精准,在spring当中连接点可以理解为方法的执行。
- 切点,需要进行功能增强的连接点即切点,说白了,就是你准备对某个方法进行功能增强,那这个方法就是一个切点,很明显连接点包含切点。
- 通知,执行功能增强的一段代码就是通知,通常都是将其封装成方法,这个实现功能增强的方法就是通知。
- 通知类,通知所处的类就是通知类。
- 切面,通知与切点的连接就是切面,通知有了,切点有了,将这两者建立的连接关系就是切面。
下面用几张图和代码来描述这几个核心概念:
连接点即所有的方法,切点就是被增强的方法。
通知就是实现增强功能的方法,通知所在类即通知类。
关系图:
在Spring中一个切点可以匹配一个方法,也可以匹配多个方法。
通知包含前置通知,后置通知,返回之后通知,抛异常后通知与环绕通知五类。
在Spring切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会调用对应满足条件的方法:
- 前置通知使用@Before∶通知方法会在目标方法调用之前执行。
- 后置通知使用@After∶通知方法会在目标方法返回或者抛出异常后调用。
- 返回之后通知使用@AfterReturning∶ 通知方法会在目标方法返回后调用。
- 抛异常后通知使用@AfterThrowing∶ 通知方法会在目标方法抛出异常后调用。
- 环绕通知使用@Around∶通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。
后续文章会说明这几类通知如何使用。
3.2SpringAOP的实现原理
AOP是一种编程思想,而SpringAOP是AOP的一种实现。
AOP的实现本质上就是基于代理实现的,下面来简单说下原理。
首先,要有一个目标对象,在上面的例子中,目标对象就是addController
,然后通过基于这个目标对象,创建一个代理类,并在某一规定的时机生成,这个时机就是织入,最后在在这个代理类上加上以下增强的方法,这个过程就叫做引入。
下面又出来了一组相关概念:
- 目标对象:代理的目标对象。
- 织入(weaving): 即代理的生成时机,织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入∶
- 编译期∶切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
- 类加载器∶切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入(load-time weaving.LTW)就支持以这种方式织入切面。
- 运行期∶切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。SpringAOP就是以这种方式织入切面的。
- 引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。