当前位置: 首页 > news >正文

Spring-Aop源码解析(二)

书接上文,上文说到,specificInterceptors 不为空则执行createProxy方法创建代理对象,即下图的createProxy方法开始执行,生成代理对象,生成代理对象有两种方式,JDK和CGLIB。
在这里插入图片描述
在这里插入图片描述
createAopProxy就是决定使用哪种方式生成动态代理对象,方法执行流程和代码如下:
在这里插入图片描述

/*** 真正的创建代理,判断一些列条件,有自定义的接口的就会创建jdk代理,否则就是cglib* @param config the AOP configuration in the form of an* AdvisedSupport object* @return* @throws AopConfigException*/@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 这段代码用来判断选择哪种创建代理对象的方式// config.isOptimize()   是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false// config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false// hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是SpringProxy类型if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {// 上面的三个方法有一个为true的话,则进入到这里// 从AdvisedSupport中获取目标类 类对象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.");}// 判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象// 如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 配置了使用Cglib进行动态代理或者目标类没有接口,那么使用Cglib的方式创建代理对象return new ObjenesisCglibAopProxy(config);}else {// 使用JDK的提供的代理方式生成代理对象return new JdkDynamicAopProxy(config);}}

此处使用ObjenesisCglibAopProxy方式

getProxy:168, CglibAopProxy (org.springframework.aop.framework)

创建代理对象方法及注释如下:

/*** 获取cglib的代理对象* @param classLoader the class loader to create the proxy with* (or {@code null} for the low-level proxy facility's default)* @return*/@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {// 从advised中获取ioc容器中配置的target对象Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;//如果目标对象已经是CGLIB 生成代理对象(就是比较类名称中有 $$ 字符串),那么就取目标对象的父类作为目标对象的类if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();// 获取原始父类的接口Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.// 打印出不能代理的方法名,CGLIB 是使用继承实现的,所以final , static 的方法不能被增强validateClassIfNecessary(proxySuperClass, classLoader);// Configure CGLIB Enhancer...// 创建及配置EnhancerEnhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}// 配置超类,代理类实现的接口,回调方法等enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 获取callbacksCallback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.// 通过 Enhancer 生成代理对象,并设置回调return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}}

rootClass是需要被代理的对象,Enhancer该类用于生成代理对象,代理如何生成:CGLIB和JDK两种生成方式。

getProxy:168, CglibAopProxy (org.springframework.aop.framework)
getProxy:116, ProxyFactory (org.springframework.aop.framework)
createProxy:519, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
wrapIfNecessary:383, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
postProcessAfterInitialization:319, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy)
applyBeanPostProcessorsAfterInitialization:529, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:2273, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:736, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:630, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:417, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 71399214 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$12)
getSingleton:370, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:414, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:260, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:993, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:1024, AbstractApplicationContext (org.springframework.context.support)
refresh:614, AbstractApplicationContext (org.springframework.context.support)
<init>:150, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:87, ClassPathXmlApplicationContext (org.springframework.context.support)
main:15, TestAop (com.mashibing.aop.xml)

一级缓存里是MyCalculator的代理对象 后续执行就是这个代理对象的方法,然后跳转到

intercept:709, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)

这个方法

在这里插入图片描述

@Override@Nullablepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// 从advised中获取配置好的AOP通知List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.// 如果没有aop通知配置,那么直接调用target对象的调用方法if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {// We can skip creating a MethodInvocation: just invoke the target directly.// Note that the final invoker must be an InvokerInterceptor, so we know// it does nothing but a reflective operation on the target, and no hot// swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);// 如果拦截器链为空则直接激活原方法retVal = methodProxy.invoke(target, argsToUse);}else {// We need to create a method invocation...// 通过cglibMethodInvocation来启动advice通知retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}@Overridepublic boolean equals(@Nullable Object other) {return (this == other ||(other instanceof DynamicAdvisedInterceptor &&this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));}

