在复杂的编程环境中,单纯的OOP(面向对象)并不能完成所有的逻辑功能,所以产生了AOP(Aspect Oriented Programming 面向切面编程)的概念。它是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,是Spring重要的功能之一,广泛用于访问控制,事务管理,日志等方面。
我们知道Java设计模式里有一个开闭原则,大致就是对扩展开放,对修改关闭。也就是说我们已经完成的业务如果要增加新功能,是很忌讳直接修改源代码的,因为修改后可能会导致大面积的功能出现错误,所以提倡的是在原来代码不变的情况下,去进行扩展,AOP的产生大多都是基于开闭原则。
我们考虑一个问题,假如我们每个方法都需要打印一个日志,说明方法在何时做了什么,你怎么设计?
用OOP的概念,最麻烦的就是每次操作后都写上一次打印日志的代码,即每个方法中都写上日志打印的相关代码。
如果利用OOP的封装思想,那我们可以把打印日志功能的相关代码单独封装起来,需要的时候只要调用一下就行了。第二种相较于第一种简单了很多,实现了代码的复用。
我们再进一步想,如果每次都需要调用方法也挺麻烦的,那我们能不能把这段代码提取出来,不放到流程中,而是通过设置让它自己判断,只要符合条件不需要手动调用,它自动就能加进去?
其实这种想法和AOP就很相似了,AOP要解决的就是那些散落、渗透在系统各处且必须处理的功能,这种就是横切逻辑,也就是切面。AOP把这些切面横向提取出来,在程序编译或者运行时,自动应用到所需要执行的地方
。它就是在不改变源代码的基础上新增加功能,设计的根本就是代理模式
。
OOP是以一个个对象为基础进行编程,而AOP更多的是把一个流程中的可以影响其他对象的点单独做成一个切面,然后管控一系列对象操作的切面环境,反射出原有对象,并在对象执行前后添加一些逻辑操作,来控制对象的执行。AOP确切来说它是一种设计思想,是对OOP的一种补充和完善。
在之前我们使用JDBC时,用的最多的就是 try、catch、finally语句和资源关闭的操作,这些代码是重复且繁琐的。如果我们把这部分代码拿出来,使用AOP做成切面,通过代理模式的方式动态织入到流程中,那么是不是会好很多呢?
我们把操作数据库做成一个切面,这个切面就是事务要么都成功要么失败,在操作数据库前需要有数据库连接池,取得流等操作,操作完数据库要放回连接池等操作,操作失败要进行回滚,操作成功即成功保存数据。而AOP就可以通过代理模式,来管控各个对象操作的切面环境,为出现异常、正常执行等几种情况提供不同的操作。
AOP有一些专业的术语:
横切关注点:和代码无关,是我们分散在每个各个模块中需要解决同一样的问题。(如:日志、缓存、事务等)
通知(Advice):代理对象在调用真实对象方法前后需要执行的方法(增强)
各种通知的执行顺序:
- Spring版本5.3.x以前:
- 前置通知
- 目标操作
- 后置通知
- 返回通知或异常通知
- Spring版本5.3.x以后:
- 前置通知
- 目标操作
- 返回通知或异常通知
- 后置通知
切面(Aspect):aop的基础单元,一个流程中可以影响所有对象的关注点。是一个封装通知方法的类。
切入点(pointcut):在什么地方执行AOP。(事务是在操作数据库时使用的),通知要增强的地方就是切入点。
连接点(Join Point):需要生成代理对象并拦截的地方,在连接点前后织入通知
织入(weaving):生成代理对象,并将切面的内容放入到流程中的过程。
目标(Target):真实对象
代理(Proxy):目标对象通知后创建的代理对象