本文共 3954 字,大约阅读时间需要 13 分钟。
当下,各大框架层出不穷,当我们还没有学完一个框架时,又会有新的框架出现,作为一名合格程序员,我们不应该满足于看着文档搬运API,而应该深入其源码,探究他的底层原理。在这个过程,虽然会很艰难,但你能收获到的不仅是知识,还有更为重要的开发思想,甚至会打破你的三观。
学习源码并不能让你的能力得到立竿见影般的提高,但学习要踏实,学习是需要长期积累的,我们学习应该本着一颗学习某一件事务的思想及其本质的心,而不是仅仅停留在表面,当你能够更加深入理解某一事务的本质,你的能力就会得到很大提高。
如果说要深入研究一个框架的源码,Spring 源码应当是不二之选,Spring 第一个版本在 2004 年发布,到现在已经更新到了第五个版本,依旧如火如荼,Spring 源码不仅是一种规范,更是众多优秀思想的集合,从中,定会让你受益匪浅。
所以我想写一个关于 Spring 源码的专栏,对 Spring 源码进行学习上的总结,但这只是我个人的理解,可能入不了很多大神的法眼,可能我写的很菜,但我承认我菜,因为学习本来就是闻道有先后,术业有专攻。也可能会有一些歧义,因为每个人的理解不同,一千个读者就有一千个哈姆雷特对吧,但学习的本质是学习一件事物的思想,而不是抓牛角尖,所以,作为一名优秀的程序猿,要学会取长补短。
何为Spring 容器,Spring 容器也就是我们所说的 Spring 的环境,Spring的上下文,Spring容器又可以分成低级容器 BeanFactory,高级容器 ApplicationContext,BeanFactory 顾名思义就是一个 Bean 的工厂,它负责创建 Bean 和管理 Bean,他可以简单完成 Spring IOC 的功能,ApplicationContext 是对 BeanFactory的扩展,它扩展了很多新的功能,如 Bean 的扫描与注册,Bean 的生命周期的管理,以及 Spring 的各大扩展点和后置处理器等等。
我们可以先看一张思维导图,这是 Spring 容器的大致思维导图,在线地址效果更佳,注释更全。
在我们构建完成 Spring源码后,在进行研究之前,我们可以先进行以下准备,也就是实现 Spring 容器的初始化。代码如下:
@Configuration@ComponentScan("com.javahly.spring")public class AppConfig {}
@Componentpublic 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 会调用 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 ,BeanDefinitionprivate final MapbeanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order *///放了所有的Bean的名字private volatile ListbeanDefinitionNames = new ArrayList<>(256);
一个是Map,一个是List,存放了 Bean 的定义,何为 Bean 的定义,就是存放一个 Bean 的信息,BeanDefinition 是一个接口,由来描述 Bean 的信息,为什么要这么定义。我们可以想一下,Java 使用 Class 来描述一个类,类的属性,方法,构造函数,但这个类是不是还有其父类,作用域等等,Class 已经无法描述了,所以 Spring 定义了这么一个数据结构来进行描述,比如描述他的 ParentName,BeanClassName,Scope,LazyInit,DependsOn,isPrimary,so on。
在在成其父类的初始化之后,在 AnnotationConfigApplicationContext 这个类的构造函数里就会进行重要的四步:
1、AnnotatedBeanDefinitionReader,顾名思义,这是一个读取器,用来读取加了注解的 bean 。
this.reader = new AnnotatedBeanDefinitionReader(this);
在这个类初始化后,会注册 6 各类,也可以是说是创世纪的 6 各类,其中有一个类 非常重要,ConfigurationClassPostProcessor,它的类型是 的类型是 BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor 最终实现 BeanFactoryPostProcessor 这个后置处理器,也是一个扩展点,它完成了 Bean 的扫描与注册,解析各种 Import,等等。它是在 refresh 方法的 invokeBeanFactoryPostProcessors 方法中调用的,我们从可以从思维导图中看到。
ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessorRequiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorEventListenerMethodProcessorDefaultEventListenerFactory
2、ClassPathBeanDefinitionScanner ,这个一个扫描器,用来扫描包或者类,继而读取类转化成 BeanDefinition,但真正的扫描和初始化不是在这里完成的,这里仅提供了一个方法 scan(),程序员可以手动调用进行扫描。
3、register 这是一个注册器,可以进行注册,我们可以传进一个配置类或者普通类,把他转化为 BeanDefinition,然后放到 beanDefinitionMap 里面去。
4、refresh 这个方法是最重要的方法,没有之一,在这个方法里面又有 12 个方法,分别完成了 Bean 的扫描,注册,实例化,初始化,销毁,生命周期的管理,后置处理器的注册等等,后面我们会展开来说。我们可以从思维导图中进行预览,思维导图里还有各个类的具体注释,各位胖友可以点击下方链接在线预览。
为了更加直观的展现源码的调用过程,我绘制了一张思维导图!
公众号:【星尘Pro】
github:
推荐阅读
转载地址:http://mdfsi.baihongyu.com/