Spring AOP 源码剖析

一.AOP基础概念 

  1. 切面(Aspect):切面是跨越多个类的关注点模块化,如事务管理。切面由切点和通知组成。
  2. 连接点(Join Point):在程序执行过程中某个特定的点,如方法调用或异常抛出。在Spring AOP中,连接点通常是方法的执行点。
  3. 切点(Pointcut):用于定义哪些连接点会被增强/通知。切点表达式决定了通知被应用到哪些具体的方法上。
  4. 通知(Advice):在切面的某个特定连接点上执行的动作。通知类型包括前置通知、后置通知、返回通知、异常通知和环绕通知。
  5. 织入(Weaving):将切面应用到目标对象并创建代理对象的过程。Spring AOP是在运行时通过动态代理完成织入的。
  6. 引入(Introduction):允许向现有的类添加新方法或属性。

二.Spring AOP源码实现机制

概念:

Spring AOP主要通过动态代理技术实现,包括JDK动态代理和CGLIB代理。对于实现了接口的类,Spring默认使用JDK动态代理;对于没有实现接口的类,则使用CGLIB生成子类作为代理。

1. 代理对象的生成

代理对象的生成通常发生在Bean的初始化后阶段,由BeanPostProcessor接口的实现类(如AnnotationAwareAspectJAutoProxyCreator)负责。这个类会在Bean初始化后检查是否需要为Bean生成代理对象。如果需要,它会根据配置和Bean的类型选择合适的代理技术(JDK或CGLIB),并创建代理对象。

2. 代理方法的执行

当代理对象的方法被调用时,Spring AOP会根据配置的通知类型和切点表达式,决定在方法执行的前、后或抛出异常时执行哪些通知。这通常通过一个“责任链”模式实现,责任链中的每个节点代表一个通知,按照配置的顺序依次执行。

对于环绕通知,它会包裹目标方法的调用,在调用前后执行自定义的逻辑。环绕通知提供了最大的灵活性,因为它可以控制目标方法的执行流程(例如,可以决定是否继续执行目标方法)。

2.1源码分析

Spring AOP 主要基于两种⽅式实现的: JDK 及 CGLIB 的⽅式
Spring对于AOP的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成
⽣成代理对象的逻辑在⽗类 AbstractAutoProxyCreator
    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);}//创建代理⼯⼚ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);/*** 检查proxyTargetClass属性值,spring默认为false* proxyTargetClass 检查接⼝是否对类代理, ⽽不是对接⼝代理* 如果代理对象为类, 设置为true, 使⽤cglib代理*/if (!proxyFactory.isProxyTargetClass()) {//是否有设置cglib代理if (shouldProxyTargetClass(beanClass, beanName)) {//设置proxyTargetClass为true,使⽤cglib代理proxyFactory.setProxyTargetClass(true);} else {/*** 如果beanClass实现了接⼝,且接⼝⾄少有⼀个⾃定义⽅法,则使⽤JDK代理* 否则CGLIB代理(设置ProxyTargetClass为true )* 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其设为true*/evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}// Use original ClassLoader if bean class not locally loaded in overridingclass loaderClassLoader classLoader = getProxyClassLoader();if (classLoader instanceof SmartClassLoader && classLoader !=beanClass.getClassLoader()) {classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();}//从代理⼯⼚中获取代理}return proxyFactory.getProxy(classLoader);}
