国庆假期,整整七天,我用76张图把Spring AOP给画明白了!

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:楼仔

98335e7c735a2718a85053e00d20419b.jpeg


本文我会简单介绍一下 AOP 的基础知识,以及使用方法,然后直接对源码进行拆解。

不 BB,上文章目录。

11836714c22db9a1be14ababd258c959.png

1. 基础知识

1.1 什么是 AOP ?

AOP 的全称是 “Aspect Oriented Programming”,即面向切面编程 。

在 AOP 的思想里面,周边功能(比如性能统计,日志,事务管理等)被定义为切面 ,核心功能和切面功能分别独立进行开发,然后把核心功能和切面功能“编织”在一起,这就叫 AOP。

AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

1.2 AOP 基础概念

  • 连接点(Join point) :能够被拦截的地方,Spring AOP 是基于动态代理的,所以是方法拦截的,每个成员方法都可以称之为连接点;

  • 切点(Poincut) :每个方法都可以称之为连接点,我们具体定位到某一个方法就成为切点;

  • 增强/通知(Advice) :表示添加到切点的一段逻辑代码,并定位连接点的方位信息,简单来说就定义了是干什么的,具体是在哪干;

  • 织入(Weaving) :将增强/通知添加到目标类的具体连接点上的过程;

  • 引入/引介(Introduction) :允许我们向现有的类添加新方法或属性,是一种特殊的增强;

  • 切面(Aspect) :切面由切点和增强/通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。

上面的解释偏官方,下面用“方言”再给大家解释一遍。

  • 切入点(Pointcut):在哪些类,哪些方法上切入(where );

  • 通知(Advice):在方法执行的什么时机(when :方法前/方法后/方法前后)做什么(what :增强的功能);

  • 切面(Aspect):切面 = 切入点 + 通知,通俗点就是在什么时机,什么地方,做什么增强;

  • 织入(Weaving):把切面加入到对象,并创建出代理对象的过程,这个由 Spring 来完成。

5 种通知的分类:

  • 前置通知(Before Advice) :在目标方法被调用前调用通知功能;

  • 后置通知(After Advice) :在目标方法被调用之后调用通知功能;

  • 返回通知(After-returning) :在目标方法成功执行之后调用通知功能;

  • 异常通知(After-throwing) :在目标方法抛出异常之后调用通知功能;

  • 环绕通知(Around) :把整个目标方法包裹起来,在被调用前和调用之后分别调用通知功能。

1.3 AOP 简单示例

新建 Louzai 类:

@Data
@Service
public class Louzai {

    public void everyDay() {
        System.out.println("睡觉");
    }
}

添加 LouzaiAspect 切面:

@Aspect
@Component
public class LouzaiAspect {
    
    @Pointcut("execution(* com.java.Louzai.everyDay())")
    private void myPointCut() {
    }

    // 前置通知
    @Before("myPointCut()")
    public void myBefore() {
        System.out.println("吃饭");
    }

    // 后置通知
    @AfterReturning(value = "myPointCut()")
    public void myAfterReturning() {
        System.out.println("打豆豆。。。");
    }
}

applicationContext.xml 添加:

<!--启用@Autowired等注解-->
<context:annotation-config/>
<context:component-scan base-package="com" />
<aop:aspectj-autoproxy proxy-target-class="true"/>

程序入口:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Louzai louzai = (Louzai) context.getBean("louzai");
        louzai.everyDay();
    }
}

输出:

吃饭
睡觉
打豆豆。。。

这个示例非常简单,“睡觉” 加了前置和后置通知,但是 Spring 在内部是如何工作的呢?

1.4 Spring AOP 工作流程

为了方便大家能更好看懂后面的源码,我先整体介绍一下源码的执行流程,让大家有一个整体的认识,否则容易被绕进去。

整个 Spring AOP 源码,其实分为 3 块,我们会结合上面的示例,给大家进行讲解。

b769d3992e6c148b2956ce25833c784b.png

第一块就是前置处理 ,我们在创建 Louzai Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中 ,比如示例中 LouzaiAspect 的所有切面信息。

第二块就是后置处理 ,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:

  • 获取 Louzai 的切面方法 :首先会从缓存中拿到所有的切面信息,和 Louzai 的所有方法进行匹配,然后找到 Louzai 所有需要进行 AOP 的方法。

  • 创建 AOP 代理对象 :结合 Louzai 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。

