【Spring源码】AOP的开端:核心对象创建的准备工作
AOP的核心成员是如何被被加载的?
本篇我们主要分析使用xml的逻辑,如果使用注解,增加注解处理类即可(ConfigurationClassPostProcessor)
拿之前分析循环的时候举的例子🌰,它的日志切面就是通过xml进行配置的(配置文件📃内容如下,完整测试代码可参考【实践向】当移除了三级缓存…… 中的示例代码)

通过ApplicationContext applicationContext = new ClassPathXmlApplicationContext("circulate.xml");启动容器

进入refresh()方法后,先调用prepareRefresh()方法进行的是容器刷新前的准备工作,这里的准备工作大致包括5个内容:
设置容器的启动时间
设置活跃状态为true
设置关闭状态为false
获取Environment对象,并加载当前系统的属性值到Environment对象中
准备监听器和事件的集合对象(默认为空)
在准备工作完成后,开始了下面的创建容器的流程
创建容器
进入方法obtainFreshBeanFactory(),开始Spring容器的创建

可以看到此时创建的是上下文对象是ClassPathXmlApplicationContext

接着进入方法refreshBeanFactory()中,开始BeanFactory的初始化以及XML配置文件的读取和解析

读取并解析XML配置文件📃

进入loadBeanDefinitions()




终于看到了咱们的doLoadBeanDefinitions() ,正式开始读取+解析标签🏷️的逻辑

registerBeanDefinitions()


进入doRegisterBeanDefinition()(没错、do-开头的方法都是干实事的~)

开始重头戏(。・ω・。)ノ

进入parseBeanDefinitions(),方法中对于 Element 的处理主要分为两种类型:
parseDefaultElement
parseCustomElement
找到对应的handler
<aop:config>就属于自定义元素类型,所以我们进入parseCustomElement()方法中

进入parseCustomElement()

我们可以看到,此时的处理器handler为AopNamespaceHandler

按照案例代码,我们进入了NamespaceHandlerSupport接口中,重写了parse

进入其一个实现类NamepaceHandlerSupport中

由于parser != null为true,再次进入名为parse()的方法中

这里主要进行了两个步骤的处理:
注册自动代理模式创建器
解析<aop:config>下的子节点
POINTCUT
ADVICE
ASPECT
我们依次来看看
第一个步骤:【注册自动代理模式创建器】configureAutoProxyCreator()

进入方法configureAutoProxyCreator()

此时集合containingComponents中只有一个元素:"aop:config"
进入registerAspectJAutoProxyCreatorIfNecessary()

registerOrEscalateApcAsRequired()

进入registerOrEscalateApcAsRequired()

如果已经存在了自动代理创建器,且存在的自动代理创建器与当前不一致,那么需要根据优先级来判断下到底需要使用哪个;如果已经存在了自动代理创建器,且存在的自动代理创建器与当前一致,则无需再次创建,返回null即可

下面的变量表中可以看到,这里注册的Bean名为org.springframework.aop.config.internalAutoProxyCreator的beanDefinition;并且其中的BeanDefinition的具体Class类为AspectJAwareAdvisorAutoProxyCreator
方法registerOrEscalateApcAsRequired()执行到最后的registerBeanDefinition()时,当前的BeanDefinition(也就是AspectJAwareAdvisorAutoProxyCreator的BeanDefinition)就已经注册成功了
由下图的静态代码块中也同样添加了InfrastructureAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator这两个类,因此可以推断出他俩的注册流程与AspectJAwareAdvisorAutoProxyCreator类似

那么问题来了:AspectJAwareAdvisorAutoProxyCreator跟我们熟悉的那些BeanPostProcessor、Aware等等有什么关系么?
(*≧ω≦)我们看下类关系图(下图中右下角的就是AspectJAwareAdvisorAutoProxyCreator)

useClassProxyingIfNecessary()

