![49677218bfde61dd18bed938101f6230.png](https://i-blog.csdnimg.cn/blog_migrate/84db4acf73c65149cc1d5b0d704f2014.jpeg)
IoC和AOP是Spring框架的两大核心特性,在之前我浅析了Spring IoC机制和实现,顺便对Bean的生命周期做了一个简单的源码探索,在这篇文章中我将对另一特性——AOP做一下浅析,以便于能够做到对AOP有一些非表面性的理解。
(写得不好请多见谅)
AOP的背景
谈到某一项技术,我们必须清楚其背景和产生的原因,这是了解一项技术的出发点。AOP的含义是面向切面编程,是面向对象编程的一个衍生的方法
何为切面?谈到这个我们就不得不提及在OOP开发过程中所遇到的一个普遍需求,那就是针对既有代码的功能增加。比如说对某个函数增加日志记录功能和权限监测功能,这些功能其实和原有的代码逻辑关联不大,但是如果直接写到函数中就不可避免地导致耦合,代码的可维护性和可用性就会下降,这样的做法也被称为有较强的“侵入性”。
降低耦合是我们的追求,那么,我们能不能有一种优雅的方式既达成这个目的,又能降低耦合呢?于是AOP思想和相关的技术就出现了,以解决这种问题。
所谓切面,就是我们在进行功能横向扩展时所面对的对象(可能是一个函数等),我们要做的是直接将新的代码横切到这个对象中,而不影响原来代码的运行,这样就优雅地实现了低耦合的代码增添(也被称为“非侵入式”,其实Spring框架很大一部分追求就是追求“非侵入式”)。因此AOP技术越来越流行,Spring框架也将AOP的功能融入到了自身中,并将其作为核心功能之一。
AOP的基本概念
AOP首先是一种思想,而后才是一种技术。在使用和理解AOP之前,我们务必要搞清楚AOP思想中的一些基本概念,才能在之后对AOP有更深的理解。先放一张图,这张图讲述的很清楚:
![7506735c64416c96f63794b3657a60c2.png](https://i-blog.csdnimg.cn/blog_migrate/abc367eaa6a71c14ca807a5bdea8e6b3.png)
Target
Target就是我们横切和织入新代码的对象
Joinpoint
Joinpoint就是连接点,就是在target上具体的执行点,与Pointcut匹配,我们的连接点一般在函数上,最终的织入的代码将在函数周围进行增加和增强,Spring中也只支持方法级别的Joinpoint
Aspect
Aspect即切面,其中包装着Advice和Pointcut。在Spring AOP中常常被称为Advisior,是通过Bean注册的基本单位
Pointcut
Pointcut是是切入点,与Jointpoint对应,定义了切面逻辑的执行地点
Advice
Adivice是具体的切入逻辑,切面所完成的工作包含在Advice中
AOP的本质:动态代理
在研究一项技术和思想时,我们务必要摸清其本质。AOP技术实现的底层原理是动态代理,使用动态代理实质上就是调用时拦截对象方法,对方法进行改造、增强。
动态代理是代理模式的一种实现,除此之外我们还有静态代理。
代理模式就是在原有的对象之外包裹一层代理对象,代理对象内存在被代理对象,那么代理对象就可以为被代理对象执行一系列其无法做到的事情。举烂的例子就是房产中介,原本购房者可以自己去执行购房这个方法,但如果购房者通过房产中介,就能够进行更多更丰富的操作:譬如签订法律协定、提前的房源筛选等等,这样实际上购房者通过代理对象也实现了本身的购房方法,但同时做了一些其他的事情,这就是代理模式的用处,这个时候听起来就蛮像AOP的。
最开始实现代理模式,我们使用静态代理,即直接代理指定的对象,但这个方法存在的问题就是代理对象必须一一对应,每个类都需要一个对应的代理类,这样就非常繁杂且难以管理,因此我们在AOP的实现上为了追求简洁,选择了动态代理。
动态代理解决了静态代理的问题,我们进行代理操作调用时,只需要传入对应的对象即可,然后动态代理对象根据传入对象自动构建代理对象。要理解动态代理,我们必须先搞清楚几个要素(这里直接结合动态代理在Java中的实现来讲):
- 被代理对象:这个很重要,不用解释
- 代理对象:由Proxy类调用newProxyInstance方法生成一个具体的代理对象后(传入classLoader、class对象,调用处理器),即可通过代理对象进行方法调用
- InvocationHandler:调用处理器,实现具体代理逻辑的地方,在这个对象中我们使用invoke方法来调用代理类内部的方法(也就是被代理对象的方法),并在此之外做一些额外的代理操作
满足并通过这三个要素,一个动态代理就能够被实现
JDK和CGLIB动态代理
在Java中,有两种实现动态代理的方式:
- JDK动态代理:通过类加载器和接口创建代理类,必须要传入接口,生成效率高,但执行效率不高
- CGLIB动态代理:对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理,可以不传入接口,生成效率不高,执行效率高
具体到Spring中
讲完了一些抽象的东西后,我们进入实际的Spring AOP中讲一讲Spring中的AOP实现是怎样的:
Spring AOP概述及实现机制
下面会针对AOP基本概念分别来讲解:
Pointcut:切入点
在Spring中的Pointcut一般会直接匹配自定义的注解或者利用正则匹配指定包下的类的方法,这些被匹配的方法会直接被织入定义好的AOP操作。Pointcut一般会在Aspect中通过注解指定
在SpringAOP的Pointcut类中,我们一般关注:
- ClassFilter:匹配执行织入操作的对象(一般为JoinPoint)
- MethodMatcher:匹配被织入的详细方法
- 常见的Pointcut类型
- 通过方法名匹配:直接匹配方法名,通过正则匹配方法名
- 通过注解匹配:通过匹配注解来匹配到具体的方法上
- ComposablePointcut:我们可以同过这种机制来进行Pointcut作用域间运算,对不同的Pointcut进行集合运算得到对应结果
- 在Spring中,可选择自行扩展Pointcut
Advice:通知,切面完成的工作
Advice一般在Aspect类中直接通过@After,@Before等注解下的方法直接定义,在这些方法内即可编写指定的逻辑
在Spring中,Advice分为两类:
- pre-class类:只提供方法拦截功能,不会为目标对象保存任何状态或添加任何新特性
- beforeAdvice
- ThrowsAdvice
- AfterReturningAdvice
- AroundAdvice
- pre-instance:为不同的实例对象保存各自的状态及相关逻辑
- Introduction:为目标类添加新的属性和行为
- DelegatingIntroduction:委派版
Aspect:切面
- Spring中叫Advisor,包装了Advice和Pointcut,IoC管理AOP相关类的单位通常是以Aspect为基本单位
织入操作
在Spring中进行最终的具体织入操作,我们通常是使用ProxyFactory,我们会传入要进行织入的目标对象(target)以及将要应用到对象的Aspect,这个过程实际上就是一个动态代理过程。织入过程完成后,会返回织入了横切逻辑的目标对象的代理对象,然后执行代理对象,我们就完成了AOP
Bean如何被包装为proxy
将bean封装为Advisor类,然后添加到proxyFactory中(proxyFactory能够提供一些针对proxy的额外操作,譬如自定义一些操作处理,目标信息,过滤器等等),最终调用proxyFactory的getProxy方法获取bean对应的proxy