cd74e3c5470ced5c3f8258db629c292f.png

第三块就是执行切面 ,通过“责任链 + 递归”,去执行切面。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

2. 源码解读

注意:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!

除了原理部分,上面的知识都不难,下面才是我们的重头戏,让你跟着楼仔,走一遍代码流程。

2.1 代码入口

c0c0e6f92d14ba05654b0a72a8b6bde0.png 829c32b9586cff53a03169059f49f746.png

这里需要多跑几次,把前面的 beanName 跳过去,只看 louzai。

c9ef5280d0cd0801c4cc3d34b8e58eeb.png 4a665666b0424d99857ed9b67234449b.png

进入 doGetBean(),进入创建 Bean 的逻辑。

3cc2c9832b4da117e3cc667b7c90dbb0.png

2.2 前置处理

9b642db63819a8b24782fbcef0266410.png

主要就是遍历切面,放入缓存。

b180f0dfbe7f7e92ef2e7a768e9d9f03.png 79ed0615684c154488acd35b568bff73.png 33cf171fa81cb5099b086c2efc966b42.png af4cc7012f233f3a191e1e229f08c144.png 4f9e5456bc3e0eb3b9063d954f765330.png c9e199aec19e41e7f04e2593a6e128cb.png

这里是重点!敲黑板!!!

  1. 我们会先遍历所有的类;

  2. 判断是否切面,只有切面才会进入后面逻辑;

  3. 获取每个 Aspect 的切面列表;

  4. 保存 Aspect 的切面列表到缓存 advisorsCache 中。

c2b256f51df1c1bbf4ad74caa43249b4.png

到这里,获取切面信息的流程就结束了,因为后续对切面数据的获取,都是从缓存 advisorsCache 中拿到。

下面就对上面的流程,再深入解读一下。

2.2.1 判断是否是切面

上图的第 2 步,逻辑如下:

d6e5c87aa92181e79c3be1ab46e8f6a4.png

2.2.2 获取切面列表

78d78040e59cd66fa03a17385652fdd9.png f9c60a0d969c1d5b2ab69f7fff46dda5.png 5bf63c26aba4e532b9c95a85f532b66e.png a57e48b26c5660f7b5012c21df467edf.png

进入到 getAdvice(),生成切面信息。

c0af8cbd6acda6484efefc1c9ac3b523.png

2.3 后置处理

50ea889fc9a38732bd9fc43461454b81.png

主要就是从缓存拿切面,和 louzai 的方法匹配,并创建 AOP 代理对象。

7d5a1a0cdf1e5f3be02506d58275a9f9.png

进入 doCreateBean(),走下面逻辑。

fa0375a2c09376eed354c980a2f53c2b.png 50b15959879f62f0d0465af96eb9c88b.png 5386f42c5d26b0631e1ea6af36ae1285.png 1f3d56235409218b4c3d76273605480d.png

这里是重点!敲黑板!!!

  1. 先获取 louzai 类的所有切面列表;

  2. 创建一个 AOP 的代理对象。

5642543ad80d6f1a5b0879458283ea86.png
2.3.1 获取切面

我们先进入第一步,看是如何获取 louzai 的切面列表。

dba541867077e2d4a57cd1b8cd81866f.png ee74086e9cc7754198665cdaee96c250.png aba32afcfe397c09a0de24e37fa94ddf.png

进入 buildAspectJAdvisors(),这个方法应该有印象,就是前面将切面信息放入缓存 advisorsCache 中,现在这里就是要获取缓存。

69b3a410dbf85759b35944fc55afe81a.png da1e1ea2961fd804fb06d34fd810b39e.png

再回到 findEligibleAdvisors(),从缓存拿到所有的切面信息后,继续往后执行。

74da906d78d33d1542017172804ef627.png ddfc531fbf342eeac7bc0006dff0fc0f.png 4096fb32bef6245763fd71527383238a.png 1b500c51cb9c208d283577ca3dc8ac1e.png 1f2283a7e178777aca6f90128e89eeeb.png
2.3.2 创建代理对象

有了 louzai 的切面列表,后面就可以开始去创建 AOP 代理对象。

a495db4c003ce28ab7907c708d17bbf5.png 90fb774eb6830670512b1c8565ae1bae.png cb23562a6b8fe88ba9e1563abcb2cd91.png