chain = {ArrayList@2276} size = 6
0 = {ExposeInvocationInterceptor@2242}
1 = {AspectJAfterThrowingAdvice@2282} “org.springframework.aop.aspectj.AspectJAfterThrowingAdvice: advice method [public static void com.mashibing.aop.xml.util.LogUtil.logException(org.aspectj.lang.JoinPoint,java.lang.Exception)]; aspect name ‘logUtil’”
2 = {AfterReturningAdviceInterceptor@2283}
3 = {AspectJAfterAdvice@2284} “org.springframework.aop.aspectj.AspectJAfterAdvice: advice method [public static void com.mashibing.aop.xml.util.LogUtil.logFinally(org.aspectj.lang.JoinPoint)]; aspect name ‘logUtil’”
4 = {AspectJAroundAdvice@2285} “org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object com.mashibing.aop.xml.util.LogUtil.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ‘logUtil’”
5 = {MethodBeforeAdviceInterceptor@2286}

执行的就是上面6个的通知,通过索引下标挨个执行

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

执行顺序如下:
在这里插入图片描述
本文主要讲解springAop如何创建动态代理对象以及使用哪种方式创建的依据;在执行方法跳转到生成的代理对象中,然后生成拦截器链去执行

相关文章:

Spring-Aop源码解析(二)

书接上文&#xff0c;上文说到&#xff0c;specificInterceptors 不为空则执行createProxy方法创建代理对象&#xff0c;即下图的createProxy方法开始执行&#xff0c;生成代理对象&#xff0c;生成代理对象有两种方式&#xff0c;JDK和CGLIB。 createAopProxy就是决定使用哪…...

antdesgin table 组件下载成excel

文章目录 发现宝藏一、需求二、报错 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 一、需求 原组件如下&#xff0c;需要添加下载功能 import React, { useState } from rea…...

MongoDB - 聚合阶段 $group 的使用

文章目录 1. 构造测试数据1. 示例 12. 示例23. 示例34. 示例45. 示例5 2. 构造测试数据1. 示例12. 示例23. 示例3 在 MongoDB 中&#xff0c;$group 是聚合管道中的一个阶段&#xff0c;用于根据指定的条件对文档进行分组。 {$group: {_id: <expression>, // 分组的依据…...

Flutter 插件之 easy_refresh(下拉刷新、上拉加载)

今天给大家较少一下日常开发中最常见的一个功能,就是 下拉刷新、上拉加载,这个在我们使用分页功能是最常见的。 此前我我也写了一篇关于 下拉刷新、上拉加载。 Flutter 下拉刷新、上拉加载flutter_easyrefresh的使用https://blog.csdn.net/WangQingLei0307/article/details/…...

DVWA的安装和使用

背景介绍 DVWA是Damn Vulnerable Web Application的缩写&#xff0c;是一个用于安全脆弱性检测的开源Web应用。它旨在为安全专业人员提供一个合法的测试环境&#xff0c;帮助他们测试自己的专业技能和工具&#xff0c;同时也帮助web开发者更好地理解web应用安全防范的过程。DV…...

CSS相关记录

文章目录 backgroundposition文字displayflexjustify-contentalign-itemsflex-directionflex-wrap gridimportant transformtranslate&#xff08;位移&#xff09;scale&#xff08;缩放&#xff09;rotate&#xff08;旋转&#xff09;origin (旋转中心点)skew (倾斜 ) borde…...

Fedora40安装telnet-server启用telnet服务

Fedora40安装telnet-server启用telnet服务 安装 telnet-server sudo yum install telnet-server或 sudo dnf install telnet-server启用服务 fedora40 或 CentosStream9 不能用 yum或dnf安装xinetd, telnet-server 的服务名为: telnet.socket 启用 telnet.socket.service …...

Unity3D结合AI教育大模型 开发AI教师 AI外教 AI英语教师案例

自2022年底ChatGPT引爆全球之后&#xff0c;大模型技术便迎来了一段崭新的快速发展期&#xff0c;由其在GPT4.0发布后&#xff0c;AI与教育领域结合产品研发、已成为教育AI科技竞争的新高地、未来产业的新赛道、经济发展的新引擎和新产品的诞生地。 据不完全统计&#xff0c;目…...

