写点什么

spring 源码解析一、概述

作者:xzy
  • 2021 年 12 月 29 日
  • 本文字数:4020 字

    阅读完需:约 13 分钟

前言

为什么要读 Spring 源码?

1、Spring 是 java 世界里应用最广泛的框架,掌握 Spring 的源码可以:

  • 更好的解决在 Spring 使用过程中遇到的各种问题

  • 基于 Spring 做扩展,做二次开发

  1. Spring 是 SpringBoot、Spring cloud 等很多框架的基础,掌握了它,可以更好的理解这些框架。

  2. Spring 代码设计精巧,有很多设计模式的经典运用,对我们自己写好代码,提供了很多借鉴意义。


需要的前置知识?

  1. java 基础

  2. 熟练使用 Spring

  3. 设计模式

设计模式可以解决代码复用、扩展性等问题,但可能会降低代码的可读性,懂一些设计模式可以更好的理解 Spring 的代码逻辑。


如何阅读 Spring 的源码?

  1. 关注主体脉络,不要关注细节

  2. 看注释

  3. 见名知意

Spring 代码拥有良好的命名,大部分时候,我们仅仅通过这些接口、类、方法、变量等命名就能看出其设计意图

  1. 大胆猜测,小心验证

  2. 自己多动手,多 debug


Spring 源码的主要脉络

接下来,我会首先介绍 Spring 的主体脉络,示例代码地址xuziyang/spring-read: 阅读 spring 源码 (github.com)


下面已 ClassPathXmlApplicationContext 为例分析 Spring 源码

  ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
复制代码


在 ClassPathXmlApplicationContext 构造方法中执行下面三个逻辑

  1. 调用父类构造器

  2. 保存配置文件路径

  3. 执行容器的刷新逻辑

public ClassPathXmlApplicationContext(			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)			throws BeansException {		// 调用父类构造方法		super(parent);    // 保存配置文件路径		setConfigLocations(configLocations);		if (refresh) {      // 刷新容器			refresh();		}	}
复制代码


在其父类 AbstractApplicationContext 构造器中,创建了 PathMatchingResourcePatternResolver

public AbstractApplicationContext() {		this.resourcePatternResolver = getResourcePatternResolver();	}
public AbstractApplicationContext(@Nullable ApplicationContext parent) { this(); setParent(parent); }
protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this);}
复制代码


保存配置文件到 configLocations 属性中

public void setConfigLocations(@Nullable String... locations) {  if (locations != null) {    Assert.noNullElements(locations, "Config locations must not be null");    this.configLocations = new String[locations.length];    for (int i = 0; i < locations.length; i++) {      this.configLocations[i] = resolvePath(locations[i]).trim();    }  }  else {    this.configLocations = null;  }}
复制代码


容器的刷新方法定义在 AbstractApplicationContext 中。refresh()方法是对模板方法模式的经典应用。


模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。


refresh() 方法定义了 Spring 应用上下文刷新的算法骨架,包括 13 个方法,子类可以重写其中的方法,对其进行扩展。这 13 个方法便是 Spring 源码的整体脉络,后面的文章会对每个方法进行细致的讲解。


