天知道我今天下午经历了什么!从去年开始断断续续看源码,在 IDEA 中 Spring 源码只读不能写,所以每次都是将方法拷贝到 vscode 中。IDEA 中看代码逻辑,接着在 vscode 对应的方法上写注释。期初这种方式没觉得有什么不便,但是看得越来越多,代码的调用也越来越复杂,这种“复制”的方式就有问题了,代码层次没有,就是保存也很不方便。那最好的方式是直接在源码上写注释,用 git 工具做版本控制,So 就有了编译源码的想法。

理想很丰满,现实很骨感!一个掉头发的下午,还是在劳动节。搞定后觉得就是几个细节没有理清楚,写篇博客记录下,后面的人少采点坑。

1. 准备工作

我的配置是:Spring 5.2.14,Gradle 5.6.4,JDK 8,IDEA 2021。

默认 JDK 8 已经安装了。

1.1 下载 Spring 5.2.14 源码

git clone https://github.com/spring-projects/spring-framework.git
cd spring-framework
git reset --hard 4ceacfa70c37596c46147a5c2b56f07e470643d8

1.2 下载 Gradle 5.6.4

这个不是手动下载的,在命令行工具中执行编译命令时,会主动去本地目录 /.gradle/wrapper/dists/ 找有没有 5.6.4 这个版本,如果没有就下载。这里怎么确定要使用 5.6.4 版本呢?

cat spring-framework/gradle/wrapper/gradle-wrapper.properties

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

gradle-wrapper.properties 文件中写了编译源码要求的 gradle 版本。

2. 修改几个配置

这里修改配置只是为了让编译能快一些,因为编译时会下载依赖,墙的缘故,将依赖源改成国内阿里源会快很多。

build.gradle

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" }
}

grade.properties

# 这个版本指的是 spring 源码的版本,不是 gradle 的,别瞎改。
version=5.2.14.RELEASE
org.gradle.jvmargs=-Xmx2048M
## 开启 Gradle 缓存
org.gradle.caching=true
## 开启并行编译
org.gradle.parallel=true
## 启用新的孵化模式
org.gradle.configureondemand=true
## 开启守护进程 通过开启守护进程,下一次构建的时候,将会连接这个守护进程进行构建,而不是重新fork一个gradle构建进程
org.gradle.daemon=true

settings.gradle

pluginManagement {
	repositories {
        // 加这个
		maven { url "https://maven.aliyun.com/repository/public" }
    gradlePluginPortal()
		maven { url 'https://repo.spring.io/plugins-release' }
	}
}

2. 编译

源码根目录下的 import-into-idea.md 文件中已经写了。

1. Precompile `spring-oxm` with `./gradlew :spring-oxm:compileTestJava`
2. Import into IntelliJ (File -> New -> Project from Existing Sources -> Navigate to directory -> Select build.gradle)
3. When prompted exclude the `spring-aspects` module (or after the import via File-> Project Structure -> Modules)
4. Code away
# -------------
1. 在源码根目录下执行命令:./gradlew :spring-oxm:compileTestJava。
2. 按照箭头的顺序,一直定位到源码的 build.gradle 文件打开。
3. 源码里没有 `spring-aspects` module 模块。
4. 使用代码。

在源码根目录下执行 ./gradlew :spring-oxm:compileTestJava 执行编译。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQOb9Hep-1620054758234)(https://raw.githubusercontent.com/diego1109/pictures/main/20210503214830.png)]

本地没有,先下载 Gradle 5.6.4,再编译源码。这里是可以直接编译成功的,有些博客中写着导入到 IDEA 中再编译,我觉得那样就更麻烦,因为还得自己配使用那个 gradle,好些时候是导入了代码,忘了该配置,出现了不兼容还不知道问题在哪。(这个后面再说)

接下来就是打开 IDEA 导入 build.gradle 文件了,导入后等着,不需要任何手动配置 IDEA 自己执行 sync ,完毕后就能直接使用了。

3. 测试

IDEA 说 “SUCCESSFUL” ,我们还得自己测试一下到底能不能用。

3.1 创建一个新的 Module

image-20210503220406536 image-20210503220310539 image-20210503220310539

创建完成后的测试项目目录:

# tree test-spring-source -L 3
test-spring-source
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   └── resources
    ├── test
    │   ├── java
    │   └── resources
    └── testFixtures
        ├── java
        └── resources

3.2 写测试逻辑

修改 build.gradle

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    // 添加依赖
    implementation(project(":spring-context"))
}
image-20210503222348368

