精通 Spring 源码 (一) | 剑指 Spring 源码

一、前言

当下,各大框架层出不穷,当我们还没有学完一个框架时,又会有新的框架出现,作为一名合格程序员,我们不应该满足于看着文档搬运API,而应该深入其源码,探究他的底层原理。在这个过程,虽然会很艰难,但你能收获到的不仅是知识,还有更为重要的开发思想,甚至会打破你的三观。

学习源码并不能让你的能力得到立竿见影般的提高,但学习要踏实,学习是需要长期积累的,我们学习应该本着一颗学习某一件事务的思想及其本质的心,而不是仅仅停留在表面,当你能够更加深入理解某一事务的本质,你的能力就会得到很大提高。

如果说要深入研究一个框架的源码,Spring 源码应当是不二之选,Spring 第一个版本在 2004 年发布,到现在已经更新到了第五个版本,依旧如火如荼,Spring 源码不仅是一种规范,更是众多优秀思想的集合,从中,定会让你受益匪浅。

所以我想写一个关于 Spring 源码的专栏,对 Spring 源码进行学习上的总结,但这只是我个人的理解,可能入不了很多大神的法眼,可能我写的很菜,但我承认我菜,因为学习本来就是闻道有先后,术业有专攻。也可能会有一些歧义,因为每个人的理解不同,一千个读者就有一千个哈姆雷特对吧,但学习的本质是学习一件事物的思想,而不是抓牛角尖,所以,作为一名优秀的程序猿,要学会取长补短。

二、源码分析

1、Spring 容器简介

何为Spring 容器,Spring 容器也就是我们所说的 Spring 的环境,Spring的上下文,Spring容器又可以分成低级容器 BeanFactory,高级容器 ApplicationContext,BeanFactory 顾名思义就是一个 Bean 的工厂,它负责创建 Bean 和管理 Bean,他可以简单完成 Spring IOC 的功能,ApplicationContext 是对 BeanFactory的扩展,它扩展了很多新的功能,如 Bean 的扫描与注册,Bean 的生命周期的管理,以及 Spring 的各大扩展点和后置处理器等等。

我们可以先看一张思维导图,这是 Spring 容器的大致思维导图,在线地址效果更佳,注释更全。

点击观看本章 Spring 源码分析思维导图

精通 Spring 源码 (一) | 剑指 Spring 源码_第1张图片

2、Spring 调试准备

在我们构建完成 Spring源码后,在进行研究之前,我们可以先进行以下准备,也就是实现 Spring 容器的初始化。代码如下:

@Configuration
@ComponentScan("com.javahly.spring")
public class AppConfig {
}
@Component
public class IndexService {
	public void  query(){
		System.out.println("query......");
	}
}
public class Test {
	public static void main(String[] args){
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.register(AppConfig.class);
		applicationContext.refresh();
		IndexService service = (IndexService) applicationContext.getBean("indexService");
		service.query();
	}
}

这里简单些了三个类,然后再运行我们的测试类,这样 Spring 容器就初始化完成了。下面我们将根据我们的思维导图进行分析 Spring 源码的实现流程,为了加强文章的可读性,就不贴大量的源码了,各位看官可以根据初始化代码和思维大图,按着流程一步一步调试,就能看到详细源码。

精通 Spring 源码 (一) | 剑指 Spring 源码_第2张图片

3、Spring 容器的初始化

Spring 会调用 AnnotationConfigApplicationContext 的构造函数进行初始化,这个类提供有参的构造函数和无参的构造函数,上面的测试类提的是无参的构造函数,如果是有参的构造函数,我们可以这样写

nnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

这样我们就不需要调用 register 手动注册配置类 AppConfig 了。

在我们调用 AnnotationConfigApplicationContext 构造函数之前,会先调用其父类的构造函数 GenericApplicationContext 去初始化一个 Bean工厂,因为你要生产 Bean,肯定先需要一个工厂,以下是源码:

public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
}

在 DefaultListableBeanFactory 这个类里面,有几个重要的属性,beanDefinitionMap,beanDefinitionNames。

/** Map of bean definition objects, keyed by bean name */
//放了类的描述,beanName ,BeanDefinition
private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order */
//放了所有的Bean的名字
private volatile List beanDefinitionNames = new ArrayList<>(256);

一个是Map,一个是List,存放了 Bean 的定义,何为 Bean 的定义,就是存放一个 Bean 的信息,BeanDefinition 是一个接口,由来描述 Bean 的信息,为什么要这么定义。我们可以想一下,Java 使用 Class 来描述一个类,类的属性,方法,构造函数,但这个类是不是还有其父类,作用域等等,Class 已经无法描述了,所以 Spring 定义了这么一个数据结构来进行描述,比如描述他的 ParentName,BeanClassName,Scope,LazyInit,DependsOn,isPrimary,so on。

4、Bean 的扫描与注册

在在成其父类的初始化之后,在 AnnotationConfigApplicationContext 这个类的构造函数里就会进行重要的四步:

1、AnnotatedBeanDefinitionReader,顾名思义,这是一个读取器,用来读取加了注解的 bean 。

this.reader = new AnnotatedBeanDefinitionReader(this);

在这个类初始化后,会注册 6 各类,也可以是说是创世纪的 6 各类,其中有一个类 非常重要,ConfigurationClassPostProcessor,它的类型是 的类型是 BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor 最终实现 BeanFactoryPostProcessor 这个后置处理器,也是一个扩展点,它完成了 Bean 的扫描与注册,解析各种 Import,等等。它是在 refresh 方法的 invokeBeanFactoryPostProcessors 方法中调用的,我们从可以从思维导图中看到。

ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
EventListenerMethodProcessor
DefaultEventListenerFactory

2、ClassPathBeanDefinitionScanner ,这个一个扫描器,用来扫描包或者类,继而读取类转化成 BeanDefinition,但真正的扫描和初始化不是在这里完成的,这里仅提供了一个方法 scan(),程序员可以手动调用进行扫描。

3、register 这是一个注册器,可以进行注册,我们可以传进一个配置类或者普通类,把他转化为 BeanDefinition,然后放到 beanDefinitionMap 里面去。

4、refresh 这个方法是最重要的方法,没有之一,在这个方法里面又有 12 个方法,分别完成了 Bean 的扫描,注册,实例化,初始化,销毁,生命周期的管理,后置处理器的注册等等,后面我们会展开来说。我们可以从思维导图中进行预览,思维导图里还有各个类的具体注释,各位胖友可以点击下方链接在线预览。

精通 Spring 源码 (一) | 剑指 Spring 源码_第3张图片

三、结语

为了更加直观的展现源码的调用过程,我绘制了一张思维导图!
为了更加直观的展现源码的调用过程,我绘制了一张思维导图!
为了更加直观的展现源码的调用过程,我绘制了一张思维导图!

Spring 思维导图

我的 Github:Github
CSDN: CSDN
个人网站: sirius 的博客
E-mail: [email protected]

推荐阅读
史上最全,最完美的 JAVA 技术体系思维导图总结,没有之一!

你可能感兴趣的:(Spring,源码解析,精通,Spring,源码)