Spring AOP 源码分析:创建代理(二)

Spring AOP 源码分析:创建代理(二)

D瓜哥
Spring AOP 源码分析:入门 中,梳理出来了 Spring AOP 的入口。 Spring AOP 源码分析:获得通知 中着重介绍了如何获取通知。上一篇文章 Spring AOP 源码分析:创建代理(一) 重点介绍了一下切面链的组装和基于 JDK 动态代理的 AOP 的实现,这篇文章介绍一下基于 cglib 的代理类是生成。 cglib 简介 CGLIB(Code Generator Library)是一个高性能的代码生成库,被广泛应用于 AOP 框架(Spring)中以提供方法拦截功能,主要以继承目标类的方式来进行拦截实现,因此 CGLIB 可以对无接口的类进行代理。 CGLIB代理主要通过操作字节码的方式为对象引入方法调用时访问操作,底层使用了ASM来操作字节码生成新的类,ASM是一个短小精悍的字节码操作框架。CGLIB的应用栈如下: 最新版的 Hibernate 已经把字节码库从 cglib 切换为 Byte Buddy。 JDK 动态代理是通过实现 InvocationHandler 接口,在其 invoke 方法中添加切面逻辑。而 cglib 则是通过实现 MethodInterceptor 接口,在其 invoke 方法中添加切面逻辑。 下面看一下在 Spring 中,是如何实现利用 cglib 来实现 AOP 编程的? CglibAopProxy 先看一下创建代理对象的方法:
Spring AOP 源码分析:创建代理(一)

Spring AOP 源码分析:创建代理(一)

D瓜哥
Figure 1. Spring AOP 源码分析:入门 中,梳理出来了 Spring AOP 的入口。上一篇文章 Spring AOP 源码分析:获得通知 中着重介绍了如何获取通知。接着上一篇文章,这篇文章介绍一下如何创建代理。 AbstractAutoProxyCreator#createProxy protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClassConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // 创建代理工厂对象 ProxyFactory proxyFactory = new ProxyFactory(); // 获取当前类的属性 proxyFactory.copyFrom(this); //如果没有使用CGLib代理 if (!proxyFactory.isProxyTargetClass( { // 是否可能使用CGLib代理 // 决定对于给定的 Bean 是否应该使用 targetClass 而不是他的接口代理 if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { // 查看beanClass对应的类是否含有InitializingBean.
Spring AOP 源码分析:获得通知

Spring AOP 源码分析:获得通知

D瓜哥
在文章 Spring AOP 处理流程概述 中,对 Spring AOP 有了一个整体认识。在文章 Spring AOP 源码分析:入门 中,对 Spring AOP 的相关入口做了分析。这篇文章就带大家看一看,Spring AOP 是如何获取通知的? 示例代码 在 如何阅读 Spring 源码?: 示例代码 中,已经给出了一个完整的 AOP 示例代码。为了节省篇幅,请直接参考那篇文章的示例代码,这里就不在赘述。 注册 Advice(通知/增强) 请根据 Spring AOP 源码分析:入门 中提到的关键方法入口处,打上断点,开始调试。 首先,需要明确一点的是:对于切面(使用 @Aspect 注解标注过的类)在 Spring 容器中,也是被统一f封装为 BeanDefinition 实例的,也需要通过一个方式,将其注册到 Spring 容器中。比如,就像 示例代码 那样,通过 ImportSelector 方式,使用类名,将其注册到容器中。这样,就可以利用 Spring 容器对 Bean 的 API 来统一处理了。 Advice(通知/增强)几乎是在意想不到的地方完成注册的:在第一次调用 AbstractAutoProxyCreator#postProcessBeforeInstantiation 方法时,通过 AspectJAwareAdvisorAutoProxyCreator#shouldSkip 方法,完成了切面的注册。下面,我们对这个过程抽丝剥茧,逐步分析。 先来看看 findCandidateAdvisors 方法: AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors @Override protected List<Advisor> findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules.
如何阅读 Spring 源码?

如何阅读 Spring 源码?

