Spring源码(11)--Spring AOP及动态代码的基础知识
AOP基础知识
- 连接点(Joinpoint)
特定点是程序执行的某个特定位置,如类开始初始化前、类初始化后、类的某个方
法调用前/调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特
定点,这些代码中的特定点就被称为“连接点”。Spring仅支持方法的连接点,即仅能
在方法调用前、方法调用后、方法抛出异常时及方法调用前后这些程序执行点织入增强。
连接点由两个信息确定:一是用方法表示的程序执行点:二是用相对位置表示的方
位。Spring使用切点对执行点进行定位,而方位则在增强类型中定义。
- 切点(Pointcut)
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,
即连接点是程序类中客观存在的事物。但在为数众多的连接点中,如何定位某些感兴趣
的连接点呢?AOP通过“切点”定位特定的接连点。借助数据库查询的概念来理解切点
和连接点的关系:
连接点相当于数据库中的记录,而切点相当于查询条件。
切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。
在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类
和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责解析切点所设定的查询
条件,找到对应的连接点。确切地说,应该是执行点而非连接点,因为连接点是方法执
行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如
果希望定位到具体的连接点上,还需要提供方位信息。
- 增强(Advice)
Advice,增强,有些人也会称之为通知。
在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,
这便是执行点的方位。结合执行点的方位信息和切点信息,就可以找到特定的连接。正
因为增强既包含用于添加到目标连接点上的一段执行逻辑,又包含用于定位连接点的方
位信息,所以Spring所提供的增强接口都是带方位名的,如BeforeAdvice(前置增强)、
AfterReturningAdvice(返回增强)、ThrowsAdvice(异常增强)等。
BeforeAdvice表示方法调用前的位置,而AfterReturningAdvice表示访问返回后的位置。
所以只有结合切点和增强,才能确定特定的连接点并实施增强逻辑。
-
目标对象(Target).
增强逻辑的织入目标类。如果没有AOP,那么目标业务类需要自己实现所有的逻辑,
在AOP的帮助下,重复的横切逻辑则可以使用AOP动态织入特定的连接点上。 -
引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本
没有实现某个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现
逻辑,让业务类成为这个接口的实现类。 -
织入(Weaving)
织入是将增强添加到目标类的具体连接点上的过程。AOP就像一台织布机,将目标
类、增强或者引介天衣无缝地编织到一起。“织入”这个词太精辟了。根
据不同的实现技术,AOP有3种织入方式。
(1)编译期织入,这要求使用特殊的Java编译器。
(2)类装载期织入,这要求使用特殊的类装载器。
(3)动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。 -
代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增强逻辑的
代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是
原类的子类,所以可以采用与调用原类相同的方式调用代理类。 -
切面(Aspect).
切面由切点和增强(引介)组成,它既包括横切逻辑的定义,也包括连接点的定义。
Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入切面所指定的连
接点中。
Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,
通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)。
此外,Spring通过Advisor(切面)将Pointcut和Advice组装起来。
有了Advisor的信息,Spring就可以利用JDK或CGLib动态代理技术采用统一的方式为目标
Bean创建织入切面的代理对象了。
动态代理
Spring AOP使用动态代理技术在运行期织入增强的代码。
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。
之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。
- JDK的动态代理:
JDK的动态代理主要涉及java.lang.reflect包中的两个类:Proxy和InvocationHandler。
其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射
机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的
代理对象。
- CGLib动态代理:
使用DK创建代理有一个限制,即它只能为接口创建代理实例,这一点可以从Poxy的接口方法newProxyInstance(ClassLoader loader,Class[]interfaces,.InvocationHandler h)中看得很清楚:
第二个入参interfaces就是需要代理实例实现的接口列表。
CGLb采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
Spring AOP的底层就是通过使用JDK或CGLib动态代理技术为目标Bean织入横切逻辑的。
参考资料:
《精通Spring 4.x 企业应用开发实战》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了