方法中主要是对proxy-target-class和expose-proxy这两个属性进行处理

如果被代理的目标对象实现了至少一个接口,就会使用JDK动态代理,该目标类型实现的接口都会被代理;如果没有实现任何接口,则创建一个Cglib代理对象
registerComponentIfNecessary()

最后调用方法registerComponentIfNecessary()注册对应的组件

接着回到parse()方法中,可以看到此时<aop:config>还是null

第二个步骤:解析<aop:config>下的子节点
继续解析<aop:config>子节点下的
<aop:pointcut>
<aop:advice>
<aop:aspect>

在这里会根据获取到的localName进入不同的if条件语句,进而执行不同的解析方法
parsePointcut()
parseAdvisor()
parseAspect()
下面我们按照我们的测试代码中的XML配置(如下图)捋一遍流程(。・ω・。)ノ

由于获取到的localName为"aspect",满足ASPECT.equals(localName)为true的条件,所以进入parseAspect()方法中

parseAspect()

先获取<aop:aspect>的两个属性:
id属性
ref属性(代表切面,必须配置)

接着解析<aop:aspect>下的declare-parents节点,通过DeclareParentsAdvisor作为beanClass加载,然后继续解析其他节点

在获取到节点后,遍历循环,解析其下的Advice类型的节点,通过调用isAdviceNode()判断遍历到的当前节点是否为Advice类型的节点
isAdviceNode()
判断是否为Advice节点

如上图中方法上面的注释,Advice类型的节点有5个:
{@code before}
{@code after}
{@code after-returning}
{@code after-throwing}
{@code around}
与上面任何一个类型形同返回值即为true
解析成功则继续向下执行

parseAdvisor()

方法parseAdvisor()先生成了methodDefinition(解析advice中的"method"属性,并包装为MethodLocatingFactoryBean对象 )、aspectFactoryDef(关联aspectName,包装为SimpleBeanFactoryAwareAspectInstanceFactory对象)这两个RootBeanDefinition对象,并依次为他俩赋值

随后解析pointcut属性,将上面👆两个对象传入了方法createAdviceDefinition()中,包装为AspectJMethodBeforeAdvice对象返回并赋值给名为adviceDef的AbstractBeanDefinition对象

最后将这个对象包装为名为advisorDefinition的RootBeanDefinition对象并为相关的属性赋值

