spring源码解析(一)spring容器启动的十六个步骤
前序
spring 是java技术系列公认的最优秀的源码,甚至没有之一 。。。那么我们平时在用到spring框架大部分都是会使用注解或者配置的方式。。但是具体其中的原理也都是似懂非懂,包括本人在内,之前只会使用一些spring的注解或者配置方式,又或者是会使用spring的扩展机制来做一些高级功能,,,学习原理最好的方式还是来读懂并且调试spring源码最直接。。。同时对于我们以后去读其他开源代码也有巨大帮助,,,小编觉得spring源码的思想被很多开源代码所采纳。。故可举一反三。。。本文将简单介绍spring启动的十六个步骤,,后续的文章会介绍每一个模块的作用
为什么要学习spring源码:
- 深入学习过源码后,自己在使用的时候显得更游刃有余。
- 可以学习到一些优秀的代码,比如设计模式和代码整洁。
- 许多中间件如dubbo都会基于 Spring 的扩展功能来实现,阅读 Spring 源码,能帮助你更好的阅读中间件源码。
- 应付面试。
怎么学习spring的源码:
1.要有耐心,,我们可以先弄懂spring大概的生命周期。。然后针对每一个步骤逐个突破,,,
2.有些部分,可能你看到会有疑惑,没有关系,小编觉得要硬着头皮看下去,因为可能你看到后面某个点,对前面的就理解了。
3.我们尽量在学习源码的时候加上我们自己的注释,,,这样有利于我们之后再去理解,复习。。。
4.如果在阅读过程中有不理解的,可以debug 来调试,这样直观且容易记住过程。。。
正文
通常我们启动一个spring容器有两种方式,,
第一种是传统的spring启动方式-利用web.xml来启动容器。。
如:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener 是实现了 javax.servlet.ServletContextListener 接口的服务器端程序,随 web 应用的启动而启动,只初始化一次,随 web 应用的停止而销毁。在web应用启动的时候会调用 contextInitialized 方法,停止的时候会调用 contextDestroyed 方法。
第二种就是springboot的启动方式如下:
我们点击new AnnotationConfigApplicationContext源码可以发现这三步:
第一步:调用的是无参构造方法AnnotationConfigApplicationContext如下
在构造AnnotationConfigApplicationContext时,会先调用父类GenericApplicationContext的构造方法,GenericApplicationContext会初始化一个beanFactory,默认值为DefaultListableBeanFactory,如下:
从中我们可以看出,这个方法就是new 一个DefaultListableBeanFactory,然后会在里面存一个beanDefinitionMap,根据名字可以看出,它放置的是一些关于注解的默认的后置处理器的beandefinition...
AnnotationConfigApplicationContext的类结构图
- new RootBeanDefinition(ConfigurationClassPostProcessor.class);
- new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
- new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
- new RootBeanDefinition(EventListenerMethodProcessor.class);
- new RootBeanDefinition(DefaultEventListenerFactory.class);
重点关注ConfigurationClassPostProcessor和AutowiredAnnotationBeanPostProcessor。
RootBeanDefinition的类结构图
第二步:构造ClassPathBeanDefinitionScanner
在生成scanner的过程中,主要会注册几个默认的includeFilters:
- new AnnotationTypeFilter(Component.class)
- new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean"))
- new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named"))
重点关注AnnotationTypeFilter(Component.class),它是用来判断一个类是不是应该被Spring扫描到的
第三步:注册自己的配置类到容器中:
debug 发现:
从代码中可以看到,,是将我自己的mainconfig这个类的beandefiniton注册到bean工厂中。。。
最终我们来到了refresh方法,如下:
第四步:用startupShutdownMonitor来加一把锁:用于“刷新”和“销毁”的同步监视器
第五步:调用prepareRefresh方法
为刷新准备新的上下文环境,设置其启动日期和活动标志以及执行一些属性的初始化。主要是一些准备工作,不是很重要的方法,可以先不关注。
第六步:调用obtainFreshBeanFactory方法
用于获得一个新的 BeanFactory。
该方法会解析所有 Spring 配置文件(通常我们会放在 resources 目录下),将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory 中。常见的,如果解析到<context:component-scan base-package="com.." /> 注解时,会扫描 base-package 指定的目录,将该目录下使用指定注解(@Controller、@Service、@Component、@Repository)的 bean 定义也同样封装成 BeanDefinition,加载到 BeanFactory 中。
这里 如果是xml中配置的bean,那么对应的beandefinition就是GenericBeanDefinition 而使用scan扫描到的注解的类对应的beandefinition就是 AnnotatedGenericBeanDefinition,AnnotatedGenericBeanDefinition 是GenericBeanDefinition 的扩展。。
从这里我们可以看出注解是xml的扩展机制。。。
上面提到的“加载到 BeanFactory 中”的内容主要指的是以下3个缓存:
beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合。
beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射。
aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。
第七步:调用prepareBeanFactory(beanFactory) 方法
配置 beanFactory 的标准上下文,例如上下文的 ClassLoader、后置处理器等。这个方法会注册3个默认环境 bean:environment、systemProperties 和 systemEnvironment,注册 2 个 bean 后置处理器:ApplicationContextAwareProcessor 和 ApplicationListenerDetector
第八步:调用postProcessBeanFactory(beanFactory) 方法
这个方法允许子类对 BeanFactory 进行后续处理,默认实现为空,留给子类实现。
第九步:调用invokeBeanFactoryPostProcessors(beanFactory) 方法
这里只截取了一部分代码。。。。
实例化和调用所有 BeanFactoryPostProcessor,包括其子类 BeanDefinitionRegistryPostProcessor。
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。
BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的 BeanFactoryPostProcessor **之前注册一些 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。
这里的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。
第十步:调用registerBeanPostProcessors(beanFactory) 方法
注册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
BeanPostProcessor 接口是 Spring 初始化 bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。在这边只是注册到 BeanFactory 中,具体调用是在 bean 初始化的时候。
具体的:在所有 bean 实例化时,执行初始化方法前会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法,执行初始化方法后会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。
第十一步:调用initMessageSource() 方法
初始化消息资源 MessageSource。
第十二步:调用initApplicationEventMulticaster() 方法
初始化应用的事件广播器 ApplicationEventMulticaster 用来保存第十四步的listener
第十三步:调用onRefresh() 方法
该方法为模板方法,提供给子类扩展实现,可以重写以添加特定于上下文的刷新工作,默认实现为空。
第十四步:调用registerListeners() 方法
注册监听器。
第十五步:调用finishBeanFactoryInitialization(beanFactory) 方法
这一步非常重要,,,也是spring的核心源码。。。。
该方法会实例化所有剩余的非懒加载单例 bean。除了一些内部的 bean、实现了 BeanFactoryPostProcessor 接口的 bean、实现了 BeanPostProcessor 接口的 bean,其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 的触发也是在这个方法中。
1、遍历所有被加载到缓存中的 beanName,触发所有剩余的非懒加载单例 bean 的实例化。
2、首先通过 beanName 尝试从缓存中获取,如果存在则跳过实例化过程;否则,进行 bean 的实例化。
3、根据 BeanDefinition,使用构造函数创建 bean 实例。
4、根据 BeanDefinition,进行 bean 实例属性填充。
5、执行 bean 实例的初始化。
5.1、触发 Aware 方法。
5.2、触发 BeanPostProcessor 的 postProcessBeforeInitialization 方法。
5.3、如果 bean 实现了 InitializingBean 接口,则触发 afterPropertiesSet() 方法。
5.4、如果 bean 设置了 init-method 属性,则触发 init-method 指定的方法。
5.5、触发 BeanPostProcessor 的 postProcessAfterInitialization 方法。
6、将创建好的 bean 实例放到缓存中,用于之后使用。
第十六步:调用finishRefresh() 方法
完成此上下文的刷新,主要是推送上下文刷新完毕事件(ContextRefreshedEvent )到监听器。
到此为止,,spring容器所有的启动过程已介绍完毕。。。。后续小编会逐个突破每一部分的源码。。。