Spring源码研读之路(1)

2023-11-26 10:30
文章标签 java 源码 spring 研读

本文主要是介绍Spring源码研读之路(1),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring的诞生为那个“黑暗年代”(EJB)带来了一丝曙光,那是一个 基于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

image

这边选择是”5.0.X“版本的Spring源码,还有”5.1.X“以及”5.2.X“的版本。同时这边采用下载”ZIP“代码包的方式进行编译,毕竟国内的网速。。。

这边先介绍要使用的Spring源码包,是因为Spring源码包中有说明要采用的”Gradle“版本,在路径**“spring-framework-5.0.x\gradle\wrapper”**下,

image

打开**“gradle-wrapper.properties”**是看到如下内容:

image

这边需要关注“GRADLE_USER_HOME”以及“gradle-4.4.1-bin.zip”,这一部分的内容在“Gradle环境”中详细说明。

2、环境

2.1、Java环境

这是最基本,这里关于Java环境不多介绍,关于Spring源码编译上,官方也没指定什么版本,所以只展示当前电脑(Win10系统)上Java环境

image

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下载好之后并解压的目录

image

image

配置“PATH”环境变量

在系统环境变量“PATH”中编辑配置写上“%GRADLE_HOME%\bin”即可。

image

#####配置“GRADLE_USER_HOME”环境变量

这一步的配置是指定Gradle的“本地仓库”所在的位置,这边可以写成与Maven的“本地仓库”所在的路径一样。

image

#####配置“init.gradle”初始化文件

在Gradle压缩包解压之后,存在一个“init.d”文件夹,但是这个文件夹里面是空的,因此需要手动创建“init.gradle”文件,这一步是配置Gradle构建项目中Jar依赖的下载地址

image

创建好之后,配置文件内容如下:

image

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

以上的内容均配置完成之后,可以查看最终结果
image.png
这边同样需要注意一下信息

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的版本关系倒是不大,最关键的是各种版本需要对应上。
image.png
##三、Spring源码配置
Spring源码在编译之前需要做一些基本配置,保证源码一致,这样能有效提高Spring源码编译成功率。

1、配置“gradle-wrapper.properties”

关于“gradle-wrapper.properties”是配置Spring编译过程中对于Gradle环境的要求,之前提到过,可以通过该配置文件知道Gradle版本,但是在实际编译过程中,会遇到如下情况
image.png
即使是你按照上述过程配置好Gradle环境,依然会出现,Downloading下载Gradle安装包
因此,按照网上参考的解决办法是,修改“gradle-wrapper.properties”中“distributionUrl”属性为采用的Gradle版本,同时直接把已经下载好的Gradle包拷到该文件夹下。
image.png
效果如下:
image.png

2、配置“docs.gradle”文件

该文件位于**“spring-framework-5.0.x\gradle**”下
image.png
这一步是去除编译过程中关于JavaDoc文档的构建,在Spring源码编译过程中,JavaDoc文档编译非常的消耗时间,甚至4-5小时编译不下来,可能和我电脑性能也有关系。
注释掉“dokka”以及“asciidoctor”内容
image.png
image.png
(PS:说明一下,这边主要是涉及到JavaDoc的编译,在实际过程中编译时间太久的话,会直接禁用Gradle编译文档的Task<任务> ,因此这一步可做可不做)

3、配置“gradle.properties”配置文件

该文件位于“spring-framework-5.0.x”,Spring源码包直接解压之后的根目录下
image.png
配置效果如下:
image.png
配置内容如下:

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源码包直接解压之后的根目录下
image.png
在这个构建的配置文件中,需要做两件事情
(1)配置远程仓库下载地址
配置效果如下:
image.png
配置内容如下:

maven { url "http://maven.aliyun.com/nexus/content/groups/public/"}

(2)核对当前版本是否保持一致

上述配置Gradle环境中,我们可以看到当前“Kotlin”以及“Groovy”版本,修改配置文件中相对应的版本
image.png
image.png
指定版本保持一致

四、Spring源码导入IDEA

在上述Spring源码配置完成之后,就可以把源码导入IDEA中

1、Spring源码导入IDEA中

在IDEA左上角上选择“File -> New -> Project from Existing Sources ”,然后再选择Spring源码目录下的“build.gradle”文件
image.png
image.png
导入项目之后,在IDEA右侧“Gradle”便会出现“spring”结果
image.png
导入项目之后,IDEA便会开始自动的下载依赖,不过一般来说第一次导入依赖多会失败,还需要配置一下IDEA相关内容

2、配置IDEA

#####1、项目Gradle配置

打开“File -> Setting
image.png
#####2、 配置“Kotlin Compiler"

打开“File -> Setting
image.png
#####3、配置项目JDK

打开”File -> Project Structure
image.png
#####4、刷新依赖
image.png
点击右侧打红框的小图标开始刷新依赖,这是一个比较漫长的过程。。。
image.png
这是表示依赖刷新成功.

五、编译Spring源码

当依赖刷新好了之后,便可以开始编译源码了,在Spring源码解压之后,可以发现Spring源码中自带了编译教程,一个是关于IDEA,还有一个就是关于Eclipse
image.png
我们只看关于IDEA的就好
image.png

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“其实就是对应如下操作
image.png
关于”spring-core“编译同样如此操作:
image.png
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“模块
image.png

3、编译Spring源码。

上述过程中都操作好了之后,就可以开始编译Spring源码
image.png
编译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

参考解决办法:
image.png
后三行替换内容如下:

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'

参考解决办法:
image.png
修改内容:

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编译过程中,无法编译完成
image.png
点击此步骤可以查看完整的编译过程,
image.png
在编译过程中如果有发现类似“doc”,或者是类似“javaDoc”的字样,就是说明在编译JavaDoc文档,这是一个非常非常耗时的过程,因此可以禁止该Task<任务>的执行
解决办法如下:
image.png
image.png
把这些都禁止掉,能加快编译速度。

(4)编译过程中卡在“test”环节,或者是其他环节导致编译无法成功

在Gradle编译项目或者是Maven编译项目过程中,都会经历“Test”环节,因此编译过程可以跳过Test环节

解决办法如下:
image.png
输入以下Build命令:

gradle build -x testClasses -x test -x javadoc -x compileTestKotlin 也可以用于跳过测试,以及文档构建环节

效果如下图:
image.png

六、构建模块测试

最终Spring项目编译完成之后。并不代表,项目就能能正常使用的,因此还需要构建一个简单的模块进行测试。

1、创建一个模块

image.png
创建模块方式类似Maven创建模块一样。

2、在“build.gradle”引入相关的依赖

在一个模块下“build.gradle”就是类似Maven的pom文件
image.png
引入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、开始测试
image.png
创建一个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() {}@Overridepublic String toString() {return "MyName:"+this.name+",MyAge"+this.age+" old";}
}

创建配置类

@Configuration
@ComponentScan
public class ApplyConfig {@Beanpublic 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());}
}

执行结果
image.png
到此Spring源码算是编译成功!

PS:关于项目控制台输出出现乱码,即使设置了“UTF-8”还是会出现乱码
image.png
image.png
添加如下内容:

-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版本下载)

这篇关于Spring源码研读之路(1)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/425535

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定