架构

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。
源码剖析 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).
分布式锁之 Apache Curator InterProcessMutex

分布式锁之 Apache Curator InterProcessMutex

D瓜哥
对分布式锁耳熟能详。不过,一直关注的是基于 Redis 实现的分布式锁。知道 ZooKeeper 也可以实现分布式锁。但是,原来的想法是把 Redis 那个思路切换到 ZooKeeper 上来实现就好。今天了解到 Apache Curator 内置了分布式锁的实现: InterProcessMutex。查看了一下源码实现,发现跟基于 Redis 实现的源码相比,在思路上还是有很大不同的。所以,特别作文记录一下。 先来看一下,整体流程: 结合流程图和源码,加锁的过程是这样的: 先判断本地是否有锁数据,如果有则对锁定次数自增一下,然后返回 true; 如果没有锁数据,则尝试获取锁: 在指定路径下创建临时顺序节点 获取指定路径下,所有节点,检查自身是否是序号最小的节点: 如果自身序号最小,则获得锁;否则 如果自身不是序号最小的节点,则通过 while 自旋 + wait(times) 不断尝试获取锁,直到成功。 获得锁后,把锁信息缓存在本地 ConcurrentMap<Thread, LockData> threadData 变量中,方便计算重入。 在 ZooKeeper 中的结构大致如下: 下面我们逐个方法进行分析说明。先来看一下 InterProcessMutex 的注释: /** * A re-entrant mutex that works across JVMs.
Spring 扩展点实践:整合 Apache Dubbo(二)

Spring 扩展点实践:整合 Apache Dubbo(二)

D瓜哥
在 Spring 扩展点实践:整合 Apache Dubbo(一) 中,D瓜哥介绍了 Dubbo 如何使用 Spring 的插件机制与 Spring 整合。限于篇幅原因,上一篇文章只介绍到了服务提供者的注册。本篇文章继续上一篇文章的主题,继续介绍 Spring 与 Dubbo 的整合过程。先来讲解一下服务消费者的生成过程。 Dubbo 生成服务消费者的过程 先来看看 XML 配置文件: dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-consumer"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/> </beans> 我们先看一下 ReferenceBean 类的声明: org.apache.dubbo.config.spring.ReferenceBean public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { // 此处省略 N 行代码 @Override public Object getObject() { return get(); } // 此处省略 N 行代码 @Override @SuppressWarnings({"unchecked"}) public void afterPropertiesSet() throws Exception { // Initializes Dubbo's Config Beans before @Reference bean autowiring prepareDubboConfigBeans(); // lazy init by default.