最后完成注册,就可以在parseContext对象的readerContext -> reader -> registry -> beanDefinitionMap属性中看到这个AspectJPointcutAdvisor对象叻(AspectJPointcutAdvisor#0)

那么问题来了:这些生成的RootBeanDefinition对象有什么区别吗,他们具体都是哪些类呢?
我们先来详细介绍下createAdviceDefinition()这个方法
createAdviceDefinition()
进入方法createAdviceDefinition()

首先根据adviceElement节点通过调用getAdviceClass()方法分析出是什么类型的Advice,获取到一个adviceDefinition对象
getAdviceClass()
这个方法其实与刚刚介绍过的isAdviceNode()判断的类型是完全一致的

继续给刚刚获取到的adviceDefinition对象设置属性值
设置aspectName属性和declarationOrder属性
设置returning、throwing、arg-name这3个属性(先判断,有则设置)
设置构造函数的入参变量
Method
AspectJExpressionPointcut
AspectInstanceFactory
然后解析<point-cut>节点

parsePointcutProperty()

至此,方法createAdviceDefinition()执行结束,返回一个AbstractBeanDefinition类型的对象adviceDef。我们也来回答下介绍方法前的问题:这些生成的RootBeanDefinition对象具体都是哪些类呢?

methodDefinition:MethodLocatingFactoryBean
aspectFactoryDef:SimpleBeanFactoryAwareAspectInstanceFactory
adviceDef:AspectJMethodBeforeAdvice
adviceDefinition:AspectJPointcutAdvisor
回到方法parseAspect()中,将当前解析过的beanDefinition(advisorDefinition)添加进集合beanDefinitions中

接着进入下一轮循环♻️,继续解析列表中的节点
第二次循环执行完成在parseContext对象的readerContext -> reader -> registry -> beanDefinitionMap属性中又多了一个AspectJPointcutAdvisor对象(AspectJPointcutAdvisor#1),嘿嘿没错每完成一次循环就会多一个~~0、1、2……递增

这些类外部都是AspectJPointcutAdvisor对象,用后缀#1、#2、#3……来区分,但是内部真正的通知类型Advisor是不一样的
循环结束后,返回到parse()方法中,由于我在XML配置文件(如下图)中加了两个aspect标签🏷️,所以childElts里有两个Element,于是会再按上面👆得大致流程再执行一遍

全部遍历完成后一路返回
其实吧,说到底BeanDefinition的解析过程( loadBeanDefinitions()),也就是配置文件的加载过程,毕竟都是Bean嘛^ ^
相关文章:
【Spring源码】AOP的开端:核心对象创建的准备工作
AOP的核心成员是如何被被加载的?本篇我们主要分析使用xml的逻辑,如果使用注解,增加注解处理类即可(ConfigurationClassPostProcessor)拿之前分析循环的时候举的例子🌰,它的日志切面就是通过xml进…...
新号涨粉22w,搞笑博主再次爆火,小红书近期创作趋势是什么?
2月借势元宵、情人节,小红书平台又涌现出哪些黑马博主?品牌在投放种草方面有何亮眼表现?为洞察小红书平台的内容创作趋势及品牌营销策略,新红推出2月月度榜单,从创作者及品牌两方面入手,解析月榜数据&#…...
【C++】30h速成C++从入门到精通(内存管理、函数/类模板)
C内存分布我们先来看一下下面的一段代码相关问题int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] {1, 2, 3, 4};char char2[] "abcd";char* pChar3 "abcd";int* ptr1 (int*)mal…...
自动驾驶决策概况
文章目录1. 第一章行为决策在自动驾驶系统架构中的位置2. 行为决策算法的种类2.1 基于规则的决策算法2.1.1 决策树2.1.2 有限状态机(FSM)2.1.3 基于本体论(Ontologies-based)2.2 基于统计的决策算法2.2.1 贝叶斯网络(B…...
金山轻维表项目进展自动通知
项目经理作为项目全局把控者,经常要和时间“赛跑”。需要实时了解到目前进展如何,跟进人是那些?哪些事项还未完成?项目整体会不会逾期?特别是在一些大型公司中,优秀的项目经理已经学会使用金山轻维表做项目…...
基于上下文分析的 Python 实时 API 推荐
原文来自微信公众号“编程语言Lab”:基于上下文分析的 Python 实时 API 推荐 搜索关注 “编程语言Lab”公众号(HW-PLLab)获取更多技术内容! 欢迎加入 编程语言社区 SIG-程序分析 参与交流讨论(加入方式:添加…...
软件测试-接口测试-代码实现接口测试
文章目录 1.request1.1 request介绍1.2 发送get请求1.3 发送set请求1.4 其他请求方式1.5 传递url参数1.6 响应内容解析1.7 cookie1.8 设置session2.集成UnitTest2.1 接口测试框架开发2.2 案例:使用TPShop项目完成对登录功能的接口测试1.request 1.1 request介绍 概念 基于py…...
中村成洋《垃圾回收的算法与实现》PDF 读书笔记
观前提醒 为了能够锻炼自己,我会查阅大量外文不停的修改内容,少部分会提示成中文。 可能有误,请见谅 提示:若是觉得阅读困难,可以看如下内容 脚本之家可获取,若失效可私信浏览器的沙拉查词扩展…...
docker 网络模式
docker 网络模式主要分为四种,可以通过docker network ls 查看 ~$ docker network ls NETWORK ID NAME DRIVER SCOPE a51d97d72f10 bridge br…...
数据库开发(一文概括mysql基本知识)
Mysql 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 关系型数据库(Relational Database Management System:关系数据库管理系统)应用软件之一。mysql在问开发中,几乎必不可少,因为其他的可能是要收费的&#x…...
【JVM】详解Java内存区域和分配
这里写目录标题一、前言二、运行时数据分区2.1程序计数器(PC)2.2 Java虚拟机栈2.3 本地方法栈2.4 Java堆2.5 方法区2.5.1 运行时常量池2.6 直接内存三、HotSpot虚拟机对象探秘3.1 对象的创建3.2 对象的内存布局3.3 对象的访问定位一、前言 C/C需要自行回收和释放已经没用的对象…...
JAVA开发(史上最完整追本溯源JAVA历史、发展和学习)
(第二次世界大战1931-1945) 世界上最先进的技术往往是由于战争催生,在第二次世界大战中除了飞机,坦克和大炮的武器较量外,在隐秘战线的情报工作其实更为重要,在军队将领来往的电报中,为了防止军事情报的泄漏ÿ…...
Qt 防止程序退出
文章目录摘要QWidgetQML方法 1方法 2关键字: Qt、 eventFilter、 Close、 键盘、 任务管理器摘要 今天要聊得内容还是怎么防止别人关闭我的程序,之前都是在win下面,一般都是用过钩子连捕获键盘事件,完了吧对应的事件忽略&#x…...
【校验码 - 循环冗余校验码CRC】
水善利万物而不争,处众人之所恶,故几于道💦 目录 循环冗余校验码 1.多项式 2.CRC编码的组成 3.校验码的生成 4.例题: 循环冗余校验码 广泛地在网络通信及磁盘存储时采用。 1.多项式 在循环冗余校验(CRC)码中,无一例…...
【Rust】一文讲透Rust中的PartialEq和Eq
前言 本文将围绕对象:PartialEq和Eq,以及PartialOrd和Ord,即四个Rust中重点的Compare Trait进行讨论并解释其中的细节,内容涵盖理论以及代码实现。 在正式介绍PartialEq和Eq、以及PartialOrd和Ord之前,本文会首先介绍…...
Vulnhub靶场----9、DC-9
文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-9下载地址:https://download.vulnhub.com/dc/DC-9.zip kali:192.168.144.148 DC-9:192.168.144.158 二、渗透流程 1、信息收集nmap -T5 -A -p- -sV -sT 192.168.144.158思路&am…...
使用Containerd搭建K8s集群【v1.25】
[toc] 一、安装要求 在开始之前,部署Kubernetes集群机器需要满足以下几个条件: 一台或多台机器,操作系统 CentOS7.x-86_x64硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多集群中所有机器之间网络互通可以访问外网,需要拉取镜像禁止swap分区二、准备环境 角色IP…...
NMT - 构建双语概率词典(Probabilistic dictionaries)
文章目录一、安装依赖包mosesdecoder安装 mgiza二、数据预处理三、训练本文参考:How to train your Bicleaner https://github.com/bitextor/bicleaner/wiki/How-to-train-your-Bicleaner 一、安装依赖包 这个过程主要依赖于 mosesdecodermgiza mosesdecoder git…...
《ChatGPT是怎样炼成的》
ChatGPT 在全世界范围内风靡一时,我现在每天都会使用 ChatGPT 帮我回答几个问题,甚至有的时候在一天内我和它对话的时间比和正常人类对话还要多,因为它确实“法力无边,功能强大”。 ChatGPT 可以帮助我解读程序,做翻译…...
Streaming System是第一章翻译
GIthub链接,欢迎志同道合的小伙伴一起翻译 Chapter 1.Streaming101 如今,流数据处理在大数据中是非常重要的,其主要原因是: 企业渴望对他们的数据有更及时的了解,而转换到流处理是实现更低延迟的一个好方法…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