代理⼯⼚有⼀个重要的属性: proxyTargetClass, 默认值为false. 也可以通过程序设置
可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true
Spring Boot 2.X开始, 默认使⽤CGLIB代理
可以通过配置项 spring.aop.proxy-target-class=false 来进⾏修改,设置默认为jdk代理
SpringBoot设置 @EnableAspectJAutoProxy ⽆效, 因为Spring Boot 默认使AopAutoConfiguration进⾏装配
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {ApplicationContext context =SpringApplication.run(DemoApplication.class, args);/*** HouseProxy houseProxy = context.getBean(HouseProxy.class);* 设置spring.aop.proxy-target-class=true cglib代理, 运⾏成功* 设置spring.aop.proxy-target-class=false jdk代理, 运⾏失败, 不能代理类* 因为 HouseProxy 是⼀个类, ⽽不是接⼝, 需要修改为* HouseSubject houseProxy = (HouseSubject) context.getBean("realHouseSubject")**/HouseProxy houseProxy = context.getBean(HouseProxy.class);//HouseSubject houseProxy = (HouseSubject) context.getBean("realHouseSubject");//正确运⾏System.out.println(houseProxy.getClass().toString());}
}
使⽤context.getBean() 需要添加注解,使HouseProxy,RealHouseSubject被Spring管理
测试AOP代理, 需要把这些类交给AOP管理(⾃定义注解或使⽤@Aspect)
点进去看代理⼯⼚的代码
public class ProxyFactory extends ProxyCreatorSupport {//...代码省略//获取代理public Object getProxy(@Nullable ClassLoader classLoader) {//分两步 先createAopProxy,后getProxyreturn createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);}//...代码省略
}
createAopProxy的实现在 DefaultAopProxyFactory中
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {//...代码省略@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws
AopConfigException {/*** 根据proxyTargetClass判断* 如果⽬标类是接⼝, 使⽤JDK动态代理* 否则使⽤cglib动态代理*/if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || 
hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine 
target class: " +"Either an interface or a target is required for proxy 
creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || 
ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}//...代码省略
}
接下来就是创建代理了
JDK动态代理
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, 
Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + 
this.advised.getTargetSource());}return Proxy.newProxyInstance(determineClassLoader(classLoader), 
this.proxiedInterfaces, this);}//...代码省略
}
CGLIB动态代理
class CglibAopProxy implements AopProxy, Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {//...代码省略// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}//...代码省略
}

 总结:

Spring AOP通过动态代理技术提供了一种灵活的方式来增强现有方法的功能,而无需修改源代码。其源码实现涉及Bean生命周期管理、动态代理技术、责任链模式等多个方面,深入理解这些机制有助于更好地使用和维护Spring AOP。

请注意,由于Spring框架的不断发展,源码实现细节可能会有所变化。因此,在实际分析源码时,建议参考最新的Spring文档和源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394790.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kafka基础概念二

1.Kafka中主题和分区的概念 1.主题Topic 主题-topic在kafka中是一个逻辑的概念&#xff0c;kafka通过topic将消息进行分类。不同的topic会被订阅该topic的消费者消费 但是有一个问题&#xff0c;如果说这个topic中的消息非常非常多&#xff0c;多到需要几T来存&#xff0c;因…

区块链的搭建和运维4

区块链的搭建和运维4 (1) 搭建基于MySQL分布式存储的区块链 1.构建单群组网络节点 使用开发部署工具构建单群组网络节点&#xff0c;命令如下&#xff1a; bash build_chain.sh -l 127.0.0.1:4 -p 30300,20200,85452. 启动 MySQL 并设置账户密码 输入如下命令&#xff0c;…

关于Git使用不成功的问题解决方案记录

关于Git使用不成功的问题解决方案记录 前言代理连接不成功总结 前言 项目中建立了Git小仓库&#xff0c;但是在使用中出现了无法push新的代码&#xff0c;显示端口出现问题&#xff0c;发现网站和端口都没有问题&#xff0c;可以打开网站。但是还是连接失败&#xff0c;无法下…

MySQL笔记(十):MySQL管理

一、用户管理 #用户管理 -- 原因&#xff1a;当我们做项目开发时&#xff0c;可以根据不同的开发人员&#xff0c;赋给她相应的mysql操作权限。 -- 所以&#xff0c;mysql数据库管理人员&#xff08;root&#xff09;&#xff0c;根据需要创建不同的用户&#xff0c;赋给相应的…

android中打包apk体积优化方案

1.在配置文件AndroidManifest中新增 android:extractNativeLibs"true" 2.在模块build文件下配置支持的cpu,一般配置64的就行了,多配一种so库体积大一倍&#xff0c;择优。 ndk { abiFilters arm64-v8a } 3.在模块builde文件下配置混淆除去无用的资源文件 注:三种…

【Kubernetes】Deployment 的状态

Deployment 的状态 Deployment 控制器在整个生命周期中存在 3 3 3 种状态&#xff1a; 已完成&#xff08;Complete&#xff09;进行中&#xff08;Progressing&#xff09;失败&#xff08;Failed&#xff09; 通过观察 Deployment 的当前特征&#xff0c;可以判断 Deploym…

Win32注册表操作

注册表的概念 注册表是一个存储计算机配置信息的数据库&#xff0c;用于存储计算机上的硬件、安装的软件、系统设置以及用户账户配置等重要信息。对注册表的编辑不当可能会影响计算机的正常运行。应用程序可以调用API函数来对注册表进行增、删等操作。 注册表结构 运行Regedi…