这里是重点!敲黑板!!!

这里有 2 种创建 AOP 代理对象的方式,我们是选用 Cglib 来创建。

932bd13f5aa3cd0bc3a2f9801573a1e0.png 34b1cca367cf621ee0ba4cc6e47fc3ad.png e19850601e5eda173b8ddb34a727f935.png

我们再回到创建代理对象的入口,看看创建的代理对象。

00cbf0062c82843eb79431f167edc7de.png

2.4 切面执行

3c269676b76abb07b3b0ef9ba1cb15c8.png

通过 “责任链 + 递归”,执行切面和方法。

e70eb45b7193119cb36b6264f6542db1.png ec4f2b1afd979d08820be062ac28b86c.png

前方高能!这块逻辑非常复杂!!!

d5dd2ea29f8856840a65e5ec4589ab40.png

下面就是“执行切面”最核心的逻辑,简单说一下设计思路:

  1. 设计思路 :采用递归 + 责任链的模式;

  2. 递归 :反复执行 CglibMethodInvocation 的 proceed();

  3. 退出递归条件 :interceptorsAndDynamicMethodMatchers 数组中的对象,全部执行完毕;

  4. 责任链 :示例中的责任链,是个长度为 3 的数组,每次取其中一个数组对象,然后去执行对象的 invoke()。

2d0ad18a1f36c19e2bb08f5a843edf7f.png

因为我们数组里面只有 3 个对象,所以只会递归 3 次,下面就看这 3 次是如何递归,责任链是如何执行的,设计得很巧妙!

2.4.1 第一次递归

数组的第一个对象是 ExposeInvocationInterceptor,执行 invoke(),注意入参是 CglibMethodInvocation。

db9eb168ad0131d80b6fc73bb8cc8b05.png

里面啥都没干,继续执行 CglibMethodInvocation 的 process()。

39699ca0e33b562c5fca99be52b6221d.png 8fa8a59bee592bd6c98a1e4efcde11eb.png
2.4.2 第二次递归

数组的第二个对象是 MethodBeforeAdviceInterceptor,执行 invoke()。

acffaa540a10a16a7627eb8918a72c37.png 42ae206f3e341cffcb3d8d7d10359368.png 0a2d1691da1f26bbe3e4280489d79bc0.png a921f1f71e5a33574a4d45cf733f18d3.png 2739e6d02cde33079677d9f78ddd206a.png ea32f4b077bb101c99ec304e82b8cbc0.png 0351623f8332721e5cd857837ee0c512.png aa114a99b21d93ff7c8ef24da6ae47f9.png 7c25a1bf41b300630f705a0c32986e6d.png
2.4.3 第三次递归

数组的第二个对象是 AfterReturningAdviceInterceptor,执行 invoke()。

4976b57867ce5cc21c4af20b1915d421.png c5a61f9b7cc1360f0b1f92df2a2d841a.png 03e166d7edfce7d5532d36d63cd0813d.png

执行完上面逻辑,就会退出递归,我们看看 invokeJoinpoint() 的执行逻辑,其实就是执行主方法。

041cf02e84815e1d34d111521e0c265c.png 18f3dead9c167c0637fe58fc36ecba8b.png b858b17da9ae7b67f49804caa6a98536.png

再回到第三次递归的入口,继续执行后面的切面。

fcb6d23f63272428e545d62ad359d920.png

切面执行逻辑,前面已经演示过,直接看执行方法。

0016644e2d3d53f69bec00a2671831b6.png

后面就依次退出递归,整个流程结束。

2.4.4 设计思路

这块代码,我研究了大半天,因为这个不是纯粹的责任链模式。

纯粹的责任链模式,对象内部有一个自身的 next 对象,执行完当前对象的方法末尾,就会启动 next 对象的执行,直到最后一个 next 对象执行完毕,或者中途因为某些条件中断执行,责任链才会退出。

这里 CglibMethodInvocation 对象内部没有 next 对象,全程是通过 interceptorsAndDynamicMethodMatchers 长度为 3 的数组控制,依次去执行数组中的对象,直到最后一个对象执行完毕,责任链才会退出。

这个也属于责任链,只是实现方式不一样,后面会详细剖析 ,下面再讨论一下,这些类之间的关系。

