天天看点

spring ioc加载流程源码分析前言正文

前言

spring ioc加载流程源码分析前言正文

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的结构关系:
spring ioc加载流程源码分析前言正文

创建AnnotationConfigApplicationContext对象

spring ioc加载流程源码分析前言正文

1. this()

spring ioc加载流程源码分析前言正文
spring ioc加载流程源码分析前言正文

 1.1 this方法会隐式调用父类的构造方法, 创建了一个DefaultListableBeanFactory

DefaultListableBeanFactory是相当重要的,从字面意思就可以看出它是一个Bean的工厂,什么是Bean的工厂?当然就是用来生产和获得Bean的。 我们看一下这类的关系图

spring ioc加载流程源码分析前言正文

 1.2 创建默认beanDefinition读取器AnnotatedBeanDefinitionReader和扫描器ClassPathBeanDefinitionScanner(ClassPathBeanDefinitionScanner不常用, 业务代码手动调用xxx.doScan()时才会用到的)

  • new AnnotatedBeanDefinitionReader(this)这个方法也很重要, 里面做了很多spring内部的组件注入工作, 创建了一些启动容器的核心组件
spring ioc加载流程源码分析前言正文

这些内部的类是容器的核心; 比如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的核心

spring ioc加载流程源码分析前言正文

容器准备工作, 顾名思义就是准备一些容器启动的计时器, 准备好bean工厂,设置好spring框架中需要加注和需要忽略的组件等

// 刷新预处理, 保存了容器的启动时间,启动标志,环境参数设置验证(给其他容器准备的),准备事件监听器    和早期事件容器
			prepareRefresh();

			// 获得当前beanFactory,(注解方式不做什么事, XML方式会loadBeanDefinitions)
			// 注解方式:DefaultListableBeanFactory, 实现了ConfigurableListableBeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			
			// 准备beanFactory, 填充beanFactory属性, 设置或忽略spring的一下组件
			prepareBeanFactory(beanFactory);
           

至此,容器准备工作都已经完成了.

下一步将是我们spring容器加载最重要的一步  

invokeBeanFactoryPostProcessors(beanFactory)

我们先来看一张草图来混个眼熟,这个图后面会慢慢深入源码补充

spring ioc加载流程源码分析前言正文

代码可以根据流程图或者网上找的图自行debug; 什么.你说为什么要自行debug? spring这个源码本身就是很复杂的, 不可能通过看看文章就可以理解的, 一定要自己去动手, 然后结合别人的理解总结出属于自己的知识. 

这里可以说一下代码中一些需要注意的点, 根据invokeBeanFactoryPostProcessors(beanFactory)我们会追踪到这个方法, 这个方法有两个参数

beanFactory  我们前面this方法创建的 DefaultListableBeanFactory 由关系图我们可以确定会走 if (beanFactory instanceof BeanDefinitionRegistry)      
beanFactoryPostProcessors 这个不是spring自己的, 是留给扩展的, 用户自己手动注入的, 正常没有注入的情况下这里集合是空的; 注入方式: 调用XXXApplicationContext(对应容器).addBeanFactoryPostProcessor(XXX)才会有数据
      
spring ioc加载流程源码分析前言正文

然后会走到  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

spring ioc加载流程源码分析前言正文

ConfigurationClassParser#doProcessConfigurationClass, 这个方法最终会执行解析我们的注解, 如@Component, @PropertySources, @ComponentScans,@Import等等

spring ioc加载流程源码分析前言正文

在解析完之后, 退出到ConfigurationClassPostProcessor#processConfigBeanDefinitions  this.reader.loadBeanDefinitions(configClasses)  将解析的元信息加载成bean定义放到beanBeanDefinitionMap和 beanBeanDefinitionNames

spring ioc加载流程源码分析前言正文

只是粗略的将了个大概, 还有很多重点和细枝末节可能没讲解到

总结来说 invokeBeanFactoryPostProcessors(beanFactory) 主要做的事就是 将需要注入的类注册成beanDefinition

registerBeanPostProcessors(beanFactory);

实例化和注册beanFactory中扩展了BeanPostProcessor的bean

initMessageSource()

初始化国际化资源处理器.      

initApplicationEventMulticaster();

创建事件多播器

onRefresh();

容器刷新处理, 不同容器做不同的事情, 留子类实现      

registerListeners()

注册监听器,广播early application events   事件的单独讲      

finishBeanFactoryInitialization(beanFactory);

这个是也是重点, 实例化所有剩余的(非懒加载)单例; 上述invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化; 上述registerBeanPostProcessors注入的BeanPostProcessor; 在实例化的过程各种BeanPostProcessor开始起作用

spring ioc加载流程源码分析前言正文

注意这个注释,  Instantiate all remaining (non-lazy-init) singletons   简单易懂, 不翻译了, 看注释都知道方法要干吗, 点进去看

spring ioc加载流程源码分析前言正文
这里需要处理实现FactoryBean接口的bean, 返回的实例是getObject()返回的对象, 但是通过 "&"+beanName可以获取到原生的对象      

接下来就是getBean; 核心且复杂,大家先看一个图

spring ioc加载流程源码分析前言正文

是不是看着有点简单, 这个我省略了很多分支细节(老实说,不省略自己都看不懂源码写了什么鬼...)还有跳过了BeanPostProcessors, 先有个大概的印象, getBean后续会在深入源码, 然后单独再拿出来讲, 涉及到内部的三级缓存,循环依赖, AOP和BeanPostProcessors处理器;  绝对配得上一个单独的篇幅(恐怕一篇都讲不透)

finishRefresh()

这个方法没做什么事, 主要清理一些缓存,初始化一个DefaultLifecycleProcessor(生命周期相关的吧,没了解过), 发布容器启动完成事件. 至此IOC容器加载完成.