Spring 5.3.10 源码编译

前言

如果想要探究 Spring 源码,那么在本地编译一份 Spring 的源码是必不可少的,这利于我们深入理解框架、方便调试和定位问题,也给予我们定制和扩展的能力。但是编译 Spring 源码并非像编译我们自己的项目那么简单,其中可能会遇到不少的问题,笔者如是,于是写下这一篇教程记录下编译安装的过程和问题。

环境准备

JDK:9 以上版本(本文示例版本为 JDK11)

网络:能正常访问 Github

安装 JDK11

因为 Spring 5.3.10 使用到了 jdk9 以后才有的 jdk.jfr 模块,所以我们需要在电脑上安装 9 以上版本的 jdk,这里以 JDK11 为例。

JDK11 下载地址:Java Archive Downloads - Java SE 11

可以下载压缩包的版本:

jdk11-download

解压后,在系统环境变量配置下 JAVA_HOME、CLASSPATH 和 PATH,这些大家应该都比较熟悉,就不在这里赘述。

下载源码

官方 github 仓库:https://github.com/spring-projects/spring-framework

git 地址:https://github.com/spring-projects/spring-framework.git

# 克隆官方仓库
git clone https://github.com/spring-projects/spring-framework.git
​​​​​​
# 切换目录
cd spring-framework

# 切换版本对应的 tag
git checkout v5.3.10

下载后会得到以下的目录,此时先不要急着导入到 IDEA,需要先修改一些配置和预编译两个模块,以确保能够正常完成编译。

repository-file-list

源码文件预处理

下载 Gradle

用文本编辑器打开源码根目录下的 /gradle/wrapper/gradle-wrapper.properties 文件,可以看到如下内容:

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

其中 distributionUrl 是 gradle 的路径,这里默认配置的是一个下载链接,默认会从这个地址下载,我们可以提前下载好,然后修改为自己本地的文件路径,例如:

其中 D:/develop/gradle-7.2/gradle-7.2-bin.zip 就是我本地 gradle 压缩包的路径

distributionUrl=file\:///D:/develop/gradle-7.2/gradle-7.2-bin.zip

更换 Gradle 镜像源

与 maven 类似,gradle 也需要我们配置下国内镜像源,否则下载起来会很慢。

打开根目录下的 build.gradle,搜索 url 关键词,在如图所示位置添加几行镜像配置:

maven { url "https://maven.aliyun.com/repository/public" }
maven { url "https://maven.aliyun.com/repository/central" }

config-build-gradle

同理配置下根目录下的 settings.gradle

maven { url "https://maven.aliyun.com/repository/gradle-plugin" }
maven { url "https://maven.aliyun.com/repository/public" }
maven { url "https://maven.aliyun.com/repository/central" }

config-settings-gradle

预编译处理

源码根目录下有一份 import-into-idea.md 文件,告诉了我们导入 idea 之前需要先预编译 spring-oxm 这个模块:

import-into-idea-precompile

那么我们在根目录下打开命令行窗口,输入以下命令执行预编译:

  • Mac / Linux 执行以下命令:

./gradlew :spring-oxm:compileTestJava
  • Windows 用户执行以下命令:

gradlew.bat :spring-oxm:compileTestJava

等待一段时间,当出现 SUCCESSFUL 字样的时候,表示预编译成功。

接着,我们最好也预编译下 spring-core 这个模块,虽然官方没有提及,但这个模块是核心模块,所以最好也提前预编译下:

  • Mac / Linux:

./gradlew :spring-core:compileTestJava
  • Windows:

gradlew.bat :spring-core:compileTestJava

导入 IDEA 进行编译

IDEA 打开 build.gradle 文件

点击 File - New - Project from Existing Sources...

idea-open-file-menu

选择源码根目录下的 build.gradle 文件

idea-select-build-gradle-file

接着就进行漫长的下载依赖的过程

idea-download-dependencies

创建一个测试项目

等依赖下载完毕且模块正常加载后,我们的目录应该会有很多小蓝点,表示 idea 已将其加载为模块:

directories-after-download

我们可以创建一个自己的模块,用来测试运行项目,在这之前,我们需要先修改一下 idea 的编译方式:

打开 Settings - Build, Execution, Deployment - Build Tools - Gradle,将编译方式都改为 IntelliJ IDEA

idea-change-complie-mode

此时我们就可以开始创建自己的模块了,在根目录下右键 New - Module

new-module-menu

接着填写项目的名称等信息,然后点击 Create

new-module-info

创建完毕后,会得到以下目录,其中 build.gradle 就类似于 maven 中的 pom.xml 文件,用来管理我们的依赖。

new-module-directories