我们的主对象是 CglibMethodInvocation,继承于 ReflectiveMethodInvocation,然后 process() 的核心逻辑,其实都在 ReflectiveMethodInvocation 中。

ReflectiveMethodInvocation 中的 process() 控制整个责任链的执行。

ReflectiveMethodInvocation 中的 process() 方法,里面有个长度为 3 的数组 interceptorsAndDynamicMethodMatchers,里面存储了 3 个对象,分别为 ExposeInvocationInterceptor、MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor。

注意!!!这 3 个对象,都是继承 MethodInterceptor 接口。

1ce38e8aa155e2faa3a58027c7bb1068.png

然后每次执行 invoke() 时,里面都会去执行 CglibMethodInvocation 的 process()。

是不是听得有些蒙圈?甭着急,我重新再帮你梳理一下。

对象和方法的关系:

  • 接口继承 :数组中的 3 个对象,都是继承 MethodInterceptor 接口,实现里面的 invoke() 方法;

  • 类继承 :我们的主对象 CglibMethodInvocation,继承于 ReflectiveMethodInvocation,复用它的 process() 方法;

  • 两者结合(策略模式) :invoke() 的入参,就是 CglibMethodInvocation,执行 invoke() 时,内部会执行 CglibMethodInvocation.process(),这个其实就是个策略模式。

可能有同学会说,invoke() 的入参是 MethodInvocation,没错!但是 CglibMethodInvocation 也继承了 MethodInvocation,不信自己可以去看。

执行逻辑:

  • 程序入口 :是 CglibMethodInvocation 的 process() 方法;

  • 链式执行(衍生的责任链模式) :process() 中有个包含 3 个对象的数组,依次去执行每个对象的 invoke() 方法。

  • 递归(逻辑回退) :invoke() 方法会执行切面逻辑,同时也会执行 CglibMethodInvocation 的 process() 方法,让逻辑再一次进入 process()。

  • 递归退出 :当数字中的 3 个对象全部执行完毕,流程结束。

所以这里设计巧妙的地方,是因为纯粹责任链模式,里面的 next 对象,需要保证里面的对象类型完全相同。

但是数组里面的 3 个对象,里面没有 next 成员对象,所以不能直接用责任链模式,那怎么办呢?就单独搞了一个 CglibMethodInvocation.process(),通过去无限递归 process(),来实现这个责任链的逻辑。

这就是我们为什么要看源码,学习里面优秀的设计思路!

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

3. 总结

我们再小节一下,文章先介绍了什么是 AOP,以及 AOP 的原理和示例。

之后再剖析了 AOP 的源码,分为 3 块:

  • 将所有的切面都保存在缓存中;

  • 取出缓存中的切面列表,和 louzai 对象的所有方法匹配,拿到属于 louzai 的切面列表;

  • 创建 AOP 代理对象;

  • 通过“责任链 + 递归”,去执行切面逻辑。

这篇文章,是 Spring 源码解析的第 3 篇,也是感觉最难的一篇 ,光图解代码就扣了 6 个小时 ,整个人都被扣麻了。

最难的地方还不是抠图,而是 “切面执行”的设计思路 ,虽然流程能走通,但是把整个设计思想能总结出来,并讲得能让大家明白,还是非常不容易的。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

29e32485d35b1a27b840eda341dfa93d.png

已在知识星球更新源码解析如下:

25b4191ae46a0167e3e26d0bc83678e5.jpeg

7d80f02afecf233aff218840931fe3cb.jpeg

ae54da505ea5839a1321d8d404dca0b7.jpeg

cd88388118b7d2d6016b2c81c0e6dcd1.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
全国职业院校技能大赛-大数据应用赛项-数据挖掘-01 -----------------------推荐Top5结果如下--------------------------------------------第一条数据前10列结果展示为:----------------------------------------相同种类前10的id结果展示为:--------------------相似度top5(商品id:14,平均相似度:0.522356)相似度top1(商品id:1,平均相似度:0.983456)若属于该spu_id,则内容为1否则为0。 阅读详情

相关推荐

educoder数字逻辑实训:寄存器设计与应用(Logisim)