@Override	public void refresh() {		synchronized (this.startupShutdownMonitor) {			// 1.刷新前准备工作			prepareRefresh();
// 2.创建容器,并完成配置文件的加载 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3.填充容器的属性值 prepareBeanFactory(beanFactory);
try { // 4.默认没有实现,留给子类进行扩展 postProcessBeanFactory(beanFactory);
// 5.执行 BeanFactoryPostProcessors,修改BeanFactory 的相关信息,最常用的是 // 对 BeanDefination的修改 invokeBeanFactoryPostProcessors(beanFactory);
// 6.注册BeanPostProcessor registerBeanPostProcessors(beanFactory);
// 7.进行国际化相关的操作, initMessageSource();
// 8.初始化事件广播器 initApplicationEventMulticaster();
// 9.空方法,在Springboot 中用于启动 web 容器 onRefresh();
// 10.注册监听器 registerListeners();
// 11.完成所有非懒加载的单例对象的创建工作 // 最重要的一个方法,前面的步骤都是为此方法的执行做的准备 // 此方法开始真正的创建对象,包括了实例化、初始化、循环依赖、AOP 等核心流程的处理过程 finishBeanFactoryInitialization(beanFactory);
// 12.完成刷新 finishRefresh(); }
catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // 13.清空运行过程中产生的缓存 resetCommonCaches(); } } }
复制代码


扩展

Spring 通过一个配置文件描述 Bean 与 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立 Bean 与 Bean 之间的依赖关系。Spring 的 IOC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、声明周期管理、Bean 实例代理、事件发布、资源装载等高级服务。


Bean 工厂(org.springframework.beans.factory.BeanFactory)是 Spring 框架最核心的接口,它提供了高级的 IOC 配置机制。BeanFactory 使管理不同类型的 Java 对象成为可能,应用上下文(org.springframework.context.ApplicationContext)建立在 BeanFactory 基础之上,提供了更多面向应用的功能,它提供了国际化支持和和框架事件体系,更容易创建实际应用。


对于二者的用途,我们可以进行简单的划分:BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都可以直接使用 ApplicationContext 而非底层的 BeanFactory。

BeanFactory 体系结构

Spring 为 BeanFactory 提供了多种实现,最常用的是 XmlBeanFactory,该类在 Spring 3.2 中已废弃,建议使用 XmlBeanDefinitionReader、DefaultListableBeanFactory 替代。BeanFactory 的类继承体系设计优雅,堪称经典。通过继承体系,我们可以很容易了解到 BeanFactory 具有哪些功能,如下图所示:


BeanFactory 位于继承体系的顶端,最核心的方法就是 getBean(String name) 从容器中获取执行名称的 Bean。BeanFacory 的功能通过其子接口得到不断扩展。下面对这些接口进行说明:

  • ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数,获取某一类型 Bean 的配置名、查看容器中是否包含某一个 Bean 等。

  • HierarchicalBeanFactory:父子级联 IOC 接口,子类容器可以通过接口访问父容器。

  • ConfigurableBeanFactory:这是一个重要的接口,增强了 IOC 容器的可定制性。他定义了设置类加载器、属性编辑器、容器初始化后置处理器等方法。

  • AutowireCapableBeanFactory:定义了将容器中的 Bean 按照某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法。

  • SingletonBeanRegistry:定义了运行期间向容器中注册单例 Bean 的方法。

  • BeanDefinitionRegistry: 提供了向容器手工注册 BeanDefinition 的方法。


ApplicationContext 体系结构

ApplicationContext 的主要实现类是:ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统加载配置文件。下面了解一下 ApplicationContext 的类继承体系。

ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上还通过继承其他接口扩展了 BeanFactory 的功能。

  • ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动、关闭事件等。

  • MessageSource:为应用提供 i18n 国际化消息访问的功能。

  • ResourcePatternResolver:所有的 ApplicationContext 都实现了类似 PathMatchingResourcePatternResolve 的功能,可以通过带前缀的 Ant 风格资源文件路径装载 Spring 的配置文件。



ConfigurableApplicationContext 扩展于 ApplicationContext,它新增了两个主要的方法:refresh() 和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。


ClassPathXmlApplicationContext:配置文件放下类路径下的 ApplicationContext 实现。

AnnotationConfigApplicationContext:基于注解类配置提供的专门的 ApplicationContext 实现。

GenericGroovyApplicationContext:基于 Groovy 的配置提供的专门的 ApplicationContext 实现。


WebApplicationContext 体系结构

WebApplicationContext 是专门为 web 应用准备的,它允许从相对于 Web 根目录的路径中装载配置文件完成初始化工作。从 WebApplicationContext中可以获取 ServletContext 的引用,整个 Web 上下文对象将作为属性放置到 ServletContext 中,以便 web 应用 环境可以访问 Spring 应用上下文。Spring 专门为此提供了一个工具类 WebApplicationContextUtils,可以通过该类的 getWebApplicationContext(ServletContext sc) 方法,就可以从ServletContext 上下文中获取 WebApplicationContext 对象。


WebApplicationContext 添加了三个新的作用域:request、session 和 global session。



发布于: 刚刚
用户头像

xzy

关注

还未添加个人签名 2017.10.17 加入

还未添加个人简介

评论

发布
暂无评论
spring 源码解析一、概述