SpringBoot-Term-Definition

近期,在Github上读到了关于源码学习的仓库,并跟着学习了Spring全家桶源码。这对设计模式的学习和程序设计是十分有帮助的,因为Spring源码中存在大量设计模式的优秀实践。但设计模式的广泛运用也导致Spring类之间关系复杂,源码阅读费力。

在学习过程中,发现自己对Spring中许多概念是模糊的。这可能是因为学习Spring时,都是从使用侧考虑,忽略了非常多底层细节。对于概念的认知,更多来自于口口相传或望文生义的猜测。例如能否严谨地阐述容器,BeanFactoryApplicationContext三者间的关系。很多文章,包括笔者自己,都在不加区分地使用这三个概念。这种模糊的概念认知,对于日常使用Spring没有任何影响,但在学习源码时,就会产生大量歧义。

比较常见的源码学习方式,例如跟踪SpringApplication.run方法,查看一层层的调用情况,容易让读者迷失在概念的海洋当中。这样的学习模式是在用未知解释未知,很大一部分还是要靠猜,并且很难靠自己验证猜想是否准确。由于笔者对Spring整体并没有很深刻认知,本篇笔记更多是对源码的翻译和转述,着重描述Spring的概念。这些概念的解释全部来自于Spring源码和注释。

源码基于SpringBoot3.1.7版本。此版本没特别之处,只是上一个做的项目恰好使用了这个版本。通常在核心层面源码也不会发生大改动,任意一个常用的版本应该都能得到一致的概念描述。

接口、抽象类、具体类

学习Spring源码,最大的体验就是复杂的类关系。使用了大量的抽象,定义了许多接口、抽象类。

在面向对象编程的学习中,接口实现通常用来表示“能干什么”,而类继承通常表示“是什么”。但在Spring源码中,很多地方使用接口表示了“是什么”的概念。当然也可以玩文字游戏,接口可以表示“作为什么应该具有的功能”。例如Resource接口表示了,作为资源应该具有的功能。但笔者认为直接使用“是什么”去解释接口更符合人们常规理解。实现了Resource接口表示某个具体类是一种Resource

会产生这样的情况,很大原因是java无法进行多继承。而spring由于高度的抽象,功能划分地十分细致(接口隔离原则)。当一个具体类具有多个功能,多实现避免了无法多继承的限制。

但针对某个接口具体实现类中,通常包含大量相似的代码。虽然java 8后,可以通过提供默认方法,将相似代码抽取到接口中。但许多接口方法都依赖于具体的成员变量,而接口中只能包含常量,不能包含非常量的成员变量。因此这部分具体实现类中重复的代码,通常会被抽取到一个抽象类中,将这个抽象类实现接口,并在抽象类中提供许多接口方法的默认实现。

这样的模式在Spring中极为常见,识别这种模式能更便于理解类之间的关系。

Resource

1
2
package org.springframework.core.io
public interface Resource extends InputStreamSource

Interface for a resource descriptor that abstracts from the actual type of underlying resource, such as a file or class path resource.

An InputStream can be opened for every resource if it exists in physical form, but a URL or File handle can just be returned for certain resources. The actual behavior is implementation-specific.

Resource接口用来表示可以被读取内容的资源。通俗地理解就是某个文件,但这个文件可以使用多种方式来定位。例如可以使用文件系统路径(FileSystemResource),也可以使用类路径(ClassPathResource),或者使用URL/URI来表示位于网络上的资源(UrlResource)。不同的定位方式对应了不同的实现类。早期使用xml文件启动Spring,就是在启动过程中将xml文件的路径,包装成了一个FileSystemResource,用于读取其中定义的配置信息。

一些Resource接口的实现类也在资源包含的内容进行了声明。例如BeanDefinitionResourceMultipartFileResource

Resource接口有一个默认实现抽象类AbstractResource

Resource接口继承于InputStreamSource接口。InputStreamSource接口只声明了一个方法getInputStream,用于从资源中获取流对象,用于读取资源内容。

BeanDefinition

1
2
package org.springframework.beans.factory.config.
public interface BeanDefinition extends AttributeAccessor,BeanMetadataElement

A BeanDefinition describes a bean instance, which has property values, constructor argument values, and further information supplied by concrete implementations.

This is just a minimal interface: The main intention is to allow a BeanFactoryPostProcessor to introspect and modify property values and other bean metadata.

BeanDefinition接口定义了Spring容器中所管理的Bean实例的配置元数据,包含了各种属性值、构造器的参数值。BeanDefinition实例和容器中的Bean实例一一对应。

除此外,还包括parentNamebeanClassNamescopelazyInitdependsOn autowireCandidateprimaryfactoryBeanNamefactoryMethodNameinitMethodNamedestroyMethodNameroledescriptionresolvableTypesingletonprototypeabstractresourceDescriptionoriginatingBeanDefinition等元数据的访问/修改。关于这些元数据的概念,建议参考源码,在此不赘述。

BeanDefinition接口也有默认实现抽象类AbstractBeanDefinition

BeanDefinition继承了AttributeAccessor接口。AttributeAccessor接口声明了查询、访问、修改对象数据的方法。对于BeanDefinition来说,这里的数据指的就是元数据。使得对象的元数据可以在运行时动态地添加、获取和删除,从而提高了对象的灵活性和可扩展性。

BeanDefinition继承了BeanMetadataElement接口。BeanMetadataElement接口声明了获取BeanDefinition来源的信息,例如来自xml标签、注解、配置类等可能包含BeanDefinition信息的对象。

综上,BeanDefinition包含了bean的元数据信息,并包含了查询/修改元数据,获取元数据来源的功能。此接口主要用于BeanFactoryPostProcessor中,对BeanDefinition进行动态调整。


SpringBoot-Term-Definition
http://dracoyus.github.io/2024/04/22/SpringBoot-Term-Definition/
作者
DracoYu
发布于
2024年4月22日
许可协议