lua 游戏架构 之 SceneLoad场景加载(一)

设计一个为BaseSceneLoad class&#xff0c;用于处理场景加载的相关操作 &#xff0c;主要作用是提供了一个通用的场景加载框架&#xff0c;使得子类可以按照统一的接口进行场景加载操作。子类需要实现这些方法&#xff0c;以便在加载场景时能够正确地处理场景加载的各个阶段。…...

【linux深入剖析】命名管道 | 匿名管道与命名管道的区别 | system V共享内存

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 命名管道2. 创建命名管…...

Vite 常用插件配置:自动导入+自动注册组件+动态创建图标+设置组件名

创建 Vue3Vite 项目 创建 Vue3 项目 $ pnpm create vuelatest通过脚手架选择开启以下功能 ✔ Project name: … <your-project-name> ✔ Add TypeScript? … No / Yes ✔ Add JSX Support? … No / Yes ✔ Add Vue Router for Single Page Application development?…...

(leetcode学习)236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…...

Zabbix监控系统:zabbix服务部署+基于Proxy分布式部署+zabbix主动与被动监控模式

一、Zabbix概述 1.1 简介 zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff0c;提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbix…...

【Vue实战教程】之 Vue Router 路由详解

Vue Router路由 1 路由基础 1.1 什么是路由 用Vue.js创建的项目是单页面应用&#xff0c;如果想要在项目中模拟出来类似于页面跳转的效果&#xff0c;就要使用路由。其实&#xff0c;我们不能只从字面的意思来理解路由&#xff0c;从字面上来看&#xff0c;很容易把路由联想…...

测试用例接口开发实战

测试用例接口开发实战 前言 在上一集&#xff0c;我们也大概完成了对Jmeter的二次开发的Demo版本的了解&#xff0c;我们接下来就要基于这个Demo来将Jmeter压测进行平台化。 那么这一集&#xff0c;我们讲一讲测试用例接口开发实战。 StressCaseController 我们的Controll…...

C#中压缩文件夹,及其内容

压缩包格式&#xff0c;本文主要用于说明如何使用代码 文件或文件夹压缩为 zip压缩包及其解压操作&#xff0c; 下面分两个版本进行实现 1.简单版本 bool DoCompressDirectoryInfo(string folderPath){try{var zipFilePath $"{folderPath}.zip";var directoryInfo …...

机器学习 | 回归算法原理——多项式回归

Hi&#xff0c;大家好&#xff0c;我是半亩花海。接着上次的最速下降法&#xff08;梯度下降法&#xff09;继续更新《白话机器学习的数学》这本书的学习笔记&#xff0c;在此分享多项式回归这一回归算法原理。本章的回归算法原理基于《基于广告费预测点击量》项目&#xff0c;…...

力扣224【基本计算器】

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 注意:不允许使用任何将字符串作为数学表达式计算的内置函数&#xff0c;比如 eval() 。 1 < s.length < 3 * 105 s 由数字、‘’、‘-’、‘(’、‘)’、和 ’ ’ 组成 s 表示一个有效的…...

【Linux】HTTP 协议

目录 1. URL2. HTTP 协议2.1. HTTP 请求2.2. HTTP 响应 1. URL URL 表示着是统一资源定位符(Uniform Resource Locator), 就是 web 地址&#xff0c;俗称“网址”; 每个有效的 URL 可以通过互联网访问唯一的资源, 是互联网上标准资源的地址; URL 的主要由四个部分组成: sche…...

@Builder注释导致@RequestBody的前端json反序列化失败,HTTP400

项目里发生了一个bug&#xff0c;就是前端请求一个接口时候&#xff0c;报了HTTP 400 Bad Request 通常来说这个问题是前后端的参数没对齐&#xff0c;比如前端传了个String&#xff0c;但后端对应的是Integer。 所以我就排查了半天&#xff0c;结果没发现啥错误&#xff0c;…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...