D瓜哥
昨晚原计划给几个朋友简单介绍一下阅读 Spring 源码的方法。结果,大家因为各种原因没能及时参加。后来,就取消分享了。干脆写一篇文章出来,感兴趣欢迎自取。 代码准备 Spring Framework 是开源的,代码托管在 GitHub 上: Spring Framework。任何人都可以方便地获得它的源代码。所以,如果想阅读 Spring 的源代码,当然是直接把代码克隆到本地,然后直接在 IDE(推荐 IDEA)中进行调试了。另外,还需要存放自己写一些测试和文档。所以,最好把代码 fork 到自己的账户下,从 master 上切出一个新分支并 push 到自己的 Repo 中,这样自己就可以随意更新了。具体步骤如下: 克隆代码 # 直接克隆原始仓库为 origin git clone git@github.com:spring-projects/spring-framework.git fork 代码,D瓜哥直接 fork 到自己账户下了: diguage/spring-framework。 添加原创仓库地址: # 添加自己仓库为 diguage # 这样就能在所有项目中保持命名的一致性,方便标识 git remote add diguage git@github.com:diguage/spring-framework.git 创建新分支 # 创建新分支 git switch -c analysis # 将新分支 push 到自己的 Repo 中 git push diguage analysis 这样,在这个新分支上,就可以随意折腾了。
Spring AOP 源码分析:入门

Spring AOP 源码分析:入门

D瓜哥
在上一篇文章 Spring AOP 处理流程概述 中,对 Spring AOP 有了一个整体认识。这篇文章就带大家做一个细致的源码分析。 登堂入室 使用 Spring AOP 也很简单,只需要在配置类上加上 @EnableAspectJAutoProxy 注解即可。这个注解处理过程与 Spring 扩展点实践:整合 MyBATIS 中 “@MapperScan 处理” 类似,不同的是,Spring AOP 注册了 AnnotationAwareAspectJAutoProxyCreator,它是一个 InstantiationAwareBeanPostProcessor。具体的类图如下: Figure 1. AnnotationAwareAspectJAutoProxyCreator 的继承体系 在正式开始源码分析之前,有一点必须强调一下:Spring AOP 只是借用了 AspectJ 的一些注解和个别关键 API,而整体实现是 Spring 自己完成的,并不是基于 AspectJ 实现的。这一点跟很多人的认识是不一样的,需要特别指出。 D瓜哥在 Spring Bean 生命周期概述 中指出:创建 AOP 代理对象,有两个时机: 调用 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 时,通过调用 AnnotationAwareAspectJAutoProxyCreator 对象的 postProcessBeforeInstantiation 方法来创建对象; 调用 BeanPostProcessor#postProcessAfterInitialization 时,通过调用 AnnotationAwareAspectJAutoProxyCreator 对象的 postProcessAfterInitialization 方法来创建对象;
TCP 三次握手和四次挥手

TCP 三次握手和四次挥手

D瓜哥
传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。在简化的计算机网络 OSI 模型中,它完成第四层传输层所指定的功能。 毫不夸张地说,TCP 协议是目前整个互联网的基础。它解决了一系列的网络问题。带来的结果,就是协议本身非常复杂。考虑到文章篇幅问题,本文着重说明 TCP 建立连接时的三次握手过程和关闭连接时的四次挥手过程。 三次握手 Figure 1. TCP 三次握手 第一次握手(SYN=1, seq=x): 客户端发送一个 TCP 的 SYN 标志位置 1 的包,指明客户端打算连接的服务器的端口,以及初始序号 x,保存在包头的序列号(Sequence Number)字段里。 发送完毕后,客户端进入 SYN_SEND 状态。 第二次握手(SYN=1、seq=y;ACK=1、ACKnum=x+1): 服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为 1。服务器端选择自己 ISN 序列号,放到包头的序列号(Sequence Number)字段里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加 1,即 x+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。 第三次握手(ACK=1,ACKnum=y+1) 客户端再次发送确认包(ACK),SYN 标志位为 0,ACK 标志位为 1,并且把服务器发来 ISN 的序号字段+1,放在确定字段中发送给对方,即数据段放写 y+1。
HikariCP 源码分析 --  FastList

HikariCP 源码分析 -- FastList

