02. spring源码编译

Posted java技术探秘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02. spring源码编译相关的知识,希望对你有一定的参考价值。

本文代码的gitee仓库链接:https://gitee.com/funcy/spring-framework.

获取spring源码[1]一文中,我们成功地将spring源码导入了gitee仓库中,并且基于v5.2.2.RELEASE创建了分支v5.2.2.RELEASE_learn,接下来我们就基于该分支对spring源码进行编译。

为何要编译spring源码?

我们编译spring源码,主要是为了调试。虽说jar包的形式也可调试,但是如果我们边在源码上做注释,边调试,甚至修改源码实现我们想要的功能,岂不美哉?

1. 在命令行中编译

关于如何编译spring源码,spring项目早有说明,如果你想在命令行中构建,可以参考Build-from-Source[2],这里我将我的编译过程记录如下:

1.1 获取源码

$ git clone https://gitee.com/xxx/spring-framework
cd spring-framework

1.2 配置gradle

gradle构建时,会自动下载对应的gradle版本,所以我们可以不必额外下载gradle。但由于国内网络下载gradle的jar实在太慢,因此我们需要给gradle加个速,相关配置如下:

在个人home目录下,创建.gradle文件夹,然后创建init.gradle文件:

cd ~
$ mkdir .gradle
cd .gradle
$ touch init.gradle

在init.gradle中添加如下内容:

allprojects {
   repositories {
       maven {
           url "https://maven.aliyun.com/repository/public"
       }
       maven {
           url "https://maven.aliyun.com/repository/jcenter"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/gradle-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/google"
       }
       maven {
           url "https://maven.aliyun.com/repository/grails-core"
       }
       maven {
           url "https://maven.aliyun.com/repository/apache-snapshots"
       }
   }
}

gradle配置就完成了。

1.3 运行构建命令

在spring-framework目录下,运行构建命令:

./gradlew build

接下来就是漫长的等待了。第一次构建,下载的依赖会比较多,构建时长完全取决于你的网速了。

2. 在idea中编译

上面介绍了如何在命令行中构建spring源码,但一般来说,这样直接构建的人并不多,绝大多数人都是在开发工具中构建,毕竟构建完成后可以用来运行自己的测试代码,多好!由于本人是用idea开发,因此这里只介绍如何在idea中编译spring源码。

关于如何在idea,spring可以参考import-into-idea[3],这里给出我的编译过程:

2.1 配置gradle

与命令行编译相同,idea在构建时,会自动下载对应的gradle版本,所以我们可以不必额外下载gradle。但由于国内网络下载gradle的jar实现太慢,因此我们需要给gradle加个速,相关配置如下:

在个人home目录下,创建.gradle文件夹,然后创建init.gradle文件:

cd ~
$ mkdir .gradle
cd .gradle
$ touch init.gradle

在init.gradle中添加如下内容:

allprojects {
   repositories {
       maven {
           url "https://maven.aliyun.com/repository/public"
       }
       maven {
           url "https://maven.aliyun.com/repository/jcenter"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/gradle-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/google"
       }
       maven {
           url "https://maven.aliyun.com/repository/grails-core"
       }
       maven {
           url "https://maven.aliyun.com/repository/apache-snapshots"
       }
   }
}

2.2 配置idea中的gradle

使用idea编译时,使用的是idea中的gradle,因此我们也需要配置下idea中的gradle,本人的配置如下:

gradle user home默认是/home/你的用户名/.gradle,如果修改了此目录,那么需要在新目录下添加一份init.gradle文件

2.3 使用idea编译

首先获取spring代码,除了可以在命令行中使用命令git clone外,也可以在idea中导入,方式如下:02. spring源码编译

02. spring源码编译

当代码下载完成后,idea会自动进行编译操作。当然,我们也可手动运行idea的编译,方法如下:

02. spring源码编译

点击build后,耐心等待即可。