打开 build.gradle,添加一些依赖,然后 reload 一下:

implementation(project(":spring-context"))
implementation(project(":spring-instrument"))
implementation("javax.annotation:javax.annotation-api:1.3.2")

new-module-add-dependencies

然后我们就可以开始愉快地编写代码了,比方说可以写几行简单的代码来测试 Spring 容器:

new-module-test-code

运行 Main 方法,输出以下信息,表示 Spring 容器正常加载,测试成功~

new-module-run-success

常见问题

java: 程序包 jdk.jfr 不存在

problem-jdk-jfr

解决办法:检查 IDEA 配置的 SDK 版本 和 编译版本 是否正确

① 检查 Project Structure

project-structure-1

project-structure-2

② 检查编译字节码版本

java-compiler-bytecode-version

java: 找不到类 InstrumentationSavingAgent

添加 spring-instrument 依赖即可:

implementation(project(":spring-instrument"))

add-spring-instrument-dependency

IDEA: 每次编译项目总会自动重置 JDK 版本为 1.8

这个问题是由于 gradle 目录下有两个文件配置了默认的 JDK 版本为 1.8,我们需要将这两个配置文件中涉及到 1.8 的内容修改为 11。

gradle/ide.gradle

只需要修改这个地方,将 1.8 或 8 改为 11 或自己的 JDK 版本即可

eclipse.jdt {
    sourceCompatibility = 11
    targetCompatibility = 11
}

gradle/toolchains.gradle

因为这个文件涉及到要修改的地方较多,所以这里我直接贴了完整内容,如果有需要直接复制全部内容然后替换即可,如果你是其他的 JDK 版本,可以搜索并替换文件中的 11 为自己的版本

/**
 * Apply the JVM Toolchain conventions
 * See https://docs.gradle.org/current/userguide/toolchains.html
 *
 * One can choose the toolchain to use for compiling the MAIN sources and/or compiling
 * and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
 * when available.
 * {@code "./gradlew check -PmainToolchain=8 -PtestToolchain=11"} will use:
 * <ul>
 *     <li>a JDK8 toolchain for compiling the main SourceSet
 *     <li>a JDK11 toolchain for compiling and running the test SourceSet
 * </ul>
 *
 * By default, the build will fall back to using the current JDK and 11 language level for all sourceSets.
 *
 * Gradle will automatically detect JDK distributions in well-known locations.
 * The following command will list the detected JDKs on the host.
 * {@code
 * $ ./gradlew -q javaToolchains
 * }
 *
 * We can also configure ENV variables and let Gradle know about them:
 * {@code
 * $ echo JDK11
 * /opt/openjdk/java11
 * $ echo JDK15
 * /opt/openjdk/java15
 * $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 check
 * }
 *
 * @author Brian Clozel
 * @author Sam Brannen
 */

def mainToolchainConfigured() {
	return project.hasProperty('mainToolchain') && project.mainToolchain
}

def testToolchainConfigured() {
	return project.hasProperty('testToolchain') && project.testToolchain
}

def mainToolchainLanguageVersion() {
	if (mainToolchainConfigured()) {
		return JavaLanguageVersion.of(project.mainToolchain.toString())
	}
	return JavaLanguageVersion.of(11)
}

def testToolchainLanguageVersion() {
	if (testToolchainConfigured()) {
		return JavaLanguageVersion.of(project.testToolchain.toString())
	}
	return mainToolchainLanguageVersion()
}

plugins.withType(JavaPlugin) {
	// Configure the Java Toolchain if the 'mainToolchain' is configured
	if (mainToolchainConfigured()) {
		java {
			toolchain {
				languageVersion = mainToolchainLanguageVersion()
			}
		}
	}
	else {
		// Fallback to JDK11
		java {
			sourceCompatibility = JavaVersion.VERSION_11
		}
	}
	// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
	if (testToolchainConfigured()) {
		def testLanguageVersion = testToolchainLanguageVersion()
		tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
			javaCompiler = javaToolchains.compilerFor {
				languageVersion = testLanguageVersion
			}
		}
		tasks.withType(Test).configureEach{
			javaLauncher = javaToolchains.launcherFor {
				languageVersion = testLanguageVersion
			}
		}
	}
}

plugins.withType(GroovyPlugin) {
	// Fallback to JDK11
	if (!mainToolchainConfigured()) {
		compileGroovy {
			sourceCompatibility = JavaVersion.VERSION_11
		}
	}
}

