Spring的诞生为那个“黑暗年代”(EJB1)带来了一丝曙光,那是一个 基于J2EE 规范统治的时代,框架中上层调用者“奴役”下层实现者,两者形成“强耦合”关系;各种第三方框架强迫开发者实现或者是继承指定接口,框架“侵入”应用之中;散布在应用中的各模块也因为“强耦合”关系,无法自由应用,那个“黑暗年代”沉重的研发模式和生态让开发者痛苦不堪。
一、前言
Spring框架可以说是日常开发过程中应用最多的框架了,但是我们对于框架应用也只是基于基本功能的使用,一旦遇到”循环依赖“,”Bean配置失效“或者是一些其他的问题就感到非常的头疼,因为我们不了解Spring的底层实现,正所谓说“知己知彼,百战不殆”,这样我们才能快速解决问题,同时还能定制化Spring应用,真正发挥Spring框架的高级功能。
在学习Spring框架中不仅只是学习框架更深层次的应用,也是为了学习Spring框架中的”设计模式“应用和框架中系统设计思想,同时学习”大佬“们是怎么写Java代码的。
在Spring源码研读过程中,需要进行大量的代码调试以及做笔记,因此第一件事情就是要看源码,不仅要能看,还要能模,所以我们需要把Spring源码编译一下。
二、工具,环境,Spring源码版本说明
1、Spring源码版本
Spring源码Github地址:https://github.com/spring-projects/spring-framework/tree/5.0.x
这边选择是”5.0.X“版本的Spring源码,还有”5.1.X“以及”5.2.X“的版本。同时这边采用下载”ZIP“代码包的方式进行编译,毕竟国内的网速。。。
这边先介绍要使用的Spring源码包,是因为Spring源码包中有说明要采用的”Gradle“版本,在路径“spring-framework-5.0.x\gradle\wrapper”下,
打开“gradle-wrapper.properties”是看到如下内容:
这边需要关注“GRADLE_USER_HOME”以及“gradle-4.4.1-bin.zip”,这一部分的内容在“Gradle环境”中详细说明。
2、环境
2.1、Java环境
这是最基本,这里关于Java环境不多介绍,关于Spring源码编译上,官方也没指定什么版本,所以只展示当前电脑(Win10系统)上Java环境
2.2、Gradle环境
(1)Gradle版本选择
在上述Spring5.0.X源码包中显示Gradle版本要求是“gradle-4.4.1-bin.zip”,不一定要按照上面说,也可以自己选择Gradle版本,但是不能选择太高的版本,之前试过“Gradle-6.4”版本过高导致编译异常,最终选择“gradle-4.9-all”版本
Gradle官网:https://gradle.org/(如果不方便,在文末为有感谢各位博主的连接,可以下载)
(2)Gradle配置
如果是开发安卓的小伙伴,对Gradle不陌生,但是我是作为Java后台开发,平常开发过程中还是使用Maven构建工程居多,对于Gradle也不是很熟悉,因此只是配置一下Gradle环境。
配置"GRADLE_HOME"环境变量
这一步和配置JAVA_HOME非常的类似,新建系统变量“GRADLE_HOME”写上,Gradle下载好之后并解压的目录
配置“PATH”环境变量
在系统环境变量“PATH”中编辑配置写上“%GRADLE_HOME%\bin”即可。
配置“GRADLE_USER_HOME”环境变量
这一步的配置是指定Gradle的“本地仓库”所在的位置,这边可以写成与Maven的“本地仓库”所在的路径一样。
配置“init.gradle”初始化文件
在Gradle压缩包解压之后,存在一个“init.d”文件夹,但是这个文件夹里面是空的,因此需要手动创建“init.gradle”文件,这一步是配置Gradle构建项目中Jar依赖的下载地址
创建好之后,配置文件内容如下:
allprojects{
repositories {
def REPOSITORY_URL ='http://maven.aliyun.com/nexus/content/groups/public/'
all {
ArtifactRepository repo-> def url =repo.url.toString()
if ((repoinstanceof MavenArtifactRepository) &&(url.startsWith('https://repo1.maven.org/maven2')||url.startsWith('https://jcenter.bintray.com'))) {
project.logger.lifecycle'Repository ${repo.url} replaced by $REPOSITORY_URL .' remove repo
}
}
maven {
url REPOSITORY_URL
}
}
}
以上的内容均配置完成之后,可以查看最终结果
这边同样需要注意一下信息
Kotlin DSL: 0.18.4
Kotlin: 1.2.41 <----当前Kotlin版本
Groovy: 2.4.12 <----当前Groovy版本
Ant: Apache Ant(TM) version 1.9.11 compiled on March 23 2018
JVM: 1.8.0_65 (Oracle Corporation 25.65-b01) <----当前JVM版本
OS: Windows 10 10.0 amd64
3、工具
当前电脑上用的是“IntelliJ IDEA 2019.3.3 ”的IDEA版本,不过Spring项目编译过程中对于IDEA的版本关系倒是不大,最关键的是各种版本需要对应上。
三、Spring源码配置
Spring源码在编译之前需要做一些基本配置,保证源码一致,这样能有效提高Spring源码编译成功率。
1、配置“gradle-wrapper.properties”
关于“gradle-wrapper.properties”是配置Spring编译过程中对于Gradle环境的要求,之前提到过,可以通过该配置文件知道Gradle版本,但是在实际编译过程中,会遇到如下情况
即使是你按照上述过程配置好Gradle环境,依然会出现,Downloading下载Gradle安装包
因此,按照网上参考的解决办法是,修改“gradle-wrapper.properties”中“distributionUrl”属性为采用的Gradle版本,同时直接把已经下载好的Gradle包拷到该文件夹下。
效果如下:
2、配置“docs.gradle”文件
该文件位于“spring-framework-5.0.x\gradle”下
这一步是去除编译过程中关于JavaDoc文档的构建,在Spring源码编译过程中,JavaDoc文档编译非常的消耗时间,甚至4-5小时编译不下来,可能和我电脑性能也有关系。
注释掉“dokka”以及“asciidoctor”内容
(PS:说明一下,这边主要是涉及到JavaDoc的编译,在实际过程中编译时间太久的话,会直接禁用Gradle编译文档的Task<任务> ,因此这一步可做可不做)
3、配置“gradle.properties”配置文件
该文件位于“spring-framework-5.0.x”,Spring源码包直接解压之后的根目录下
配置效果如下:
配置内容如下:
version=5.0.19.BUILD-SNAPSHOT
## 开启缓存
org.gradle.caching=true
## 开启守护进程
org.gradle.daemon=true
## 设置此参数主要是编译下载包会占用大量的内存,可能会内存溢出
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
## 开启并行编译
org.gradle.parallel=true
## 启用新的孵化模式
org.gradle.configureondemand=true
4、配置“build.gradle”项目构建文件
该文件位于“spring-framework-5.0.x”,Spring源码包直接解压之后的根目录下
在这个构建的配置文件中,需要做两件事情
(1)配置远程仓库下载地址
配置效果如下:
配置内容如下:
maven { url "http://maven.aliyun.com/nexus/content/groups/public/"}
(2)核对当前版本是否保持一致
上述配置Gradle环境中,我们可以看到当前“Kotlin”以及“Groovy”版本,修改配置文件中相对应的版本
指定版本保持一致
四、Spring源码导入IDEA
在上述Spring源码配置完成之后,就可以把源码导入IDEA中
1、Spring源码导入IDEA中
在IDEA左上角上选择“File -> New -> Project from Existing Sources ”,然后再选择Spring源码目录下的“build.gradle”文件
导入项目之后,在IDEA右侧“Gradle”便会出现“spring”结果
导入项目之后,IDEA便会开始自动的下载依赖,不过一般来说第一次导入依赖多会失败,还需要配置一下IDEA相关内容
2、配置IDEA
1、项目Gradle配置
打开“File -> Setting”
2、 配置“Kotlin Compiler"
打开“File -> Setting”
3、配置项目JDK
打开”File -> Project Structure“
4、刷新依赖
点击右侧打红框的小图标开始刷新依赖,这是一个比较漫长的过程。。。
这是表示依赖刷新成功.
五、编译Spring源码
当依赖刷新好了之后,便可以开始编译源码了,在Spring源码解压之后,可以发现Spring源码中自带了编译教程,一个是关于IDEA,还有一个就是关于Eclipse
我们只看关于IDEA的就好
1、编译”spring-core“以及”spring-oxm“模块
`spring-core` and `spring-oxm` should be pre-compiled due to repackaged dependencies.
翻译大致如下:
由于重新包装了依赖关系,应预先编译“ spring-core”和“ spring-oxm”
按照提示我们应该先编译”spring-core“以及”spring-oxm“模块
在IDEA操作如下:
文中要求执行”./gradlew :spring-oxm:compileTestJava“其实就是对应如下操作
关于”spring-core“编译同样如此操作:
2、移除”spring-aspects“模块
`spring-aspects` does not compile due to references to aspect types unknown to IntelliJ IDEA. See https://youtrack.jetbrains.com/issue/IDEA-64446 for details. In the meantime, the 'spring-aspects' can be excluded from the project to avoid compilation errors.
翻译大致如下:
由于引用了未知的方面类型,因此无法编译“ spring-aspects” IntelliJ IDEA。 有关详细信息,请参见https://youtrack.jetbrains.com/issue/IDEA-64446。 同时, 可以从项目中排除“ spring-aspects”,以避免编译错误。
因此在编译过程中需要先移除”spring-aspects“模块
3、编译Spring源码。
上述过程中都操作好了之后,就可以开始编译Spring源码
编译Spring源码需要花费挺长的时间,而且编译过程中也会需要各种各样的问题,在编译过程中如遇到以下问题,可以参考一下解决办法。
4、编译过程常见问题
(1)No such property: values for class: org.gradle.api.internal.tasks.DefaultTaskDependency
详细报错信息如下:
* Where: Build file 'D:\git��Ŀ\springԴ��\spring-framework-5.0.x\spring-framework-5.0.x\spring-beans\spring-beans.gradle' line: 28 *
What went wrong: A problem occurred evaluating project ':spring-beans'.
> No such property: values for class: org.gradle.api.internal.tasks.DefaultTaskDependency
Possible solutions: values
参考解决办法:
后三行替换内容如下:
def deps = compileGroovy.taskDependencies.immutableValues + compileGroovy.taskDependencies.mutableValues
compileGroovy.dependsOn = deps - "compileJava"
compileKotlin.dependsOn(compileGroovy)
compileKotlin.classpath += files(compileGroovy.destinationDir)
(2)Execution failed for task ':spring-beans:javadoc'.
详细报错信息如下:
FAILURE: Build failed with an exception.
* What went wrong: Execution failed for task ':spring-beans:javadoc'.
> Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting): 'E:\myspring\spring-framework-4.3.10.RELEASE\spring-beans\build\tmp\javadoc\javadoc.options'
参考解决办法:
修改内容:
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED options.author = true
options.encoding = "UTF-8"
options.header = project.name options.use = true
options.links(project.ext.javadocLinks)
options.addStringOption("Xdoclint:none", "-quiet")
(3)等待了很长的编译时间,还是编译未完成
问题描述:在编译过程中如果模块一直处在JavaDoc编译过程中,无法编译完成
点击此步骤可以查看完整的编译过程,
在编译过程中如果有发现类似“doc”,或者是类似“javaDoc”的字样,就是说明在编译JavaDoc文档,这是一个非常非常耗时的过程,因此可以禁止该Task<任务>的执行
解决办法如下:
把这些都禁止掉,能加快编译速度。
(4)编译过程中卡在“test”环节,或者是其他环节导致编译无法成功
在Gradle编译项目或者是Maven编译项目过程中,都会经历“Test”环节,因此编译过程可以跳过Test环节
解决办法如下:
输入以下Build命令:
gradle build -x testClasses -x test -x javadoc -x compileTestKotlin 也可以用于跳过测试,以及文档构建环节
效果如下图:
六、构建模块测试
最终Spring项目编译完成之后。并不代表,项目就能能正常使用的,因此还需要构建一个简单的模块进行测试。
1、创建一个模块
创建模块方式类似Maven创建模块一样。
2、在“build.gradle”引入相关的依赖
在一个模块下“build.gradle”就是类似Maven的pom文件
引入Spring相关依赖:
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'
}
3、开始测试
创建一个Bean对象
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public User() {
}
@Override
public String toString() {
return "MyName:"+this.name+",MyAge"+this.age+" old";
}
}
创建配置类
@Configuration
@ComponentScan
public class ApplyConfig {
@Bean
public User user(){
User user = new User();
user.setAge(18);
user.setName("张三");
return user;
}
}
应用主类
public class MyMain {
public static void main(String[] args) {
System.out.println("Hello Spring");
ApplicationContext ac =new AnnotationConfigApplicationContext(ApplyConfig.class);
User user = (User) ac.getBean("user");
System.out.println(user.toString());
}
}
执行结果
到此Spring源码算是编译成功!
PS:关于项目控制台输出出现乱码,即使设置了“UTF-8”还是会出现乱码
添加如下内容:
-Dfile.encoding=UTF-8
添加完成之后,一定要记得重启IDEA!这样配置才能生效。
七、参考文献
在此次搭建Spring源码阅读环境中,也是遇到非常多的坑,在踩坑过程中参考了诸多博主的博客内容才得以解决问题,感谢这些博主所分享的内容。
- Spring相关博客连接:
码之初 ttps://www.cnblogs.com/mazhichu/p/13163979.html
Dcwjh https://blog.csdn.net/Dcwjh/article/details/104471560
疯狂的暴走蜗牛 https://blog.csdn.net/u010936936/article/details/103404842
微瞰技术 https://blog.csdn.net/u011342403/article/details/104485619
- Gradle相关博客连接:
visionarywind https://www.jianshu.com/p/ff5d9c33c108
刘亚芳 https://www.jianshu.com/p/d9329117aa2f (各Gradle版本下载)