spring5.2.x 源码阅读环境搭建gradle构建编译
- 一、依赖工具
- 二、下载源码
- 三、开始构建
- 四、编译源码
- 五、代码测试
- 六、问题及解决方案
- spring 源码各模块作用
Spring 系列生态十分丰富,涉及到各个方面。但是作为 Spring 生态的核心基础 Spring,是最重要的环节,需要理解 Spring 的设计原理,就需要深度研读 Spring 源码。本文着重阐述当前最新版 spring5.2.x 的源码构建过程,由于构建工具采用 gradle(spring 团队已经抛弃 maven 构建,全面拥抱 gradle 了),很多小伙伴不太熟悉 gradle,所以构建过程有少许困难。本文将带大家手把手的搭建 spring 源码阅读环境构建。
由于spring5.3.x 目前处于master分支,随时都有变更提交,而且5.3.x版本还没有完全开发完成,编译会有很多报错,以及jdk的兼容问题。(需要jdk11)
***一、依赖工具
1.1、Git
拉取源码使用
1.2、JDK1.8 及以上
一般小伙伴机器上都已经装好了
1.3、gradle 5.6.3
打开 services.gradle.org/distributio… 选择最新版本:gradle-6.5.1-all.zip(all 版本是带源码的)
下载解压后目录结构如下:
设置环境变量:
GRADLE_HOME
Path变量中添加如下配置:
完成后打开 cmd,执行
gradle -v
表示已经安装成功,版本为 5.6.3
1.4、idea2020.1.2
网上很多朋友表示 idea2020 之前的版本导入时始终有问题,各种编译报错不通过,建议升级到 2020.1 版本,本文当前使用的就是 2020.1.2 版本。分享下载链接地址如下:
链接: pan.baidu.com/s/1GP4c_9_q… 提取码: qksz
以前使用idea2019版本时,控制台乱码都是修改这两处来解决
1、idea安装的bin目录下找到这两个文件
这两个文件最后一行添加上 -Dfile.encoding=UTF-8
2、idea中tomcat配置上添加 -Dfile.encoding=UTF-8
这样基本就可以解决了。
***二、下载源码
从官方仓库 github.com/spring-proj… Fork 出属于自己的仓库。也可以从 github 仓库 fork 到 oschina git 开源中国。主要提高代码下载速度
- 为什么要 Fork?既然开始阅读、调试源码,我们可能会写一些注释,有了自己的仓库,可以进行自由的提交。
- 本文使用的 Spring 版本为 5.3.x 的 master 分支代码 (5.3.0-SNAPSHOT)。
- 使用 IntelliJ IDEA 从 Fork 出来的仓库拉取代码。因为 Spring 项目比较大,从仓库中拉取代码的时间会比较长。所以我这边是 git clone 到本地,然后再导入 idea 中的。
具体过程如下:
2.1、github 仓库 Fork 代码
打开 github.com/spring-proj… ,点击右上角 Fork 即可,这样就把 spring 仓库 fork 到自己的仓库中了。
2.2、clone 代码
选择一个目录,我的是 E:\mypro\IdeaProjects,空白处右击 Git Bash Here
执行:
git clone https://gitee.com/peterchain/spring-source-code.git
下载到本地。
然后切换分支到5.2
***三、开始构建
3.1、前置准备
构建之前先安装 gradle,因为 spring 是 gradle 构建的。
在 cmd 中进入源码根目录,输入 gradlew.bat 命令,脚本将自动下载 gradle-6.5.1-bin.zip 包(这一步其实也可以省略,可以直接将源码导入 idea 中)
3.2、在 IDEA 中 open 源码项目
【文件】->【Open】,选择源码根目录,->【ok】
3.3、设置项目 Gradle 配置
打开项目之后,【File】->【Settings】,设置 Gradle,Gradle user home 。如果不设置的话,它默认是 C:\Users\ling.gradle,这个目录你就可以认为相当于是我们的本地的 maven 仓库,gradle 编译项目所依赖的 jar 都会下载后放入这个目录中。jvm 默认我这里就是 jdk8,jdk 至少就是 jdk8。仓库目录。
3.4、gradle 配置文件修改
设置完毕之后,打开工程下的 gradle 目录->wrapper 目录下的,gradle-wrapper.properties 文件。因为 gradle 每次编译都会从官网下载指定版本(gradle-5.6.3-all.zip),所以我们在它第一次下载完之后,将 distributionUrl 设置成本地文件,这样就不会每次编译都从官网下载了,如下图:
distributionUrl=file:///e:/tools/gradle-5.6.3-all.zip
(这里选择 gradle 的压缩包的全路径地址)
(建议用最新的 idea2020 版本,最省事,打开后自动编译,免去不必要的麻烦)
3.5、build.gradle 设置
打开 build.gradle 文件(这个就相当于是 maven 的 pom 文件),在文件头部加上:
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
}
}
如下图:
继续往下查找,找到如下代码段:
红色线框部分是需要添加的,主要就是添加阿里云镜像和 spring 的插件库,这里的镜像和我们配置 maven 的镜像是一样的,目的就是加快依赖包的下载速度,如果不配置镜像的话,可能会编译几个小时。具体修改如下:
repositories {
//新增以下 2 个阿里云镜像
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" }
maven { url "https://repo.spring.io/milestone" } // Reactor
//新增 spring 插件库
maven { url "https://repo.spring.io/plugins-release" }
}
3.6、验证是否构建成功
构建成功之后,找到 ApplicationContext 类
打开后,按下 Ctrl+Alt+U 键,如果出现下图所示类图界面说明构建成功了!(构建过程就是找依赖对象的过程)
此时可以查看 Spring 的源码了,但是我们需要在源码的基础上面进行修改,开发,调试,添加注释等等操作,所以需要将源码进行编译打包,下面就是将源码编译的过程。
***四、编译源码
4.1、查看 import-into-idea.md 文档
在构建完成源码之后,就搭建好了阅读源码的环境了,此时我们还需要将源码编译打包。
在编译之前需要进行一些配置修改,可以查看 import-into-idea.md 文档。
4.2、编译 spring-oxm 模块
文档要求先编译 spring-oxm 下的 compileTestjava,点击右上角 gradle 打开编译视图,找到 spring-oxm 模块,然后在 other 下找到 compileTestjava,双击即可!
编译成功!
4.3、编译 spring-core 模块
保险起见再编译下 spring-core 模块,因为之后的 spring-context 依赖于 core,方法同上。
4.4、整体编译
核心模块都编译完成且成功之后,开始编译整个工程(这个过程非常耗时间,可能 10-20 分钟!),如下图:打开顶层 spring->build->build
经过一段时间编译,build 成功!(每个人电脑的性能不一样,所需时间也不一样)
本人在编译过程中没有发现问题,非常顺利,但是在下面的测试中发现了问题。
五、源码测试
5.1、添加测试模块
完成了上面的过程后,我们可以自己编写一个模块测试该源码构建编译过程是否真正成功完成!
步骤:【File】->【New】->【Module...】
在 Spring 中添加自己的 module 模块,同样选择 gradle 构建。
5.2、新模块添加依赖
新模块 Parent:spring,Name:spring-mytest(我们的测试模块作为 spring 源码的子模块添加进来,下面的配置默认即可)
5.3、构建测试模块
Finish,idea 会自动帮助我们构建 spring-mytest 模块。打开全局配置文件:settings.gradle 文件,拉到最下面,我们看到系统自动加上了 spring-mytest 模块。
找到我们自己的测试模块 spring-mytest,打开 build.gradle 文件(相当于是 pom 文件),注意:idea默认创建的构建文件可能无法直接使用(build.gradle),需要重命名。
这里需要重命名spring-mytest子模块下的build.gradle文件,否则盖子模块无法加载依赖包,改成模块名称: ${moduleName}.gradle.
如下图:
默认 dependencies 依赖(这里的 dependencies 和 maven 里的依赖是一样的)只有一个 junit,我们需要手工添加 spring-context,spring-beans,spring-core,spring-aop 这 4 个核心模块,具体如下:
dependencies {
//添加完要构建一下,否则代码中无法引用
compile(project(":spring-context"))
compile(project(":spring-beans"))
compile(project(":spring-core"))
compile(project(":spring-aop"))
testCompile group: 'junit', name: 'junit', version: '4.12'
}
5.4、编写测试代码
下面编写一个简单的 applicationContext 获取容器用的 bean,主要是测试 Spring 源码构建编译过程是否成功!
新建一个实体类 User.java
package com.mx;
/**
* @author peter
*/
public class User {
private String id;
private String userName;
public User() {
}
public User(String id, String userName) {
this.id = id;
this.userName = userName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"id='" + id + ''' +
", userName='" + userName + ''' +
'}';
}
}
新建 JavaConfig.java (使用注解的方式声明 bean)
package com.mx;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author wnb
*/
@Configuration
@ComponentScan
public class JavaConfig {
@Bean
public User user(){
return new User("001","smart 哥");
}
}
最后写一个测试类 Test.java:
package com.mx;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author wnb
*/
public class Test {
public static void main(String[] args) {
System.out.println("hah");
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(JavaConfig.class);
User user = (User)context.getBean("user");
System.out.println(user.toString());
}
}
运行,console 输出
hah
User{id='001', userName='smart 哥'}
成功!撒花!!
***六、问题及解决方法
1、CoroutinesUtils 找不到该类
错误信息:
Error:(354, 51) java: 找不到符号
符号: 变量 CoroutinesUtils
位置: 类 org.springframework.core.ReactiveAdapterRegistry.CoroutinesRegistrar
解决方案:
点击【File】 ->【Project Structure】 -> 【Libraries】 -> 【+】 -> 【Java】,然后选择 spring-
framework/spring-core/kotlin-coroutines/build/libs/kotlin-coroutines-5.3.0-SNAPSHOT.jar,在弹出的对话框中选择 spring-core.main,再重新 build 项目即可。
2、InstrumentationSavingAgent 找不到该类
错误信息:
Error:(26, 38) java: 找不到符号
符号: 类 InstrumentationSavingAgent
位置: 程序包 org.springframework.instrument
解决方案:
修改 spring-context 模块下的 spring-context.gradle 文件,找到 optional(project(":spring-instrument")),将 optional 改为 compile
//optional 改为 compile,否则报错:找不到 InstrumentationSavingAgent
//optional(project(":spring-instrument"))
compile(project(":spring-instrument"))
3、executesHugeScriptInReasonableTime() FAILED
错误信息:
H2DatabasePopulatorTests > executesHugeScriptInReasonableTime() FAILED
解决方案:
修改 spring-jdbc 模块下的 spring-jdbc.gradle 文件,找到 optional("com.h2database:h2"),将 optional 改成 compile
//报错:H2DatabasePopulatorTests > executesHugeScriptInReasonableTime() FAILED
//解决方案:将 optional 换成 compile
//optional("com.h2database:h2")
compile("com.h2database:h2")
4、header.mismatch [SpringHeader]
该错误是修改完以上 3 个问题后重新对整个工程进行重新编译时报的错,这个错是我们新建的 spring-mytest 模块报的错。
这个错误其实无关紧要,是格式错误,所以可以忽略,不要管它,也用不着重新编译,我们可以直接执 Test.java 中的 main 方法。如果某些小伙伴有强迫症,非要全部编译成功才肯罢休,那么可以尝试修改全局配置文件,在编译的时候把 spring-mytest 模块剔除在外。
七、附:spring 源代码各个模块作用
主要模块:
spring-core:核心模块 依赖注入 IOC 和 DI 的最基本实现
spring-beans:Bean 工厂与装配
spring-context:上下文,即 IOC 容器
spring-context-support:对 IOC 的扩展,以及 IOC 子容器
spring-context-indexer:类管理组件和 Classpath 扫描
spring-expression:表达式语句
切面编程:
spring-aop:面向切面编程,CGLIB,JDKProxy
spring-aspects:集成 AspectJ,Aop 应用框架
spring-instrument:动态 Class Loading 模块
数据访问与集成:
spring-jdbc:提供 JDBC 主要实现模块,用于简化 JDBC 操作
spring-tx:spring-jdbc 事务管理
spring-orm:主要集成 Hibernate,jpa,jdo 等
spring-oxm:将 java 对象映射成 xml 数据或将 xml 映射为 java 对象
spring-jms:发送和接受消息
web 组件:
spring-web:提供了最基础的 web 支持,主要建立在核心容器上
spring-webmvc:实现了 spring mvc 的 web 应用
spring-websocket:主要与前端页的全双工通讯协议
spring-webflux:一个新的非阻塞函数式 Reactive Web 框架
报文:
spring-messaging:4.0 加入的模块,主要集成基础报文传送应用
测试:
spring-test:测试组件
集成兼容:
spring-framework-bom:解决不同模块依赖版本不同问题