Linux学习笔记:Linux基础知识汇总(个人复习版)

常用命令&#xff1a; 1、ls -a&#xff1a;显示所有文件&#xff08;包括隐藏文件&#xff09;&#xff0c;简洁版 -l&#xff1a;显示所有文件&#xff0c;详细版 -R&#xff1a;显示所有文件以及子目录下文件&#xff0c;简洁版 可以搭配使用。 2、netstat -i&#x…

priority_queue模拟实现【C++】

文章目录 全部的实现代码放在了文章末尾什么是适配器模式&#xff1f;准备工作包含头文件定义命名空间类的成员变量什么是仿函数&#xff1f;比较仿函数在priority_queue中的作用通过传入不同的仿函数可以做到大堆和小堆之间的切换通过传入不同的仿函数可以做到改变priority_qu…

书生.浦江大模型实战训练营——(三)Git基本操作与分支管理

最近在学习书生.浦江大模型实战训练营&#xff0c;所有课程都免费&#xff0c;以关卡的形式学习&#xff0c;也比较有意思&#xff0c;提供免费的算力实战&#xff0c;真的很不错&#xff08;无广&#xff09;&#xff01;欢迎大家一起学习&#xff0c;打开LLM探索大门&#xf…

Java设计模式(命令模式)

定义 将一个请求封装为一个对象&#xff0c;从而让你可以用不同的请求对客户进行参数化&#xff0c;对请求排队或者记录请求日志&#xff0c;以及支持可撤销的操作。 角色 抽象命令类&#xff08;Command&#xff09;&#xff1a;声明用于执行请求的execute方法&#xff0c;通…

LeNet5模型搭建

文章目录 LeNet1 搭建模型2 训练模型3 测试模型3.1 预测一3.2 预测二 LeNet LeNet 诞生于 1994 年&#xff0c;是最早的卷积神经网络之一&#xff0c;并且推动了深度学习领域的发展。自从 1988 年开始&#xff0c;在许多次成功的迭代后&#xff0c;这项由 Yann LeCun 完成的开拓…

【最长递增子序列】python刷题记录

R4-dp 目录 常规方法遇到以下序列时就会变得错误 动态规划的思路 单调栈 ps: class Solution:def lengthOfLIS(self, nums: List[int]) -> int:#最简单的方法nlen(nums)if n<2:return nmx1for i in range(n):max_i1for j in range(i1,n):if nums[i]<nums[j]:nums…

RK3568平台(触摸篇)FT5X06驱动程序分析

一.设备树 &i2c1 {status "okay";myft5x06: my-ft5x0638 {compatible "my-ft5x06";reg <0x38>;reset-gpios <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;interrupt-parent <&gpio3>;interrupts-gpio <&gpio3 RK_PA5 GPI…

大数据-70 Kafka 高级特性 物理存储 日志存储 日志清理: 日志删除与日志压缩

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

K8S资源之NameSpace

作用 隔离资源(默认不隔离网络) 查看所有的NS kubectl get ns创建NS kubectl create ns hello删除NS kubectl delete ns hello

VUE基础快速入门

VUE 和 VUE-Cli VUE 是一种流行的渐进式JavaScript框架&#xff0c;用于构建Web用户界面它具有易学、轻量级、灵活性强、高效率等特点&#xff0c;并且可以与其他库和项目集成是目前最流行的前端框架之一VUE-Cli 称为“VUE脚手架”,它是由VUE官方提供的客户端&#xff0c;专门为…

简单Qt贪吃蛇项目

目录 先看效果 项目介绍 界面一&#xff1a;游戏大厅界面 界面二&#xff1a;关卡选择界面​编辑 界面三&#xff1a;游戏界面 游戏大厅页面 游戏关卡选择页面 游戏房间页面 封装贪吃蛇数据结构 初始化游戏房间界面 设置窗口大小、标题、图标等 蛇的移动 初始化贪…

RocketMQ Dashboard安装

RocketMQ Dashboard 是一个基于 Web 的管理工具&#xff0c;用于监控和管理 RocketMQ 集群。它提供了一个用户友好的界面&#xff0c;使管理员能够轻松地查看和操作 RocketMQ 系统中的各种组件和状态。 主要功能包括&#xff1a; 集群管理: 监控和管理 NameServer 和 Broker …

大数据-65 Kafka 高级特性 分区 Broker自动再平衡 ISR 副本 宕机恢复再重平衡 实测

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…