D瓜哥
在前面的文章 HikariCP 源码分析 — ConcurrentBag 中,D瓜哥分析了一下 HikariCP 中一个非常重要的数据结构 ConcurrentBag。 今天,继续再介绍 HikariCP 中另一个很关键的数据结构: FastList。 FastList 本身的实现非常简单,要理解它的奥秘,就需要结合 Java 原生集合类的 ArrayList 来比较性地看。 构造函数 先来对比一下两者的构造函数。先来看看 FastList: FastList public final class FastList<T> implements List<T>, RandomAccess, Serializable { private static final long serialVersionUID = -4598088075242913858L; private final Class<?> clazz; private T[] elementData; private int size; / * Construct a FastList with a default size of 32. * @param clazz the Class stored in the collection */ @SuppressWarnings("unchecked") public FastList(Class<?
使用 Hugo 搭建博客

使用 Hugo 搭建博客

D瓜哥
一个朋友对D瓜哥的博客感兴趣,觉得很好玩。问我怎么玩,D瓜哥也懒得手把手教了,干脆写篇文章来做个说明吧。也许对其他朋友也有所帮助。 潮起潮落 D瓜哥早在 2012年就使用 WordPress 搭建了一个博客: "地瓜哥"博客网—分享技术带来的喜悦,WordPress 不亏是全世界最流行的开源 CMS 系统,各种插件可以满足非常对的需求。当年D瓜哥还想研究一下它的源代码,可惜对 PHP(对,就是那个拍黄片的)无感,没有坚持下去。 但是,在熟悉了 MarkDown、 Asciidoctor(D瓜哥是 AsciiDoctor 的死忠粉,坚决支持它的发展) 等轻量级标记语言后,愈发觉得 WordPress 太繁琐:写作一个小时,排版发布一小时。实在是太浪费时间了。 先尝试了一下 Antora,之所以选它,是因为它是 AsciiDoctor 的作者发起的项目,对 AsciiDoctor 的支持性非常好。尝试了一段时间后,发现它更适合写产品说明书,更适合像阿里云等这种对外提供产品,需要提供在线产品说明书的情况。不适合做个人博客。 去年,经过多次测试后(主要测试对 AsciiDoctor 的支持性),发现现在D瓜哥用的这个主题对 AsciiDoctor 支持得不错,随后下定决心切换到了 Hugo。 Hugo 简介 关于 Hugo 的介绍,直接转摘维基百科的介绍了: Hugo 是一个用 Go 编写的静态网站生成器,2013由 Steve Francia 原创,自 v0.14 (2015年) 由 Bjørn Erik Pedersen 主力开发,并由全球各地的开发者和用户提交贡献。Hugo 以 Apache License 2.0 许可的开放源代码项目。 Hugo 一般只需几秒钟就能生成一个网站(每页少于 1 毫秒),被称为“世界上最快的网站构建框架”,也使 Hugo 大受欢迎,成为最热门的静态网站生成器之一,被广泛采用。例如,2015年7月,Netlify 推出专为 Hugo 而设的网站托管服务,而2017年,Smashing Magazine 推出重新设计的官方网站,从原来的 WordPress 迁移到基于 Hugo 的 JAMstack 解决方案。
源码剖析 Spring 循环依赖

源码剖析 Spring 循环依赖

D瓜哥
循环依赖在编程中是一个常见问题(当然,这并不是最佳实践)。并且,Spring 如何解决循环依赖这个问题在面试中也经常见。下面,D瓜哥就从源码的层面深入剖析一下这个问题。 示例程序 先展示一下示例程序: package com.diguage.truman.context; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.stereotype.Component; /** * @author D瓜哥, https://www.diguage.com/ * @since 2020-05-24 13:02 */ public class CircularDependenceSingletonTest { public static final Log log = LogFactory.getLog(CircularDependenceSingletonTest.class); @Test public void test() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(Config.class); applicationContext.refresh(); log.info(applicationContext.getBean(A.class)); log.info(applicationContext.getBean(B.class)); log.info(applicationContext.getBean(C.class)); log.info("-A--------"); A a = applicationContext.getBean(A.class); log.info(a); log.
分布式锁之 Apache Curator InterProcessReadWriteLock

分布式锁之 Apache Curator InterProcessReadWriteLock

D瓜哥
在上一篇文章 分布式锁之 Apache Curator InterProcessMutex 中介绍了基于 ZooKeeper 实现的互斥锁。除此之外,还可以实现读写锁。这篇文章就来简要介绍一下 InterProcessReadWriteLock 的实现原理。 老规矩,先看看类的注释: /** * <p> * A re-entrant read/write mutex that works across JVMs. Uses Zookeeper to hold the lock. All processes * in all JVMs that use the same lock path will achieve an inter-process critical section. Further, this mutex is * "fair" - each user will get the mutex in the order requested (from ZK's point of view).