当前位置: 代码迷 >> 综合 >> java源码 - Spring5.x(1)之 基本概览
  详细解决方案

java源码 - Spring5.x(1)之 基本概览

热度:52   发布时间:2024-02-08 03:57:35.0

书籍《Spring源码深度解析》
官方文档
之前的一篇
推荐一个博主的系列博客,手写IOC,AOP
源码Spring5.x


文章目录

  • 1. 整体架构
  • 1.2 环境搭建pom
  • 2. Spring流程分析
  • 3. 核心类
    • 3.1 DefaultlistableBeanFactory
    • 3.2 XmlBeanDefinitionReader
    • 3.3 XmlBeanFactory

1. 整体架构

在这里插入图片描述
从Spring的架构图从下往上看,下层是上层的依据基础。

  1. Core Container
    其中的Core和Beans模块是框架的基础部分,提供IoC(转控制)和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
    但Context模块构建于Core和Beans模块基础之上,提供了一种类似于JNDI注册器的框架式的对象访问方法。
    Expression Language模块提供了一个强大的表达式语言用于在运行时查询和操纵对象。

  2. Data Access/Integration

  • JDBC模块提供了一个JDBC抽象层,它可以消除冗长的JDBC编码和解析数据库厂商特有的错误代码。这个模块包含了Spring对JDBC数据访问进行封装的所有类。
  • RM模块为流行的对象-关系映射API,如JPA、JDO、Hibernate、iBatis等,提供了一个交互层。利用ORM封装包,可以混合使用所有Spring提供的特性进行O/R映射。如前边提到的简单声明性事物管理。
  1. Web
    Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。
  2. AOP
    AOP模块提供了一个符合AOP联盟标准的面向切面编程的实现,它让你可以定义例如方法拦截器和切点,从而将逻辑代码分开,降低它们之间的耦合性。AOP采用Aspects模块提供了对AspectJ的集成支持。

1.2 环境搭建pom

    <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin></plugins></build><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.8.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>compile</scope></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.1</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency></dependencies>

2. Spring流程分析

使用过Spring的都应该了解,Spring的工作流程一般是读取配置文件(扫描配置),实例化Bean,调用Bean。
在这里插入图片描述
那么对于源码的学习也就从以上的三个角度来探讨。

3. 核心类

3.1 DefaultlistableBeanFactory

在这里插入图片描述
XmlBeanFactory 继承 DefaultListableBeanFactory ,而DefaultListableBeanFactroy 是整个 bean加载的核心部分,是 Spring 注册及加载 Bean 的默认实现,而对于 XmlBeanFactory与DefaultListableBeanFactory 不同的地方其实是在 XmlBeanFactory 中使用了自定义的 XML 读取器XmlBeanDefinitionReader ,实现了个性化的 BeanDefinitionReader 读取, DefaultListableBeanFactory
继承了 AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory以及BeanDefinitionRegistry 接口。

如果想了解其父类以及接口的作用,可以查看源码的注释。
在这里插入图片描述

3.2 XmlBeanDefinitionReader

XML 置文件的读取是 spring 重要的功能 ,因为 Spring 的大部分功能都是以配置作为切入点的。
在这里插入图片描述
其中:

  • BeanDefinitionReader :主要用于定义资源文件读取并转换为BeanDefinition的功能。
  • AbstractBeanDefinitionReader:对 EnvironmentCapab BeanDefinitionReader 类定义的功能进行实现。
  • EnvironmntCapable :定义获取 Environment 方法。

其次,在5.x注解明显还是用得多些,那么就存在AnnotatedBeanDefinitionReader
用于编程注册注释bean类。是一个适配器。这是的另一种应用注释的解析方式,但仅用于显式注册的类。是非常常用的注解模式下的配置读取类。

XML 文件读取的大致流程:

  1. 通过继 AbstractBeanDefinitionReader 中的方法,来使用 ResoureLoader 将资源文件路径转换为对应的 Resource 文件。
  2. 通过 DocumentLoaderResource 文件进行转换,将 Resource 文件转换为 Document文件。
  3. 通过实现接口 BeanDefinitionDocumentReader 的DefaultBeanDefinitionDocumentReader对Document 进行解析,并使用 BeanDefinitionParserDelegate对 Element 进行解析。

ResoureLoader - > Resource - > Document

3.3 XmlBeanFactory

这还是一个很传统的配置文件的解析方式。

public class XmlBeanFactory extends DefaultListableBeanFactory {private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);/**使用给定的资源创建一个新的XmlBeanFactory,*必须使用DOM解析。* @param 从其中加载bean定义的XML资源* @throws 加载或解析错误时的BeansException异常*/public XmlBeanFactory(Resource resource) throws BeansException {this(resource, null);}/**使用给定的输入流创建一个新的XmlBeanFactory,*必须使用DOM解析。* @param 从其中加载bean定义的XML资源* @param 从它加载由bean定义的XML资源* @throws 加载或解析错误时的BeansException异常*/public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}
}

Spring 的配置文件读取是通过 ClassPathResource 进行封装的。