Main.java

package com;

import com.config.AppConfig;
import com.domain.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
  public static void main(String[] args){
    // 创建 context ,并向其注册一个 User bean。
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    // 将 User bean 拿出来。
    User user =  context.getBean(User.class);
    // 查看内容。
    System.out.println("name: "+ user.getName());
    System.out.println("age: "+ user.getAge());
  }
}

AppConfig.java

package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.domain")
public class AppConfig {
}

User.java

package com.domain;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {

  @Value("diego")
  private String name;
  @Value("100")
  private int age;

  public String getName() {
    return name;
  }

  public int getAge() {
    return age;
  }
}

最终目录结构:

test-spring-source
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       ├── Main.java
    │   │       ├── config
    │   │       │   └── AppConfig.java
    │   │       └── domain
    │   │           └── User.java
    │   └── resources
    ├── test
    │   ├── java
    │   └── resources
    └── testFixtures
        ├── java
        └── resources

运行结果:

> Task :test-spring-source-code:Main.main()
name: diego
age: 100

4. 踩的坑

**兼容问题:**Spring 版本,Gradle 版本,JDK 版本,编译失败都得归功于这三个不兼容。先确定 JDK 的版本,再去找 JDK 能支持的 spring 版本 和 gradle 版本。

spring:https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions。

gradle:https://docs.gradle.org/current/userguide/compatibility.html。

你编译时用的是哪个版本的 Gradle?

这先得回答一台电脑上怎么会出现多个 gradle 版本的?

image-20210503223814241

这三条命令应该能说明问题了。首先电脑上肯定装了 gradle 这先是一个版本,此电脑上装的是 6.5.1。但是 gradle 是不断更新的工具,它会有版本迭代,而不同的 Spring 项目可能会用到不同版本的 gradle。这时候怎么办?升级或者降级 6.5.1,显然不靠谱。采用的方式是 当 spring 项目启动时,先检查其所要求的版本在本地有没有,如果没有,那先下载该版本,并保存到本地的 ~/.gradle/wrapper/dists 目录下,这就解释了为什么一台电脑上会出现多个 gradle 版本。

如果本地有多个版本,spring 项目编译时用哪个?

至于使用哪个,在 spring 项目的 gradle/wrapper/gradle-wrapper.properties 定义了,可以翻上去看看。

使用命令行工具,这里没什么大碍。

但如果使用 IDEA编译,坑货来了。使用 IDEA 编译失败,多半这里有鬼。原因是 IDEA 使用哪个版本编译,是要手动配置的。如果默认的正好符合,那完事大吉,如果不符合,开发者有忘了这里,那坑死人不偿命。

image-20210503225907400quan

圈住的地方是用来设置:本项目到底使用哪个 gradle。

gradle-wrapper.properties : IDEA 推荐的方式,gradle 来源于项目的 gradle-wrapper.properties 中定义的版本。

wrapper task in Gradle build script:task 中定义的,如果开发者喜欢控制 gradle 版本,可以使用这种方式。

Specified location:开发者自己指定一个版本的 gradle 所在的目录。

gradle-setting更详细的信息。

最后想写的是:猜到坑了要淡定、要坚持。可以晚一点吃饭,可以晚一会上厕所,因为屁股离开凳子,等再坐上来就没了之前的感觉了。别拖。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