还是那句话,初次构建,下载的依赖会很多,构建的时长取决于你的网速。

3. 其他

3.1 spring 5.2.2.RELEASE使用的gradle版本

查看spring使用的gradle版本,可以在spring项目下的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

distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-bin.zip可以知道,使用的是gradle版本为5.6.4

3.2 gradle版本过高导致编译出错

如果本地装了gradle且直接使用命令gradle build(注意不是./gradlew build)构建,可以会报 如下错误:

$ gradle build
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

FAILURE: Build failed with an exception.

* Where:
Build file '/data/git_repository/spring-framework/build.gradle' line: 15

* What went wrong:
An exception occurred applying plugin request [id: 'com.gradle.build-scan', version: '2.4.2']
> Failed to apply plugin 'com.gradle.build-scan'.
   > The build scan plugin is not compatible with this version of Gradle.
     Please see https://gradle.com/help/gradle-6-build-scan-plugin for more information.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 9s

原因是gradle版本太高了,参考官网[4]说明。

02. spring源码编译

解决方法也简单,可以降低gradle版本,要么升级插件版本,或者使用命令./gradlew build

4. 失败的尝试:使用maven本地仓库

mavengradle共存的情况下,有个尴尬的问题:同一个jar包,使用gradle构建时,会下载一份到gradle本地仓库中,使用maven构建时,也会下载一份到maven本的仓库。那么,有没有可能,让gradle使用maven本地仓库,从而同样一个jar包,在本地只保留一份jar包?对照着网上的文章,本人作了多次尝试,但都以失败告终,以下是本人的尝试过程。

4.1 添加环境变量GRADLE_USER_HOME

在环境变量中添加如下内容:

export GRADLE_USER_HOME=你的maven本地仓库地址

注意:这个配置仅对命令行构建生效,如果使用idea构建,还需要进行另外配置:

4.2 在$GRADLE_USER_HOME目录下添加init.gradle文件

allprojects {
   repositories {
       maven {
           url "https://maven.aliyun.com/repository/public"
       }
       maven {
           url "https://maven.aliyun.com/repository/jcenter"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring"
       }
       maven {
           url "https://maven.aliyun.com/repository/spring-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/gradle-plugin"
       }
       maven {
           url "https://maven.aliyun.com/repository/google"
       }
       maven {
           url "https://maven.aliyun.com/repository/grails-core"
       }
       maven {
           url "https://maven.aliyun.com/repository/apache-snapshots"
       }
   }
}

4.3 在gradle使用本地仓库

spring-frameworkbuild.gradle中进行maven本地仓库配置,具体为:

configure(allprojects) { project ->
     apply plugin: "io.spring.dependency-management"

     dependencyManagement {
        ...
        repositories {
            // 大约在294行,添加 mavenLocal(),表示使用maven本地仓库
            mavenLocal()
            mavenCentral()
            maven { url "https://repo.spring.io/libs-spring-framework-build" }
        }
        ...
     }
     ...
}

4.4 进行编译,结果并没有达到预期

经过以上配置后,进行源码编译,发现会在maven的本地仓库下生成两个目录:

  1. wrapper内容如下:
wrapper
└── dists
    └── gradle-5.6.4-bin
        └── bxirm19lnfz6nurbatndyydux
                   ├── gradle-5.6.4
                   ├── gradle-5.6.4-bin.zip
                   ├── gradle-5.6.4-bin.zip.lck
                   └── gradle-5.6.4-bin.zip.ok

从内容可以知道,该目录用来存放编译过程中下载的gradle版本。

  1. caches内容如下:
caches
├── 5.6.4
├── 6.6
├── build-cache-1
├── journal-1
├── modules-2
├── transforms-2
├── user-id.txt
└── user-id.txt.lock

该目录用来存放gradle构建过程中产生的缓存文件,这里我们主要关注modules-2这个目录。稍微了解gradle的用户就知道,caches/modules-2/files-2.1正是gradle用来存放jar包的地方,部分内容如下:

