Spring-frameWork-5.0.x源码编译踩坑
spring是java开发工程师避不过去的常用开发框架,能够提供方便的对象管理、低耦合面向切面、对上集成springMvc,Stucts,对下集成Mybatis,Hibernate框架,等等这些功能都对日常的开发帮助很大,但是想深入了解spring的原理、高级功能以及设计思想必须能够本地编译spring源码并将之运行起来,但是这一步基本都卡住了不少的人,当然,硬看代码的大佬除外哈。此文记录了本地编译spring源码的一些问题,最终将代码在本地跑起来。希望能给大家一点帮助。纯属娱乐,不喜勿碰。
代码下载
首先去GitHub上下载对应版本的spring代码,本文用到的是5.0.x,如下:
然后打开build.gradle文件,找到spring使用的gradle版本(spring-5.0.x使用的是gradle-4.4.1),为下一步安装gradle做准备,版本入下:
Gradle安装
首先下载对应版本gradle并安装,下载安装文件就不过多的做介绍,下载安装包解压即可,详情自行搜索;需要注意的是,安装过后需要配置环境变量、修改远程仓库源地址为国内镜像,同时配置本地仓库地址。
配置环境变量、配置本地仓库地址
需要在环境变量值配置GRADLE_HOME环境变量和CRADLE_USER_HOME来配置文档仓库路径。
修改镜像仓库为国内镜像
在gradle的安装目录的init.d目录下创建init.gradle文件,内容如下:
文件内容:
allprojects{
repositories {
def ALIYUN_REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public'
def ALIYUN_JCENTER_URL = 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
all { ArtifactRepository repo ->
if(repo instanceof MavenArtifactRepository){
def url = repo.url.toString()
if (url.startsWith('https://repo1.maven.org/maven2')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
remove repo
}
if (url.startsWith('https://jcenter.bintray.com/')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL."
remove repo
}
}
}
maven {
url ALIYUN_REPOSITORY_URL
url ALIYUN_JCENTER_URL
}
}
}
到此,gradle安装完成
项目导入
代码下载到本地之后,导入idea,步骤如下,选择import project 选择本地代码路径,然后
选择import project from extenal model 选择Gradle,如下图
点击下一步,具体配置如下图
完成配置之后等待代码下载依赖以及编译,此处耗费很长时间,耐心等待,一两个小时是有可能的。但是一般编译或者下载依赖有问题都是正常的不要惊慌,一个个解决,我遇到的问题记录如下。
问题解决
编译时长过长或者编译报javadoc.options的错误
如果编译报如下错误,或者编译时间过长,找到build.graldle文件,注释掉里面的archives sorcesJar 和 archivesjavadocJar,问题如下图:
解决方式如下图:
自己建的子项目编译报错
错误如下,排查发现依赖了不需要的spring-webflux,在对应项目的.gradle文件中注释掉即可
解决方案如下图:
项目编译成功运行报错
项目编译成功后运行报错找不到对应的类,排查发现包含这些源码的工程没有引入到相应子工程,添加依赖即可,问题如下
解决方式如下:
编译一直报javadoc相关的错误
找到build.gradle中的javadoc配置,直接配置忽略编译时候的javadoc异常,可以解决大部分问题,配置如下图。
项目字符集要与工程配置的一致
找到build.gradle中关于字符集的配置,如下图:
都是UTF-8,那么idea中对于项目字符集配置也要是UTF-8,不让编译一直包字符集错误,特别是自己看代码加了中文注释之后,配置如下图:
项目镜像仓库更换国内镜像
项目编译下载依赖的时候spring的原生镜像仓库在近段时间下载需要认证,所以有些jar包或者插件下载会包401认证错误,将build.gradle文件关于镜像仓库的配置改为国内镜像即可。一般有两处如下图所示:
buildscript {
repositories {
maven { url 'https://repo.spring.io/plugins-release' }
maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
maven { url "https://maven.aliyun.com/repository/spring" }
maven { url "https://maven.aliyun.com/repository/google" }
maven { url 'https://maven.aliyun.com/repository/central'}
maven { url "https://maven.aliyun.com/repository/public" }
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
dependencies {
classpath("io.spring.gradle:propdeps-plugin:0.0.8")
classpath("io.spring.gradle:docbook-reference-plugin:0.3.1")
classpath("org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16")
classpath("org.asciidoctor:asciidoctorj-epub3:1.5.0-alpha.7")
}
}
repositories {
// maven { url "https://repo.spring.io/libs-release" }
maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
maven { url "https://maven.aliyun.com/repository/spring" }
maven { url "https://maven.aliyun.com/repository/google" }
maven { url 'https://maven.aliyun.com/repository/central'}
maven { url "https://maven.aliyun.com/repository/public" }
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
最后附上自己搞了一两天搞成功的build.gradle全部文件
buildscript {
repositories {
maven { url 'https://repo.spring.io/plugins-release' }
maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
maven { url "https://maven.aliyun.com/repository/spring" }
maven { url "https://maven.aliyun.com/repository/google" }
maven { url 'https://maven.aliyun.com/repository/central'}
maven { url "https://maven.aliyun.com/repository/public" }
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
dependencies {
classpath("io.spring.gradle:propdeps-plugin:0.0.8")
classpath("io.spring.gradle:docbook-reference-plugin:0.3.1")
classpath("org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16")
classpath("org.asciidoctor:asciidoctorj-epub3:1.5.0-alpha.7")
}
}
// 3rd party plugin repositories can be configured in settings.gradle
plugins {
id "com.gradle.build-scan" version "1.8"
id "io.spring.dependency-management" version "1.0.3.RELEASE" apply false
id "org.jetbrains.kotlin.jvm" version "1.2.71" apply false
id "org.jetbrains.dokka" version "0.9.17"
id "org.asciidoctor.convert" version "1.5.6"
}
buildScan {
licenseAgreementUrl = "https://gradle.com/terms-of-service"
licenseAgree = "yes"
}
ext {
linkHomepage = "https://projects.spring.io/spring-framework"
linkCi = "https://build.spring.io/browse/SPR"
linkIssue = "https://jira.spring.io/browse/SPR"
linkScmUrl = "https://github.com/spring-projects/spring-framework"
linkScmConnection = "scm:git:git://github.com/spring-projects/spring-framework.git"
linkScmDevConnection = "scm:git:ssh://git@github.com:spring-projects/spring-framework.git"
moduleProjects = subprojects.findAll {
!it.name.equals("spring-build-src") && !it.name.equals("spring-framework-bom")
}
aspectjVersion = "1.8.13"
freemarkerVersion = "2.3.28"
groovyVersion = "2.4.16"
hsqldbVersion = "2.4.1"
jackson2Version = "2.9.8"
jettyVersion = "9.4.15.v20190215"
junitJupiterVersion = "5.0.3"
junitPlatformVersion = "1.0.3"
junitVintageVersion = "4.12.3"
kotlinVersion = "1.2.71"
log4jVersion = "2.11.2"
nettyVersion = "4.1.34.Final"
reactorVersion = "Bismuth-SR17"
rxjavaVersion = "1.3.8"
rxjavaAdapterVersion = "1.2.1"
rxjava2Version = "2.1.17"
slf4jVersion = "1.7.26" // spring-jcl + consistent 3rd party deps
tiles3Version = "3.0.8"
tomcatVersion = "8.5.39"
undertowVersion = "1.4.27.Final"
gradleScriptDir = "${rootProject.projectDir}/gradle"
withoutJclOverSlf4J = {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
}
configure(allprojects) { project ->
group = "org.springframework"
version = qualifyVersionIfNecessary(version)
apply plugin: "java"
apply plugin: "kotlin"
apply plugin: "propdeps"
apply plugin: "test-source-set-dependencies"
apply plugin: "io.spring.dependency-management"
apply from: "${gradleScriptDir}/ide.gradle"
dependencyManagement {
resolutionStrategy {
cacheChangingModulesFor 0, "seconds"
}
applyMavenExclusions = false
generatedPomCustomization {
enabled = false
}
}
configurations.all {
// Check for updates every build
resolutionStrategy.cacheChangingModulesFor 0, "seconds"
// Consistent slf4j version (e.g. clashes between slf4j versions)
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == "org.slf4j") {
details.useVersion slf4jVersion
}
}
}
def commonCompilerArgs =
["-Xlint:serial", "-Xlint:cast", "-Xlint:classfile", "-Xlint:dep-ann",
"-Xlint:divzero", "-Xlint:empty", "-Xlint:finally", "-Xlint:overrides",
"-Xlint:path", "-Xlint:processing", "-Xlint:static", "-Xlint:try", "-Xlint:-options"]
compileJava.options*.compilerArgs = commonCompilerArgs +
["-Xlint:varargs", "-Xlint:fallthrough", "-Xlint:rawtypes",
"-Xlint:deprecation", "-Xlint:unchecked", "-Werror"]
compileTestJava.options*.compilerArgs = commonCompilerArgs +
["-Xlint:-varargs", "-Xlint:-fallthrough", "-Xlint:-rawtypes",
"-Xlint:-deprecation", "-Xlint:-unchecked"]
compileJava {
sourceCompatibility = 1.8 // can be switched to 10 for testing
targetCompatibility = 1.8
options.encoding = "UTF-8"
// options.encoding = "GBK"
}
compileTestJava {
sourceCompatibility = 1.8 // can be switched to 10 for testing
targetCompatibility = 1.8
options.encoding = "UTF-8"
// options.encoding = "GBK"
options.compilerArgs += "-parameters"
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
apiVersion = "1.1"
languageVersion = "1.1"
// apiVersion = "1.2"
// languageVersion = "1.2"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
test {
systemProperty("java.awt.headless", "true")
systemProperty("testGroups", project.properties.get("testGroups"))
scanForTestClasses = false
include(["**/*Tests.class", "**/*Test.class"])
// Since we set scanForTestClasses to false, we need to filter out inner
// classes with the "$" pattern; otherwise, using -Dtest.single=MyTests to
// run MyTests by itself will fail if MyTests contains any inner classes.
exclude(["**/Abstract*.class", '**/*$*'])
reports.junitXml.setDestination(file("$buildDir/test-results"))
}
repositories {
// maven { url "https://repo.spring.io/libs-release" }
maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
maven { url "https://maven.aliyun.com/repository/spring" }
maven { url "https://maven.aliyun.com/repository/google" }
maven { url 'https://maven.aliyun.com/repository/central'}
maven { url "https://maven.aliyun.com/repository/public" }
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
dependencies {
testCompile("junit:junit:4.12") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
testCompile("org.mockito:mockito-core:2.19.1") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
testCompile("com.nhaarman:mockito-kotlin:1.6.0") {
exclude module: "kotlin-stdlib"
exclude module: "kotlin-reflect"
exclude module: "mockito-core"
}
testCompile("org.hamcrest:hamcrest-all:1.3")
testRuntime("org.apache.logging.log4j:log4j-core:${log4jVersion}")
testRuntime("org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}")
testRuntime("org.apache.logging.log4j:log4j-jul:${log4jVersion}")
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
testCompileOnly("com.google.code.findbugs:jsr305:3.0.2")
}
ext.javadocLinks = [
"https://docs.oracle.com/javase/8/docs/api/",
"https://docs.oracle.com/javaee/7/api/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"https://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/",
"https://glassfish.java.net/nonav/docs/v3/api/",
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"https://tiles.apache.org/tiles-request/apidocs/",
"https://tiles.apache.org/framework/apidocs/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://www.ehcache.org/apidocs/2.10.4/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.9/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.9/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.9/",
"https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/",
"https://junit.org/junit4/javadoc/4.12/",
"https://junit.org/junit5/docs/${junitJupiterVersion}/api/"
] as String[]
}
configure(subprojects - project(":spring-build-src")) { subproject ->
apply from: "${gradleScriptDir}/publish-maven.gradle"
jar {
manifest.attributes["Implementation-Title"] = subproject.name
manifest.attributes["Implementation-Version"] = subproject.version
manifest.attributes["Automatic-Module-Name"] = subproject.name.replace('-', '.') // for Jigsaw
manifest.attributes["Created-By"] =
"${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})"
from("${rootProject.projectDir}/src/docs/dist") {
include "license.txt"
include "notice.txt"
into "META-INF"
expand(copyright: new Date().format("yyyy"), version: project.version)
}
}
javadoc {
description = "Generates project-level javadoc for use in -javadoc jar"
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = project.name
options.use = true
options.links(project.ext.javadocLinks)
options.addStringOption("Xdoclint:none", "-quiet")
//忽略编译时候javadoc的异常
failOnError = false
options.encoding = "UTF-8"
// Suppress warnings due to cross-module @see and @link references.
// Note that global 'api' task does display all warnings.
logging.captureStandardError LogLevel.INFO
logging.captureStandardOutput LogLevel.INFO // suppress "## warnings" message
}
task sourcesJar(type: Jar, dependsOn: classes) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
classifier = "sources"
from sourceSets.main.allSource
// Don't include or exclude anything explicitly by default. See SPR-12085.
}
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
artifacts {
// archives sourcesJar
// archives javadocJar
}
}
configure(rootProject) {
description = "Spring Framework"
apply plugin: "groovy"
apply from: "${gradleScriptDir}/jdiff.gradle"
apply from: "${gradleScriptDir}/docs.gradle"
dependencyManagement {
imports {
mavenBom "io.projectreactor:reactor-bom:${reactorVersion}"
}
}
// Don't publish the default jar for the root project
configurations.archives.artifacts.clear()
dependencies { // for integration tests
testCompile(project(":spring-aop"))
testCompile(project(":spring-beans"))
testCompile(project(":spring-context"))
testCompile(project(":spring-core"))
testCompile(project(":spring-expression"))
testCompile(project(":spring-jdbc"))
testCompile(project(":spring-orm"))
testCompile(project(":spring-test"))
testCompile(project(":spring-tx"))
testCompile(project(":spring-web"))
testCompile("javax.inject:javax.inject:1")
testCompile("javax.resource:javax.resource-api:1.7")
testCompile("javax.servlet:javax.servlet-api:3.1.0")
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.hibernate:hibernate-core:5.1.17.Final")
}
artifacts {
// archives docsZip
// archives schemaZip
// archives distZip
}
task wrapper(type: Wrapper) {
description = "Generates gradlew[.bat] scripts"
gradleVersion = "4.4.1"
doLast() {
def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m"
def gradleBatOpts = "$gradleOpts -XX:MaxHeapSize=256m"
File wrapperFile = file("gradlew")
wrapperFile.text = wrapperFile.text.replace("DEFAULT_JVM_OPTS=",
"GRADLE_OPTS=\"$gradleOpts \$GRADLE_OPTS\"\nDEFAULT_JVM_OPTS=")
File wrapperBatFile = file("gradlew.bat")
wrapperBatFile.text = wrapperBatFile.text.replace("set DEFAULT_JVM_OPTS=",
"set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=")
}
}
}
/*
* Support publication of artifacts versioned by topic branch.
* CI builds supply `-P BRANCH_NAME=<TOPIC>` to gradle at build time.
* If <TOPIC> starts with 'SPR-', change version
* from BUILD-SNAPSHOT => <TOPIC>-SNAPSHOT
* e.g. 3.2.1.BUILD-SNAPSHOT => 3.2.1.SPR-1234-SNAPSHOT
*/
def qualifyVersionIfNecessary(version) {
if (rootProject.hasProperty("BRANCH_NAME")) {
def qualifier = rootProject.getProperty("BRANCH_NAME")
if (qualifier.startsWith("SPR-")) {
return version.replace("BUILD", qualifier)
}
}
return version
}
到此处,spring-framework-5.0.x在我本地就可编译成功,成功启动如下图:
后记
我的问题较多的原因可能是我代码是从我旧电脑拷贝到新电脑,然后再重新编译的问题。记录下来希望对大家有帮助。欢迎交流。