pluginManager.withPlugin("kotlin") {
	// Configure the Kotlin compiler if the 'mainToolchain' property is defined
	if (mainToolchainConfigured()) {
		def mainLanguageVersion = mainToolchainLanguageVersion()
		def compiler = javaToolchains.compilerFor {
			languageVersion = mainLanguageVersion
		}
		// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
		def javaVersion = mainLanguageVersion.toString() == '11' ? '11' : mainLanguageVersion.toString()
		compileKotlin {
			kotlinOptions {
				jvmTarget = javaVersion
				jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
			}
		}
		// Compile the test classes with the same version, 'testToolchain' will override if defined
		compileTestKotlin {
			kotlinOptions {
				jvmTarget = javaVersion
				jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
			}
		}
	}
	else {
		// Fallback to JDK11
		compileKotlin {
			kotlinOptions {
				jvmTarget = '11'
			}
		}
		compileTestKotlin {
			kotlinOptions {
				jvmTarget = '11'
			}
		}
	}

	if (testToolchainConfigured()) {
		def testLanguageVersion = testToolchainLanguageVersion()
		def compiler = javaToolchains.compilerFor {
			languageVersion = testLanguageVersion
		}
		// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
		def javaVersion = testLanguageVersion.toString() == '11' ? '11' : testLanguageVersion.toString()
		compileTestKotlin {
			kotlinOptions {
				jvmTarget = javaVersion
				jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
			}
		}
	}
}

// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
pluginManager.withPlugin("me.champeau.jmh") {
	if (mainToolchainConfigured() || testToolchainConfigured()) {
		tasks.matching { it.name.contains('jmh') && it.hasProperty('javaLauncher') }.configureEach {
			javaLauncher.set(javaToolchains.launcherFor {
				languageVersion.set(testToolchainLanguageVersion())
			})
		}
		tasks.withType(JavaCompile).matching { it.name.contains("Jmh") }.configureEach {
			javaCompiler = javaToolchains.compilerFor {
				languageVersion = testToolchainLanguageVersion()
			}
		}
	}
}

// Store resolved Toolchain JVM information as custom values in the build scan.
rootProject.ext {
	resolvedMainToolchain = false
	resolvedTestToolchain = false
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
	if (mainToolchainConfigured() && !resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) {
		def metadata = task.javaCompiler.get().metadata
		task.project.buildScan.value('Main toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
		resolvedMainToolchain = true
	}
	if (testToolchainConfigured() && !resolvedTestToolchain && task instanceof Test && task.javaLauncher.isPresent()) {
		def metadata = task.javaLauncher.get().metadata
		task.project.buildScan.value('Test toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
		resolvedTestToolchain = true
	}
}

参考资料

Spring5.3.10源码编译

听说你还没学Spring就被源码编译劝退了?手把手带你编译Spring框架源码,让你的学习事半功倍

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
spring-beans-5.3.10.release.jar是Spring框架的一个核心模块之一。Spring框架是一个开源的Java应用程序开发框架,用于简化企业级应用程序的开发。spring-beans模块提供了容器和配置文件等机制,用于管理和组织JavaBean对象。 在Spring框架中,JavaBean是一种符合特定规范的类,它用于封装数据和提供业务逻辑。Spring框架的思想是通过将应用程序的配置信息和业务逻辑与具体实现解耦,从而提高应用程序的可维护性和可测试性。 spring-beans模块的主要功能包括: 1. 容器:spring-beans模块提供了一个容器,用于管理和组织JavaBean对象。容器负责创建、销毁和管理对象的生命周期,并提供依赖注入等功能,使得对象之间的协作更加灵活和可配置。 2. 配置文件:spring-beans模块支持使用XML、注解或Java代码等方式来配置应用程序的各个组件,包括JavaBean的定义、依赖关系的声明以及其他配置信息。配置文件使得应用程序的配置更加直观和可维护。 3. 依赖注入:spring-beans模块通过依赖注入的方式,将对象之间的依赖关系从代码中分离出来,并由容器进行管理。这使得对象之间的耦合度降低,同时也提高了代码的可测试性和可重用性。 4. AOP(面向切面编程):spring-beans模块支持AOP,可以通过配置文件或注解的方式来声明横切逻辑,并将其应用到目标对象的方法上。AOP可以实现事务管理、日志记录、性能监控等功能,提高应用程序的扩展性和可维护性。 5. 事件机制:spring-beans模块提供了一个事件机制,用于在对象之间传递消息。应用程序中的某个事件发生时,容器会将该事件通知给所有注册了对应监听器的对象。事件机制可以实现解耦和、松散耦合的软件设计。 总之,spring-beans-5.3.10.release.jar是Spring框架的一个核心模块,提供了容器、配置文件、依赖注入、AOP和事件机制等功能,用于简化企业级Java应用程序的开发。它的存在使得应用程序的开发更加简单、灵活和可维护。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值