前言
IOC有多重要, 网络上找的图, 感觉非常形象
spring源码地址 https://github.com/spring-projects/spring-framework
建议选择Tags, 版本选最新的RELEASE版即可
本文版本5.2.7.RELEASE
源码下下来之后编译好,编译过程中可能会出现问题,针对问题搜索引擎解决.
本文介绍的内容为 容器加载工作,正文内容流程图是本人自己画的, 理解的还不是很深; 如有不对之处多多指点
正文
新建一个自己的模块用于调试
这里我们采用注解方式的上下文启动容器
创建配置类 IocConfig
@Configuration
@ComponentScan({"org.springframework.dmy.ioc"})
@Import(MyImportBeanDefinitionRegister.class)
public class IocConfig {
@Bean
public MyListener myListener(){
return new MyListener();
}
}
@Component
public class Car {
}
public class IocMain {
public static void main(String[] args) {
// 启动上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IocConfig.class);
context.getBean("car");
}
}
启动类 IocMain
- AnnotationConfigApplicationContext的结构关系:
创建AnnotationConfigApplicationContext对象
1. this()
1.1 this方法会隐式调用父类的构造方法, 创建了一个DefaultListableBeanFactory
DefaultListableBeanFactory是相当重要的,从字面意思就可以看出它是一个Bean的工厂,什么是Bean的工厂?当然就是用来生产和获得Bean的。 我们看一下这类的关系图
1.2 创建默认beanDefinition读取器AnnotatedBeanDefinitionReader和扫描器ClassPathBeanDefinitionScanner(ClassPathBeanDefinitionScanner不常用, 业务代码手动调用xxx.doScan()时才会用到的)
- new AnnotatedBeanDefinitionReader(this)这个方法也很重要, 里面做了很多spring内部的组件注入工作, 创建了一些启动容器的核心组件
这些内部的类是容器的核心; 比如ConfigurationClassPostProcessor 这个类是用来加载我们注解的,我们业务中标注的组件,配置,Bean,Import都是通过这个类来解析的;
还有EventListenerMethodProcessor和DefaultEventListenerFactory,看类名都知道是处理Listener相关的; 注册了一个默认的事件工厂和方法事件的处理器
这里插一个事件相关的. 可能有人不清楚方法注解类的事件, 这里将接口方式和方法注解方式分别做个展示
public class MyListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("----------接口监听: "+ event);
}
}
/**
* 功能说明
*
* org.springframework.context.event.EventListenerMethodProcessor#isSpringContainerClass(java.lang.Class)
* 注解类监听器, 类必须要注入到容器里
*/
@Component
public class TestListener {
@EventListener
public void onTestEvent(ApplicationEvent event) {
System.out.println("----------注解监听: "+ event);
}
}
调用方式不同, 接口类的是通过找实现类循环调用方法, 方法注解类的是通过反射调用, 事件不是今天的核心, 有时间再单独讲一篇
到这里, 我们已经将 创建AnnotationConfigApplicationContext对象 中的this() 大致分析完了; 相信很少有人这个重点来分析这个this()方法, 都是重点分析refresh()
2. register(componentClasses)
这个方法做的是就是将我们的启动时添加的配置类注册到容器的BeanDefinitionMap
这里为什么要注册到BeanDefinitionMap? 因为后面容器要解析这个配置类的信息,如类注解, 通过注解容器才知道要扫描哪些类,做什么操作等等.
3. refresh()
refresh是容器加载的核心, 也是IOC的核心
容器准备工作, 顾名思义就是准备一些容器启动的计时器, 准备好bean工厂,设置好spring框架中需要加注和需要忽略的组件等
// 刷新预处理, 保存了容器的启动时间,启动标志,环境参数设置验证(给其他容器准备的),准备事件监听器 和早期事件容器
prepareRefresh();
// 获得当前beanFactory,(注解方式不做什么事, XML方式会loadBeanDefinitions)
// 注解方式:DefaultListableBeanFactory, 实现了ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备beanFactory, 填充beanFactory属性, 设置或忽略spring的一下组件
prepareBeanFactory(beanFactory);
至此,容器准备工作都已经完成了.
下一步将是我们spring容器加载最重要的一步
invokeBeanFactoryPostProcessors(beanFactory)
我们先来看一张草图来混个眼熟,这个图后面会慢慢深入源码补充
代码可以根据流程图或者网上找的图自行debug; 什么.你说为什么要自行debug? spring这个源码本身就是很复杂的, 不可能通过看看文章就可以理解的, 一定要自己去动手, 然后结合别人的理解总结出属于自己的知识.
这里可以说一下代码中一些需要注意的点, 根据invokeBeanFactoryPostProcessors(beanFactory)我们会追踪到这个方法, 这个方法有两个参数
beanFactory 我们前面this方法创建的 DefaultListableBeanFactory 由关系图我们可以确定会走 if (beanFactory instanceof BeanDefinitionRegistry)
beanFactoryPostProcessors 这个不是spring自己的, 是留给扩展的, 用户自己手动注入的, 正常没有注入的情况下这里集合是空的; 注入方式: 调用XXXApplicationContext(对应容器).addBeanFactoryPostProcessor(XXX)才会有数据
然后会走到 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 在beanFactory获取 BeanDefinitionRegistryPostProcessor 类型的beanName; 此时也只有一个, 之前说在创建 this方法中 会创建bean定义读取器new AnnotatedBeanDefinitionReader()的这个类中会创建一些spring内部的组件
其中ConfigurationClassPostProcesso就会被获取到, 然后通过getBean()创建该组件的实例;然后调用
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 可以理解为执行 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方; 这里也会解析我们的组件类
其实后面的代码也是重复调用, 按照优先级, 先调用实现了PriorityOrdered 然后调用Ordered 最后调用没有实现排序的 BeanDefinitionRegistryPostProcessor
最后在调用一下上述方法中所有的BeanDefinitionRegistryPostProcessor的 postProcessBeanFactory(BeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor)方法
我们先回到 第一次调用invokeBeanDefinitionRegistryPostProcessors 最终会走到
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
->org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
->org.springframework.context.annotation.ConfigurationClassParser#parse
->org.springframework.context.annotation.ConfigurationClassParser#parse
->org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
ConfigurationClassParser#doProcessConfigurationClass, 这个方法最终会执行解析我们的注解, 如@Component, @PropertySources, @ComponentScans,@Import等等
在解析完之后, 退出到ConfigurationClassPostProcessor#processConfigBeanDefinitions this.reader.loadBeanDefinitions(configClasses) 将解析的元信息加载成bean定义放到beanBeanDefinitionMap和 beanBeanDefinitionNames
只是粗略的将了个大概, 还有很多重点和细枝末节可能没讲解到
总结来说 invokeBeanFactoryPostProcessors(beanFactory) 主要做的事就是 将需要注入的类注册成beanDefinition
registerBeanPostProcessors(beanFactory);
实例化和注册beanFactory中扩展了BeanPostProcessor的bean
initMessageSource()
初始化国际化资源处理器.
initApplicationEventMulticaster();
创建事件多播器
onRefresh();
容器刷新处理, 不同容器做不同的事情, 留子类实现
registerListeners()
注册监听器,广播early application events 事件的单独讲
finishBeanFactoryInitialization(beanFactory);
这个是也是重点, 实例化所有剩余的(非懒加载)单例; 上述invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化; 上述registerBeanPostProcessors注入的BeanPostProcessor; 在实例化的过程各种BeanPostProcessor开始起作用
注意这个注释, Instantiate all remaining (non-lazy-init) singletons 简单易懂, 不翻译了, 看注释都知道方法要干吗, 点进去看
这里需要处理实现FactoryBean接口的bean, 返回的实例是getObject()返回的对象, 但是通过 "&"+beanName可以获取到原生的对象
接下来就是getBean; 核心且复杂,大家先看一个图
是不是看着有点简单, 这个我省略了很多分支细节(老实说,不省略自己都看不懂源码写了什么鬼...)还有跳过了BeanPostProcessors, 先有个大概的印象, getBean后续会在深入源码, 然后单独再拿出来讲, 涉及到内部的三级缓存,循环依赖, AOP和BeanPostProcessors处理器; 绝对配得上一个单独的篇幅(恐怕一篇都讲不透)
finishRefresh()
这个方法没做什么事, 主要清理一些缓存,初始化一个DefaultLifecycleProcessor(生命周期相关的吧,没了解过), 发布容器启动完成事件. 至此IOC容器加载完成.