type
status
date
slug
summary
tags
category
icon
password
Property
Nov 8, 2024 04:59 PM
我认为短期内要想快速学习理解Spring,很重要的一定要具备的一点潜质:多问问为什么

Spring中的知识铺垫

阅读本文,需要知道的一点,想必也是赘述的一些内容,大佬们可以自行跳过,我为不熟悉Spring的朋友介绍一下。Spring作为一款老牌的强大的OOP思想的容器框架,他有着自己的一套管理Bean的方式,对于Spring框架,最为人所熟知的莫过于它的IOC容器与AOP代理,这也是最基本的。那么对于那些被Spring所管理的Bean而言,他们也有着自己的生命周期。Spring正是通过基于良好的OOP思想,为每一个它所管理的Bean构建了全生命周期链路…知道了这些,那么看下面的内容就不会头疼了哈。

SpringCloud的一些细节

这里用一个回顾SpringCloud基础中发现的一个细节来讲讲,Spring设计的精妙。
在微服务中,我们经常与分布式集群打交道,那么对于RPC请求的时候负载均衡策略因该是耳熟能详了,这里我们不讲广为人知的负载均衡算法,而是看看发起请求的客户端。
 
经常可以看到,在SpringCloud的一些早期项目中,在没有引入OpenFeign之前,发起RPC请求,我们会看到有RestTemplate与WebClient这两种原生的选择。通常,我们是这么使用的

对RestTemplate的管理

对WebFlux中WebClient的管理

 
通过被Spring管理后,可以在其他上下文的地方引入Bean进行调用。我们可以看到,对于RestTempalte我们是直接将new出来的新建的对象交由Spring管理,而WebClient则是通过创建WebClient的Builder,最后将Builder对象交由Spring管理,如果你仔细分析,按照形式主义的思想去看,很容易看出来,为什么这里不能直接给New,新创建的WebClient对象给到Spring管理呢?而是给出一个Builder给到Spring。
 
这时候,重点来了,我们反思一下,为什么要提供Builder给Spring,回忆一下,Spring管理Bean核心是什么,是为每一个Bean设计了自己的生命周期,通过代理的方式去增强Bean,那么答案呼之欲出了,Spring要管理Bean,增强Bean就必须遵守Spring的规则,Spring只能通过:
(1)CGLib增强字节码增强(需要继承类);
(2)基于JDK动态代理增强(需要实现接口);
 
而在看WebClient,WebClient被设计为一个响应式的、非阻塞的Web客户端,它的构建过程是通过WebClient.Builder完成的。一旦构建完成,WebClient实例就不应该被修改,这种不可变性使得它不适合被Spring通过动态代理增强,所以在回过头来就能想清楚原因了:
 
(1)能被CGLib增强字节码增强吗?
CGLIB是通过创建目标类的子类来实现代理的,这意味着它需要目标类不是final的,并且不包含final的方法。CGLIB通过字节码生成技术在运行时动态创建代理类。对于WebClient来说,他在调用了Build之后,是最终类,无法再修改,因此Spring Cloud的负载均衡器是基于WebClient.Builder进行配置的,而不能在WebClient实例上。
 
(2)能基于JDK动态代理来实现增强吗?
JDK动态代理是基于接口的代理机制,它只能代理实现了接口的类。这是因为JDK动态代理是通过创建一个实现了一组接口的代理类来实现的。由于WebClient是一个具体的类,不是一个接口,因此它不能被JDK动态代理
 
所以在注入WebClient的时候为什么要给Spring它的Builder而不是直接给出WebClient对象,清楚了吧,这就是融会贯通。
 
好,不迷糊,为了进一步验证是这个原因,再来看RestTemplate,这是JDK17底下Spring中RestTemplate的实现。可以很清晰的发现,RestTemplate继承了InterceptingHttpAccessor
类,实现了RestOperations 接口。
notion image
InterceptingHttpAccessor 是一个抽象类,它继承自 HttpAccessor 并添加了对请求拦截器的支持。这个类允许配置请求拦截器,这些拦截器可以在请求发送前后执行自定义逻辑。
RestOperations 接口定义了一组基本的REST操作,如GET、POST、PUT、DELETE等。RestTemplate 是这个接口的唯一实现,这意味着它提供了这些REST操作的具体实现。
这种设计使得 RestTemplate 既可以利用接口实现JDK动态代理,又可以通过继承类实现CGLIB字节码增强。
  • JDK动态代理:由于 RestTemplate 实现了 RestOperations 接口,它可以被JDK动态代理。JDK动态代理通过创建一个实现了相同接口的代理类来实现,这个代理类在运行时动态生成,可以拦截对应接口方法的调用。
  • CGLIB字节码增强:尽管 RestTemplate 继承了 InterceptingHttpAccessor 类,但由于CGLIB可以代理没有实现接口的类(通过创建目标类的子类),RestTemplate 也可以通过CGLIB进行增强。CGLIB不要求目标类实现接口,而是通过生成目标类的子类来实现代理。

总结

这就是Spring的精妙设计。

参考文章

 
致谢:
💡
欢迎各位大佬们赐教,子曰:三人行,必有我师焉
 
 
Spring浅谈一二(一):你有理解过Spring编码中的英文思想吗?关于新一代知识订阅:Follow
fntp
fntp
多一点兴趣,少一点功利
公告
type
status
date
slug
summary
tags
category
icon
password
Property
Sep 5, 2023 06:04 AM
📝 博客只为了记录我的学习生涯
😎 我的学习目标是成为一名极客
🤖 我热爱开源当然我也拥抱开源
💌 我期待能收到你的Email留言
📧 我的邮箱:stickpoint@163.com
欢迎交流~