【Spring源码学习系列】基础架构和环境搭建
一直以来都把精力花在中间件的研究和系统设计上,忽略了离我最近的spring,最近开始学习spring的源码了,为了学习到成体系的spring知识和提高学习效率,想要找了一本书看,最终选的是郝佳的《Spring源码深度解析(第2版)》,虽然这本书在豆瓣上褒贬不一,但是spring源码相关的确实也没有很高评分的书,有的也是很老的spring版本,看了这本书的目录后还是决定试一试。不过我只会以这本书的内容框架作为我学习的内容框架。并不会完全只参考这本书,对于一些有疑惑的知识我也会搜集多方资料作对比,文章内容也不会完全以这本书的内容为准,最后我会从我自己的角度评价一下这本书。
然后我也打算在学习spring源码的过程中做一些笔记,把核心内容和自己的理解记录下来,首先是因为和一个朋友有定期发布文章的约定,而且也能够通过做一些笔记来加强自己的记忆,在后面淡忘的时候也能通过自己的笔记快速回忆起来这块内容,最后还能通过输出内容来为自己的学习增加动力,让自己看到阶段性的成果。
首先记录一下spring的基本架构,基础概念和相关环境搭建
一 基本架构
在学习和部署spring源码之前我们有必要先了解一下spring的基础,包括spring的架构还有相关的核心概念,Spring框架是一个分层架构,每一层包含一系列的功能要素,分为约20个模块,下面就是spring的架构图
1.1. Core Container层
Core Container层提供IoC容器功能,包含了Spring的通用工具类,公共抽象接口和容器内对Bean的定义、配置、创建、生命周期管理、依赖注入等能力的支持。是Spring 的根基,所有上层模块(AOP、Data Access、Web 等)均基于 Core Container 实现。
1.1.1 Core模块
Core模块提供了Spring 的核心工具类、资源访问抽象和IoC 容器的最低层接口定义,比如模块内的BeanFactory定义了Bean 的获取,是否存在,是否是单例等方法,Resource作为统一资源访问抽象接口,屏蔽不同资源位置(classpath、file、URL)的访问差异,定义了判断资源是否存在,获取输入流的方法。其他模块都会依赖这个模块。
1.1.2 Beans模块
Beans模块提供 Bean定义、配置解析、注册、依赖注入、属性装配等Bean生命周期管理的实现。比如模块内的BeanDefinition用于记录Bean的名称,类信息,属性值,构造函数参数,生命周期回调方法等,BeanWrapper则用于封装对Bean对象进行属性封装与操作。主要就是设置和获取Bean对象的属性值。
1.1.3 Context 模块
Context模块提供了管理容器上下文模块的能力,比如获取容器的某个Bean(依赖于Core模块的BeanFactory接口),加载国际化资源,发布事件,访问各种资源等等,比如Context模块定义和实现了国际化消息解析接口MessageSource,MessageSource接口定义了根据locale获取本地化字符串的方法,Context模块还提供了用于Spring事件发布监听机制的事件类(ApplicationEvent),事件监听类(ApplicationListener)等接口和实现类。这些能力可以通过模块提供的聚合类ApplicationContext类来完成。
1.1.4 Expression (SpEL) 模块
SpEL(Spring Expression Language)是 Spring 提供的一个功能强大的表达式语言,用于在运行时查询和操作对象图。主要功能包括访问对象属性、调用对象方法,类的静态方法,计算表达式的值(算术运算、逻辑运算、关系运算等),在Spring中用于配置文件中动态计算值(如 @Value
注解),Spring Security 的权限表达式等地方,比如@Value("#{T(java.lang.Math).random() * 100.0}")里面的"#{T(java.lang.Math).random() * 100.0}"就是SpEl,表示获取值时调用Math的random()方法并乘于100.0,
SpEL模块的功能就是提供SpEL相关的能力,比如SpEL的解析和计算,比如SpEL模块的ExpressionParser接口就是用于解析表达式字符串,生成 Expression 对象。
1.1.5 Instrument模块
提供类加载器级别的工具,主要用于类增强,支持类加载时织入(Load-time Weaving, LTW),在类加载到 JVM 时对类进行增强,Spring AOP和AspectJ LTW都需要此模块来配合。模块里面比较重要的类就是InstrumentationSavingAgent类了,它在JVM 启动时运行,保存Instrumentation对象供后续使用。主要用于支持JPA weaving。
这里的Instrumentation是指JDK 提供的Instrumentation API,位于 java.lang.instrument包。JVM 启动时,若使用了 -javaagent 参数,并且 agent jar 的 MANIFEST.MF 中配置了 Premain-Class,JVM 会调用该类的 premain(String, Instrumentation) 方法,并传入Instrumentation对象,Instrumentation 提供对JVM的运行时检测与字节码修改能力主要功能包括注册 ClassFileTransformer,实现 load-time weaving(加载时织入),获取JVM已加载的所有类信息,重定义已加载类的字节码(在支持的 JVM 下),这个是JDK自带的功能,这里就不仔细介绍了。有兴趣的读者可以专门去研究一下。
1.1.6 JCL模块
Jakarta Commons Logging 是 Apache 提供的一个 日志抽象框架,全称 Commons Logging,常简称 JCL。它是 日志抽象层,而不是具体的日志实现。提供统一的日志 API,底层可对接 Log4J、Java Util Logging(JUL)等,自动适配 Log4j、SLF4J、JUL。这个模块主要提供了LogFactory类,用于获取Log实例,Log类提供统一的日志接口。
1.2. AOP层
AOP(Aspect Oriented Programming)即面向切面编程,是Spring提供的另一核心功能,用于在不修改业务代码的情况下,将横切逻辑(日志、事务、权限校验、监控等)织入目标代码中,提高代码模块化和可维护性。Aop层就是Spring提供的AOP的能力,
1.2.1 AOP模块
AOP模块为Spring提供了基于代理的切面编程能力,实现了切面(Aspect)、通知(Advice)、切入点(Pointcut)、织入(Weaving)等AOP基本概念的抽象与实现。这里主要定义切面编程的基础接口(Advice、Pointcut、Advisor 等),提供基于 JDK 动态代理(接口代理)或 CGLIB(子类代理)的 AOP 实现,与 IoC 容器集成,实现声明式事务管理、方法拦截、权限控制等功能
Advice:通知,定义在 JoinPoint 处要执行的动作
Pointcut:切点,定义匹配哪些连接点
Aspect:切面,是 通知(Advice)和切点(Pointcut)的组合,它定义了 横切关注点 的整体。
1.2.2 Aspects模块
Aspects模块是 Spring 对 AspectJ 框架的支持模块,提供了与 AspectJ 的整合功能,提供对 AspectJ 注解风格切面的支持(@Aspect),支持 AspectJ 的 Load-time Weaving(加载时织入)和 Compile-time Weaving(编译时织入)并且使Spring容器能够识别和处理 AspectJ 定义的切面,定义了@Aspect @Before, @After, @Around, @AfterReturning, @AfterThrowing等注解,需要依赖上面的AOP模块提供的代理来实现其处理逻辑,
1.3. Data Access/Integration层
Data Access/Integration层是Spring的数据访问和集成层,提供spring对数据库访问、ORM框架整合、事务管理、消息传递,远程调用,缓存,任务调度等的支持,提供连接和集成外部系统(数据库,外部服务等)的能力。
1.3.1 JDBC 模块
JDBC模块通过封装JDBC,包括封装 Connection/Statement/ResultSet 处理,统一异常处理等,简化了对数据库的连接和操作,比如模块定义和实现了JdbcTemplat接口来提供简单易用的数据库调用API。定义了和实现了RowMapper接口, 将 ResultSet 的每一行映射为 Java 对象
1.3.2 ORM模块
ORM模块顾名思义就是集成 Hibernate、JPA、MyBatis 等 ORM 框架。比如实现了JPA 事务管理器JpaTransactionManager,配置Hibernate SessionFactory的工厂Bean,LocalSessionFactoryBean类
ORM(Object-Relational Mapping)是一种程序设计技术,用于将面向对象编程语言中的对象(Java 对象)与关系型数据库(RDBMS)的表 进行映射,使得: 你可以直接通过操作对象(例如 Java Bean),来实现对数据库表的插入、更新、查询、删除(CRUD), 而不需要手动编写繁琐的 SQL 语句和 ResultSet 解析。
1.3.3 JMS 模块
JMS模块封装了Java Message Service,提供一套更加简单易用的消息发送与监听API。模块内提供了JmsTemplate类,用于发送与接收消息和管理Connection/Session/Producer/Consumer,也提供了MessageListener类异步消息监听接口
1.3.4 Messaging模块
提供消息抽象模型,支持消息通道、处理器,比如定义了Message来表示消息,包含消息头和消息体,定义了MessageChannel来表示消息传输通道。
1.3.5 Transaction (tx) 模块
tx模块提供了spring的声明式/编程式事务管理能力。比如定义了核心事务管理接口PlatformTransactionManager,包括了获取事务状态,提交和回滚事务的方法。以及提供了TransactionDefinition用来定义事务隔离级别、传播行为、超时时间。其他的比如JMS,ORM等模块需要用到事务的地方都会实现自己的PlatformTransactionManager来实现事务能力。
1.3.6 OXM模块
OXM就是Object-XML Mapping,OXM模块主要就是支持将Java对象与XML相互转换。主要有两个核心接口,将对象序列化为XML的Marshaller接口和将XML反序列化为对象的Unmarshaller
1.4 Web层
Web层是 Spring 提供的 面向 Web 应用程序的功能层,主要用于构建基于 Servlet 的 Web 应用、RESTful API、以及响应式 Web 应用,提供了处理 HTTP 请求、请求路由分发、参数绑定、数据转换、视图解析、异常处理、WebSocket 通信等能力。也是Spring面向用户请求的最外层,作为用户与应用程序交互的入口,负责接收并处理来自浏览器或客户端的请求,将请求数据传递给业务逻辑层,并将业务逻辑层返回的结果封装成 HTTP 响应或 WebSocket 消息返回给用户。
1.4.1 Web模块
Web模块提供 Spring 基础的 Web 功能支持,包括封装 Servlet API、文件上传解析、客户端 REST 调用等功能。比如模块提供了 MultipartResolver 接口用于多部分文件上传解析,提供了 RestTemplate 类,封装了基于HttpURLConnection或Apache HttpClient 的 REST 调用逻辑,提供了便捷的 GET、POST、PUT、DELETE 方法。Web层的其他模块也都会依赖Web模块来完成网络传输。
1.4.2 Web MVC模块
Web MVC模块就是Spring的Web MVC框架实现模块,提供了基于Servlet的MVC架构,核心是 DispatcherServlet类,作为前端控制器,接收所有请求并分发给相应的 Handler处理。模块的主要组件有负责根据请求URL匹配处理器的HandlerMapping,调用处理器方法的HandlerAdapter,负责解析视图名称为实际View对象的ViewResolver,封装模型数据与视图名称的对象ModelAndView等等,同时支持注解控制器(@Controller, @RequestMapping)以及数据绑定、参数解析、异常处理、拦截器等能力。
1.4.3 WebFlux模块
WebFlux模块提供了 Spring 5 引入的响应式 Web 框架,基于 Reactor 提供的 Flux/Mono 实现非阻塞式编程模型,适用于高并发 IO 密集型场景。 模块提供了函数式编程风格的路由与处理 WebFlux.fn,注解风格支持@RestController + RouterFunction,将各种响应式库类型适配为 Flux/Mono的ReactiveAdapterRegistry, WebFlux 可以运行在 Netty、Undertow 等非 Servlet 容器,也可运行在支持 Servlet 3.1+ 的容器上。
1.4.4 WebSocket 模块
WebSocket模块为Spring提供了基于WebSocket协议的双向通信支持,支持浏览器与服务器建立持久连接,实现实时消息推送。比如模块定义和实现了WebSocketHandler接口用于处理WebSocket消息,提供了SockJsClient和SockJsService对SockJS协议进行支持,兼容不支持原WebSocket的浏览器,也提供了注解配置支持@EnableWebSocket和@ServerEndpoint,底层可结合 Spring Messaging 模块进行消息编程,支持 STOMP 协议集成
1.5 Test层
Test层主要提供对 JUnit/TestNG 的集成,用于项目进行一些Mock测试和Web测试。
1.5.1 Test 模块
支持Spring容器的单元测试和集成测试。比如提供了在JUnit中加载Spring ApplicationContext的SpringJUnit4ClassRunner / SpringRunner,用于测试Spring MVC 控制器的MockMvc,以及@SpringBootTest ,@WebMvcTest等测试注解
二 环境搭建和依赖导入
如果你对java和spring的使用还不熟悉,是不建议自己看spring的源码的,这样不仅会浪费你很多时间,而且很多地方也无法真正看懂,所以这里会假设你电脑已经装备了java环境并且已经有了一定的开发经验。
2.1 引入项目
在github上拉取Spring核心代码
我们直接在github上拉取spring的源码,当然,如果你有定制化spring或者参与spring建设的野心你也可以先fork一下spring的源码再拉取,如果不想科学上网的话也可以从gitee等平台上拉取spring的镜像。我们这里直接在idea上拉取spring5.2.x的代码
github:https://github.com/spring-projects/spring-framework/tree/5.2.x
git:https://github.com/spring-projects/spring-framework.git
2.2 依赖导入
设置maven仓库镜像
引入项目后我们需要修改一下build.gradle文件,设置一下maven仓库镜像,不然运行build太慢了
repositories {mavenCentral()maven { url "https://repo.spring.io/libs-spring-framework-build" } }
改为
repositories {maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/'}maven{ url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}mavenCentral()maven { url "https://repo.spring.io/libs-spring-framework-build" } }
编译compileTestJava模块
./gradlew :spring-oxm:compileTestJava
最后看到
BUILD FAILED in 1m 30s
编译测试完成,说明代码没问题
然后在idea上导入spring依赖
也可以直接使用命令
./gradlew build
提示
提示要我使用java12来作为gradle的jvm,而我现在是Java21,然后我在idea上调整一下gradle的jvm
重新执行,经过两年半的时间依赖导入完成了,后面就可以开始学习代码了
BUILD SUCCESSFUL in 29m 36s
BUILD SUCCESSFUL in 409ms
2.3 番外知识 AspectJ
AspectJ 是一个面向切面编程的扩展框架,为 Java 提供了完整的 AOP 语法和编译器支持,Spring使用了AspectJ技术来实现Aop,使用aj文件来编写Aop逻辑,普通的javac并不能编译这些aj文件,需要使用AspectJ编译器ajc(增强版javac)来编译项目,才能把aj文件编译为class文件,当然spring源码的gradle配置上已经引入了AspectJ,并且在构建项目时候能够使用它来编译aj文件,正常情况下我们不需要自己安装aspectJ,但是我这里也额外记录一下自己安装AspectJ。
下载和安装aspectJ编译器
我们直接进入aspectJ的github页面下载,下载最新版本的jar包就好了
github页面:https://github.com/eclipse-aspectj/aspectj/releases/
下载后运行这个jar包
java -jar aspectj-1.9.24.jar
弹出弹窗
跟随弹窗引导安装,最终点击finish就完成了安装,中间我们可以选择自己的安装目录,安装完成后可以去自己安装目录看看是否已经有了,如图所示