在Java 中,将不同来源的资游抽象成 URL ,通过注册不同的 handler ( URLStreamHandler ) 来处理不同来源的资源的读取逻辑,一般 handler 类型使用不同前缀(协议, Protocool )来识别,如“file :”“ http :” jar :”等,然而 URL 没有默认定义相对 Classpath 或者ServletContext 等资源的 handler ,虽然可以注册自己的 URLStreamHandler 来解析指定得 URL 前缀(协议 ), 比如Classpath ,然而这需要了解 URL的实现机制,而URL 也也没有提供基本 方法,如检查当前资源是否存在、检查当前资源是否可读 等方法。 因而Spring 内部使用到的资源实现了自己的抽象结构 :Resource 接口封装底层资源。

public interface Resource extends InputStreamSource {/***确定该资源是否以物理形式实际存在。该方法执行确定的存在性检查,而*存在一个{@code Resource}句柄只能保证有效*描述符句柄。*/boolean exists();/***指示是否可以通过读取该资源的内容* {@link #getInputStream()}.* <p>对于典型的资源描述符,将为{@code true};*注意,实际的内容阅读仍然可能失败时,尝试。但是,值{@code false}是一个明确的指示*无法读取资源内容。* @see #getInputStream()*/default boolean isReadable() {return true;}/**指示该资源是否表示一个打开流的句柄。*如果{@code true}, InputStream不能被多次读取,*必须读取和关闭,以避免资源泄漏。* <p对于典型的资源描述符>将是{@code false}。*/default boolean isOpen() {return false;}/**确定该资源是否表示文件系统中的文件。强烈建议(但不保证)值为{@code true}*一个{@link #getFile()}调用将成功。* <p>默认情况下,这是{@code false}。* @since 5.0* @see #getFile()*/default boolean isFile() {return false;}/***返回此资源的URL句柄。* @throw IOException如果资源不能被解析为URL,*例如,如果资源不能作为描述符使用*/URL getURL() throws IOException;/***返回该资源的URI句柄。* @抛出IOException,如果资源不能被解析为URI,*例如,如果资源不能作为描述符使用*/URI getURI() throws IOException;/***返回该资源的文件句柄。* @throws . io .如果资源不能被解析为*绝对文件路径,即,如果资源在文件系统中不可用*在一般的解析/读取失败时,@抛出IOException* @see # getInputStream ()*/File getFile() throws IOException;/***返回{@link ReadableByteChannel}。* p>期望每个调用创建一个fresh通道。*默认实现返回{@link Channels#newChannel(InputStream)}*,结果为{@link #getInputStream()}。* @返回底层资源的字节通道(不能是{@code null})* @throws . io .如果底层资源不存在,FileNotFoundException异常* @抛出IOException,如果内容通道无法打开* @see #getInputStream()*/default ReadableByteChannel readableChannel() throws IOException {return Channels.newChannel(getInputStream());}/***确定该资源的内容长度。* @抛出IOException,如果资源不能被解析*(在文件系统或其他已知的物理资源类型中)*/long contentLength() throws IOException;/***确定该资源的最后修改时间戳。* @抛出IOException,如果资源不能被解析*(在文件系统或其他已知的物理资源类型中)*/long lastModified() throws IOException;/**创建一个相对于该资源的资源。* @param relativePath相对路径(相对于此资源)返回相对资源的资源句柄* @抛出IOException,如果相关资源无法确定*/Resource createRelative(String relativePath) throws IOException;/***确定这个资源的文件名,通常是最后一个路径的一部分:例如,“myfile.txt”。*返回{@code null},如果该类型的资源没有返回*有一个文件名。*/@NullableString getFilename();/***返回该资源的描述,*用于处理资源时的错误输出。*实现也被鼓励返回这个值*从他们的{@code toString}方法。* @see Object#toString()*/String getDescription();
}

由以上代码可见Resource主要定义了当前资源状态的方法:存在性,可读性,是否处于打开状态。其次还提供了URL,URI,file类型的转换。

public interface InputStreamSource {/***返回一个{@link InputStream}作为底层资源的内容。期望每个调用都创建一个fresh流。当您考虑这样的API时,这个要求是特别重要的*作为JavaMail,其中需要能够多次读取流时创建邮件附件。对于这样的用例,它是required*每个{@code getInputStream()}调用都会返回一个新的流。* @返回底层资源的输入流(不能是{@code null})* @throws . io .如果底层资源不存在,FileNotFoundException异常如果无法打开内容流,@抛出IOException*/InputStream getInputStream() throws IOException;
}

InputStreamSource封装能返回InputStream的类。可以说他提供了一个对外的同一接口,而底层可以是任意的文件来源。

其继承类图如下:
在这里插入图片描述

//ClassPathResource 可以是任意的文件来源采用对于的xxxResource类。
Resource resource=new ClassPathResource("beanFactoryTest.xml”), 
InputStream inputStream=resource.getinputStream();

那么接下来可以稍微的了解一下XmlBeanFactory的初始化过程。

	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}

上面函数中的代码 this.reader.loadBeanDefinitions(resource)才是资源加载的真正实现。
再次之前父类的最终调用方法为:

	public AbstractAutowireCapableBeanFactory() {super();ignoreDependencyInterface(BeanNameAware.class);ignoreDependencyInterface(BeanFactoryAware.class);ignoreDependencyInterface(BeanClassLoaderAware.class);}

ignoreDependencyInterface的主要功能是忽略给定接口的向动装配功能,主要功能是为了解决属性依赖问题,当 A中有属性B,那么 Spring 在获取 Bean 的时候如果其属性,B还没有初始化,那么Spring会自动初始化 ,这也是 Spring提供的一个重要特性。

  相关解决方案