工作中使用Spring Boot将近一年了,由于某些需要也接触过Spring Cloud。Spring到Spring Boot再到Spring Cloud,逐渐地变成了一套完整的企业分布式系统的解决方案,已经不能单纯的认为是基于Spring的一套快速配置脚手架了。虽说如此,但是两者都脱离不开Spring最基本的两个概念:IOC和AOP。也因此,如果希望更加了解和掌握Spring,这两个概念必须学习一下,本篇会简单介绍下AOP的思想,主要基于《Spring实战(第四版)》中的相关章节与一些个人理解。
什么是AOP
简单一句话概括:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。
上面的概括可以通俗的了解到什么是AOP,但是并不严谨,AOP不一定都在运行时生成代理对象来织入的,也可以在编译期,类加载期织入,这一点Spring AOP和AspectJ就不完全相同。
为什么要有AOP
AOP是对OOP(面向对象编程)的补充和完善,当一个集合有大量的公共行为和属性时,我们可以通过封装,继承,多态等来表明集合内对象的层次结构。我们最常说的,实现一个父类,然后子类继承它,父类/子类也可以很好的说明,OOP使得我们可以很好的定义一个集合内元素的纵向关系,减少代码的冗余,提高复用性,易扩展性。然而对于某些横向的关系,OOP并不能很好的满足,就会出现大量的重复代码,各个模块的复用性也会降低。
举个例子: 有三个人爷爷,爸爸和儿子,自然就是儿子继承爸爸,爸爸继承爷爷,这三者一生都会经历出生,入学,工作,结婚等等一系列人生阶段。现在当我们需要记录这三者的每个人生阶段发生的时间节点,只能在每个阶段发生时刻记录一下,这就是最简单的日志功能。必然会导致代码的重复性,并且以“入学”为例,“入学”应该只需要知道入学本身的步骤,比如:体检,面试,交学费。而不是: 记录开始时间,体检,面试,交学费,记录结束时间。日志功能对于各个函数都应该是非透明的,函数本身只需要实现核心关注点即业务逻辑本身,这些散落在各个方法核心功能上,却又与核心业务逻辑无关的功能,便是横切关注点。
简而言之,我们把那些与业务逻辑无关的,却被各个业务模块大量调用的逻辑给封装起来,进而便于减少系统的重复代码量,并且能够降低模块间的耦合度,并有利于未来的扩展和维护,降低了维护成本。最重要的我认为,各个函数本身只关注了核心业务逻辑。
AOP术语以及解释
1)连接点(Joinpoint)
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入通知。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
2)切点(Pointcut)
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。
连接点是一个比较空泛的概念,就是定义了哪一些地方是可以切入的,也就是所有允许你通知的地方。
切点就是定义了通知被应用的位置 (配合通知的方位信息,可以确定具体连接点)
3)通知(Advice)
通知是织入到目标类连接点上的一段程序代码,在Spring中,通知除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
Spring切面可以应用5种类型的通知:
前置通知(Before):在目标方法被调用之前调用通知功能;
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning):在目标方法成功执行之后调用通知;
异常通知(After-throwing):在目标方法抛出异常后调用通知;
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
通知就定义了,需要做什么,以及在某个连接点的什么时候做。 上面的切点定义了在哪里做。
4)目标对象(Target)
通知逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
5)引介(Introduction)
引介是一种特殊的通知,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
允许向现有类中添加方法和属性(通知中定义的)
6)织入(Weaving)
织入是将通知添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、通知或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
a、编译期织入,这要求使用特殊的Java编译器。
b、类装载期织入,这要求使用特殊的类装载器。
c、动态代理织入,在运行期为目标类添加通知生成子类的方式。
把切面应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
7)代理(Proxy)
一个类被AOP织入通知后,就产出了一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
8)切面(Aspect)
切面由切点和通知组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
切点的通知的结合,切面知道所有它需要做的事:何时/何处/做什么
本文介绍的比较简单,单纯的介绍了一下AOP的概念,至于如何声明切面,定义切点等等,有时间会写一个小demo分享。
我所有文章的个人理解和举例都可能存在偏差以及错误,欢迎指正。另外如果有想指正,探讨,提问,都欢迎评论。我都会尽我所能一一回复。