为什么读不懂Spring源码?到底如何去读?从哪开始读?
怎样阅读Spring源码
面试的时候,你肯定被问过,Spring的初始化过程、或者Bean的生命周。我相信大部分人都是死记硬背的,真正能自己读懂代码而说出整个过程的人寥寥无几。
造成这种局面的原因,尝试阅读Spring源码的人肯定深有感触,代码量是一部分原因,还有就是里面的各种抽象的接口,如果没有人或者书的指点,你很难知道它是起什么作用。
不是我们的能力不行,而是方法不对。
就像我们工作中,对于别人写的代码让你直接去阅读,没有当事人的帮助,那绝对是很痛苦的一件事,不论你的技术有多牛。
如下图所示,是Spring中加载Bean对象的一个简单过程:
图像看着很简单,但是对于不熟悉Spring的人来说,确实可以死记硬背,但是并不能理解图中说的是什么。因为里面的各种概念(Resource、BeanDefinition、BeanDefinitionRegistry )从来没见过。
因此,阅读源码我们应该从点到面。先了解Spring设计的各种概念、各种抽象的接口。然后在去整体串起来看,就会容易很多了。
这里打算从各个点开始详细介绍Spring相关的概念,和各位道友一起讨论交流,最后在串起来,真正读懂Spring。
第一步:资源访问Resource
Spring中喜欢把一切都看做资源(Resource)。除了我们认为的图片、css、js、配置文件(xml、yml),甚至我们定义的类都可以看做是资源。
Spring设计了一个Resource接口,它为应用提供了资源的访问能力,该接口拥有对应不同资源类型的实现类。
1、资源接口Resource
先看一下Resource接口的主要方法:
public interface InputStreamSource {
//返回资源对应的输入流
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
//资源是否存在
boolean exists();
//资源是否打开
default boolean isOpen() {
return false;
}
//如果资源可以表示成Url,则返回对应的URL对象。
URL getURL() throws IOException;
URI getURI() throws IOException;
//如果资源是一个文件,返回File对象
File getFile() throws IOException;
}
复制代码
Spring框架使用Resource装载各种资源,如下图为Resource的具体实现类:
- WritableResource:可写资源接口,用来写资源的。实现类有FileSystemResource、FileUrlResource、PathResource
- ByteArrayResource: 读取二进制数组表示的资源
- ClassPathResource: 类路径下的资源,资源以相对于类路径的方式表示
这里就不一一列举了,可以自己看看图片。
阅读各个实现类的办法:
我们知道Resource是用来读取资源的,所以最重要的一个实现方法就是getInputStream方法。从这个方法为入口阅读,你会看到最终会回到Jdk的IO流上去。
还需要了解一下Java的类加载器ClassLoader的相关知识。
2、Resource实现类的使用
这里使用ClassPathResource和FileSystemResource来举例获取Spring的资源文件application.properties。
提示:我们常说的classpath,也就是类路径,就是代码编译后,会有一个target目录,里面的classes就是类路径。
public class Test {
public static void main(String[] args) throws IOException {
String systemFile = "/Users/xuezhendong/IdeaProjects/SpringLearn/src/main/resources/application.properties";
FileSystemResource fileSystemResource = new FileSystemResource(systemFile);
System.out.println(fileSystemResource.getFilename());
InputStream inputStream = fileSystemResource.getInputStream();
System.out.println(inputStream);
String classPathFile = "application.properties";
ClassPathResource resource = new ClassPathResource(classPathFile);
System.out.println(resource.getFilename());
System.out.println(resource.getInputStream());
}
}
复制代码
结果
结果中返回的均是JDK中的IO流。我们获取资源后,可以通过定义的方法获取File对象,获取InputStream、OutPutStream,然后就能进行我们想操作的一切。
3、资源加载
为了访问不同的资源,必须使用不通的Resource的实现类,这是比较麻烦的一件事。因此Spring提供了一个强大的资源加载机制,不但能够通过”classpath:“、”file:“等资源地址前缀识别不通的资源类型,还支持Ant风格带通配符的资源地址。
3.1资源地址表达
Ant风格:
?匹配文件名中的一个字符。 *匹配文件名中的任意多个字符。**配置多层路径。
地址前缀 | 资源类型 |
---|---|
classpath: | 类路径下的资源 |
file: | 文件系统资源 |
http:// | 从网络加载资源 |
ftp:// | 从ftp服务器加载资源 |
没有前缀 | 根据具体的实现类加载 |
定义好了资源如何表示,接下来看看Spring如何使用不同的表示来加载资源的。
3.2资源加载器
Spring定义了一套资源加载的接口,并提供了实现类:
ResourceLoader接口如下,最主要的接口就是获取资源Resource,该方法可以根据带资源类型前缀的地址去获取资源,但是不支持Ant风格的方式。
ResourcePatternResolver扩展了ResourceLoader接口,支持了Ant风格,PathMatchingResourcePatternResolver是Spring提供的该接口的标准实现类。
public interface ResourceLoader {
//"classpath:"
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
复制代码
阅读方式仍然就去从子类中的getResource方法入手,容易看出是根据不同的前缀来调用不通的Resource类。
总结:
阅读Spring源码不是那么容易的事,里面概念很多,希望与众道友一起讨论交流。
大家在看文章的过程中,最好自己能打开代码,去查看和验证,这样记忆最深刻。