Spring IOC 容器源码解析 - Sanarous的博客

Spring IOC 容器源码解析

关于 Spring 框架,其实在做项目的时候,往往都是会用就行,很少会去深入底层分析其原理,而 Spring 的核心概念相信用过的人都应该知道,就是 IoC(控制反转) 和 AOP(面向切面编程),既然解析源码,我们就一步步开始分析,先从 IoC 原理开始,本文采用的 Spring 版本是 4.3.11.RELEASE,现在 Spring 版本已经是 5.x 了,但是这不妨碍我们从 4.x 版本探究原理,毕竟核心思想是没有变化的。

本文阅读前提是读者需要知道怎么配置 Spring 并了解 Spring 中的各种概念,最好也会使用 SpringMVC。对于分析 IoC 容器原理来说,从整体上需要把握两个大方向,一个是 IoC 是如何创建 Bean 容器的,另外一个是如何初始化 Bean 的,我们可以按照这个方向一步步往下走,如此可对 IoC 原理有一个更加清晰的认识。

和很多分析源码的博客一样,本文同样采用 xml 配置的方式,这样操作上更加简单直观,并且易于查看源码执行过程。

引言

我们使用 ClassPathXmlApplication 启动 Spring 容器,方式如下,这个大家应该不会陌生:

1
2
3
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}

既然是由 ClassPathXmlApplicationContext 创建的,那我们先来看看这个类的继承图:

好像看的眼花缭乱,不碍事,我们直接看它的主干关系:

其实 Spring 源码中每个类的名字起的是非常符合规范的,我们通过上述类名就可以推断出这个类大概有什么作用的,实际上也真是这样,上图的各个继承类的作用如下:

  • DefaultResourceLoader:提供获取配置文件方法 getResource(),返回资源信息 Resource
  • AbstractApplicationContext:提供主要创建容器,初始化对象方法 refresh()
  • AbstractRefreshableApplicationContext: 提供刷新 refreshBeanFactory() 方法,进行BeanFactory 的初始化
  • AbstractRefreshableConfigApplicationContext:保存配置文件信息
  • AbstractXmlApplicationContext:提供 loadBeanDefinitions() 读取BeanDefinitions方法,将资源转换为配置
  • ClassPathXmlApplicationContext:具体实现类,用于定位资源文件
读者可以大致看一下类名,源码分析的时候不至于找不着看哪个类,因为 Spring 为了适应各种使用场景,提供的各个接口都可能有很多的实现类。对于我们来说,就是揪着一个完整的分支看完。

为了更加直观,我们使用一个例子来说明,以下例子来自参考文章[2],我们首先来看一下如何实例化 ApplicationContext 的。

首先定义一个接口:

1
2
3
public interface MessageService {
String getMessage();
}

然后定义其实现类:

1
2
3
4
5
6
public class MessageServiceImpl implements MessageService {

public String getMessage() {
return "hello world";
}
}

接下来,我们在 resources 目录新建一个配置文件,文件名一般叫做 applicationContext.xml,当然名字可以根据自己的习惯任取。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">

<bean id="messageService" class="com.javadoop.example.MessageServiceImpl"/>
</beans>

这样,我们就可以跑起来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class App {
public static void main(String[] args) {
// 用我们的配置文件来启动一个 ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

System.out.println("Spring 容器启动成功");

// 从 context 中取出我们的 Bean,而不是用 new MessageServiceImpl() 这种方式
MessageService messageService = context.getBean(MessageService.class);

// 这句将输出: hello world
System.out.println(messageService.getMessage());
}
}

我们就以 Hello World 引出 IoC 容器的创建和初始化流程。ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等。

BeanFactory

BeanFactory,从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。可别小看了 BeanFactory,如果你对 Spring 有一定了解的话,一定知道 Spring 中用到最多的设计模式就是工厂模式,几乎随处都在使用。而这里的 BeanFactory 就是管理整个 Spring bean 的工厂,这可不就是个厉害角色嘛!

我们来看一下 BeanFactory 的关系图:

我们可以很直观的看到 ApplicationContext 就是继承自 BeanFactory 的,而 ApplicationContext 的继承关系图在上面已经看到过。这张图当然也不是让你记住,我们只需要标出其中几个关键的地方就行了:

  1. ApplicationContext 继承了 ListableBeanFactory,这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。

  2. ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical 单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。

  3. AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。

  4. ConfigurableListableBeanFactory 也是一个特殊的接口,看图,特殊之处在于它继承了第二层所有的三个接口,而 ApplicationContext 没有。这点之后会用到。

  5. 请先不用花时间在其他的接口和类上,先理解我说的这几点就可以了。

为了更加深刻的理解和接下来的阅读,读者最好先翻一下 BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory、ApplicationContext 这几个接口的代码,大概看一下各个接口中的方法,做到心中有底。

启动过程分析

整个过程比较长,看的也比较累,读者最好打开源码进行对比观看,这样理解的会更好一些。

参考文章

  1. Spring官网
  2. SpringIOC容器源码分析
  3. Spring源码分析:Spring IOC容器初始化
  4. Spring IOC 容器源码分析系列文章导读
如果这篇文章对您很有帮助,不妨
-------------    本文结束  感谢您的阅读    -------------
0%