第1关:基本寄存器的设计 任务描述 本关任务:在Logisim中,构建由4个D触发器构成的4位基本寄存器,要求上升沿触发,具有异步复位功能。 第2关:移位寄存器的设计 任务描述 本关任务:在Logisim中,构建由4个D触发器构成的4位右移移位寄存器,要求上升沿触发,具有异步复位功能。 第3关:循环移位寄存器的设计 任务描述 本关任务:在Logisim中,构建由4个D触发器构成的4位循环右移移位寄存器,要求上升沿触发,具有异步复位功能以及并行数据同步加载功能。 第4关:并行..

qq_52209002的博客 1万+

Java JDK 动态代理(AOP)使用及实现原理分析

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息 ​ 一、什么是代理? 二、Java 动态代理类 三、JDK的动态代理怎么使用? 四、动态代理怎么实现的? 五、结论 一、什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 代理模式UML图: 简单结构示意图: 为了保持行为的...

勿忘初心 16万+

维纳滤波和卡尔曼滤波

本文的主要内容是维纳滤波和卡尔曼滤波的介绍,包含匹配滤波器、维纳滤波器、维纳预测、卡尔曼滤波等内容。

西岸贤 9520

【JavaEE进阶】——第七节.Spring AOP统一功能处理(切面、切点、连接点、通知)

今天我们将介绍有关Spring AOP统一功能处理的内容

未央的博客哟 2329

[金仓数据库V8R6]更换授权

上传授权,使用mv license_xxx_0.dat /home/kingbase/KingbaseES/license.dat替换每个授权,并使用ls -l license.dat确保替换前后属主和权限不变。根据步骤1的数据目录位置,执行/home/kingbase/KingbaseES/Server/bin/sys_ctl -D /home/kingbase/KingbaseES/data reload使新的授权文件生效。该参数意义为授权剩余有效天数,查询结果为新授权中有效期间的天数即为替换成功。

l04050924的博客 3712

【逆向实战】Unity游戏资源提取与解密全攻略:从AssetBundle到libil2cpp.so的破解技巧

本文详细介绍了Unity游戏资源提取与解密的完整流程,涵盖从AssetBundle到libil2cpp.so的破解技巧。通过AssetStudio和Il2CppDumper等工具,读者可以学习如何提取未加密资源、处理加密文件,并掌握动态dump技术。文章还提供了自动化脚本编写和常见问题解决方案,帮助逆向工程爱好者高效完成资源提取任务。

weixin_29012765的博客 1050

19 张图秒懂 Spring Cloud 全家桶

1、什么是微服务 1.1、架构演进 架构的发展历程是从单体式架构,到分布式架构,到SOA架构,再到微服务架构。 图1:架构演进 单体架构:未做任何拆分的Java Web程序 图2:单体架构示意图 分布式架构:按照业务垂直划分,每个业务都是单体架构,通过API互相调用。 图3:分布式架构示意图 SOA架构:SOA是一种面向服务的架构。其应用程序的不同组件通过网络上的通信协议向其它组件提供服务或消费服务,所以也是分布式架构的一种。 图4:SOA架构示意图 1.2、微服务架构 微服务架构在某种程度上是SO

qq_40603010的博客 346

数据增强综述及albumentations代码使用

数据增强综述及albumentations使用基于基本图形处理的数据增强 本文涉及到的资料: 1.A survey on Image Data Augmentation for Deep Learning 2.Albumentations: Fast and Flexible Image Augmentations 3.Code 基于基本图形处理的数据增强 1.翻转;2.旋转;3.仿射变换;4.色彩空间(RGB、YUV、CMY、HSV,在图像情感分析中会影响标签。最简单的方法就是只改变一个通道。对比度、直方

qq_41917697的博客 6681

SpringCloudAlibaba架构图

SpringCloudAlibaba架构图

辣椒炒肉~的博客 1629

MTGS与3D高斯泼溅:如何用多轨迹数据重建自动驾驶场景(附代码解析)

本文深入解析了MTGS(多轨迹高斯泼溅)技术在自动驾驶场景重建中的工程实践。该方法通过分层场景表示,巧妙融合多轨迹数据,利用3D高斯泼溅技术统一处理静态几何与动态物体,有效解决了复杂环境下的重建难题。文章结合代码,详细阐述了其实现框架、损失函数设计及优化策略,为构建高保真数字孪生场景提供了实用指南。

v5w6x的博客 447

数据分析项目:直播电商用户流失分析

其中,'Tenure'(在网时长)可能是影响用户流失的关键因素,意味着用户在平台上的停留时间越长,流失的可能性可能越低,这提示平台应注重提高用户粘性,增加用户在平台上的活跃度和停留时间。)的分析,挖掘用户流失的原因和模式,分析发现用户投诉处理效率(Complain)、距上次消费时长(DaySinceLastOrder)、会员生命周期(Tenure)等8项核心指标对流失预测贡献度达87%,模型准确率94.58%,并提出相应的建议。重视用户的满意度,及时处理用户的投诉和反馈,不断改进平台的服务和功能。