├── antlr
├── aopalliance
├── cglib
├── ch.qos.logback
├── com.beust
├── com.caucho
├── com.fasterxml
├── com.fasterxml.jackson
...

可以看到,jar包还是下载了,并没有使用maven本地仓库的jar包,以org.slf4j:slf4j-api为例,maven本地仓库:

$ tree ~/.m2/repository/org/slf4j/slf4j-api/
/root/.m2/repository/org/slf4j/slf4j-api/
├── 1.7.25
│   ├── _remote.repositories
│   ├── slf4j-api-1.7.25.jar
│   ├── slf4j-api-1.7.25.jar.lastUpdated
│   ├── slf4j-api-1.7.25.jar.sha1
│   ├── slf4j-api-1.7.25.pom
│   └── slf4j-api-1.7.25.pom.sha1
└── 1.7.29
    ├── _remote.repositories
    ├── slf4j-api-1.7.29.jar
    ├── slf4j-api-1.7.29.jar.sha1
    ├── slf4j-api-1.7.29.pom
    └── slf4j-api-1.7.29.pom.sha1

gradle本地仓库:

$ tree ~/.m2/repository/caches/modules-2/files-2.1/org.slf4j/slf4j-api/
~/.m2/repository/caches/modules-2/files-2.1/org.slf4j/slf4j-api/
├── 1.7.26
│   ├── 4d3419a58d77c07f49185aaa556a787d50508d27
│   │   └── slf4j-api-1.7.26.pom
│   └── 77100a62c2e6f04b53977b9f541044d7d722693d
│       └── slf4j-api-1.7.26.jar
└── 1.7.29
    ├── e56bf4473a4c6b71c7dd397a833dce86d1993d9d
    │   └── slf4j-api-1.7.29.jar
    └── f6bb48229ddfeb20486473b19b13fd9b72bbdb23
        └── slf4j-api-1.7.29.pom

可以看到,相同的jar包slf4j-api-1.7.29.jar,maven与gradle仓库各有一份,gradle并没有使用maven的本地仓库。

为了配置让两者使用同一仓库,本人在网上阅读了大量博客,配置方式大同小异,其中发现一篇博客遇到了与我同样的问题:Gradle 使用Maven的本地仓库(坑)[5],将博客中关于失败的内容摘录如下:

文中说,使用maven本地仓库的意思是,当发现maven本地仓库有该jar包,就从本地仓库复制到自己的仓库中,并不是复用maven的本地仓库。这与我尝试的结果大体一致。如果有小伙伴成功让gradle复用了maven的本地仓库,麻烦在留言指导下,在这里先谢过了。

5. 总结

本文介绍了如何编译spring源码,分别讲述了命令行和idea中如何编译,首次编译耗时会比较久,需要耐心等待下,问题不大。

源码编译完成了,接下来正式开启spring的源码分析之路!


本文原文链接:https://my.oschina.net/funcy/blog/4527453 ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。

本系列的其他文章

【spring源码分析】spring源码分析系列目录

参考资料

[1]

获取spring源码: https://my.oschina.net/funcy/blog/4523714

[2]

Build-from-Source: https://github.com/spring-projects/spring-framework/wiki/Build-from-Source

[3]

import-into-idea: https://github.com/spring-projects/spring-framework/blob/master/import-into-idea.md

[4]

这里: https://docs.gradle.com/enterprise/compatibility/#build_scan_plugin

[5]

Gradle 使用Maven的本地仓库(坑): http://www.jeepxie.net/article/563412.html


以上是关于02. spring源码编译的主要内容,如果未能解决你的问题,请参考以下文章

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

spring源码环境编译问题

Spring4源码编译完成后导入eclipse报错求助

听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译

java,gradle编译spring源码出错

Spring boot:thymeleaf 没有正确渲染片段