Nervousr的博客 2459

国庆7天没休息,我用17张图画明白了SpringCloudAlibaba

今天给大家介绍一个非常热门的技术,同时也是面试的时候面试官特别喜欢问的一个话题,那就是 SpringCloudAlibaba 的底层原理。

石杉的架构笔记 1546

如何使用Python爬取并保存网页上的所有附件

爬虫(Crawler)是一种自动化程序,它通过模拟人工访问网页并下载网页上的数据或文件,常用于数据采集和分析。爬虫通常以“爬取”的形式自动浏览和提取网页中的各种信息。本文详细介绍了如何使用Python爬取并保存网页中的附件文件,涵盖了网页解析、文件下载、多线程与异步优化等内容。希望通过本文的讲解,能够帮助你理解并掌握附件爬取的技巧。在实际应用中,爬虫开发不仅需要解决技术问题,还要遵守网络道德和法律法规,避免不当使用爬虫造成负面影响。

2201_76125261的博客 968

9,000+ 字,彻底征服 Spring AOP!

点击上方“芋道源码”,选择“设为星标”管她前浪,还是后浪?能浪的浪,才是好浪!每天 8:55 更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2020 超神之路,很肝~...

芋艿V 175

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

实现一个小轮子,用AOP实现异步上传

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析...

weixin_44421461的博客 228

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 302

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 526

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 355

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 331

【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端

上一篇讲了 MCP 的概念:标准协议、三大角色、M+N 替代 M×N。

nice_lcj520的博客 561

Java 集合容器高频面试题全解:HashMap 树化阈值、1.7 死循环与 ConcurrentHashMap 演进(2026 招聘 JD 真题复盘)

HashMap 树化阈值 8 的泊松分布依据、退化阈值 6 的防抖设计、JDK 1.7 头插成环死循环的排查与修复、1.8 尾插后为何仍线程不安全、ConcurrentHashMap 放弃分段锁的原因与 ForwardingNode 协作扩容、computeIfAbsent 死锁陷阱、大集合找重复的场景题选型,附三层追问链自测。题目来源为 38 期公开招聘 JD 样本与字节、拼多多、建行、美团、快手公开面经。

蒙眼过河 422

spdlog 同步日志库使用详解

spdlog是一个轻量级 C++ 日志库,支持同步日志、异步日志、多线程日志、日志文件轮转和自定义日志格式。对于嵌入式 Linux 应用开发,我建议先使用。它可以帮我们完成:日志级别过滤。时间戳和线程 ID 输出。日志格式化。终端和文件输出。多线程安全写入。日志文件按大小或时间轮转。我们不需要自己实现互斥锁、文件写入、日志格式化和轮转等底层功能。

zengjiajun2006的博客 1121

JavaSE通关手册:数据类型与变量

代码无需额外处理,编译时编译器自动完成转换。

xys_678的博客 231

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 368

IDEA 简介:从零认识 IntelliJ IDEA

IDEA 全称,是由 JetBrains 公司开发的一款主流 Java 集成开发环境(IDE)。它集代码编辑、编译、调试、测试、版本控制、构建管理等功能于一体,被大量 Java 开发者视为日常开发的首选工具。所谓 IDE(Integrated Development Environment,集成开发环境),就是把写代码所需的各类工具整合到同一个界面中。相比只用文本编辑器加命令行编译,IDE 能显著减少重复操作,让开发者把精力集中在业务逻辑上。

ztI10的博客 176

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 341
上一篇: 牛逼哄哄的京东 JD-hotkey !
下一篇: MybatisPlus 使用 saveOrUpdate() 方法踩坑记录(慎用)
公众号-芋道源码
博客等级 码龄10年 1634粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值