Spring IOC 容器 bean 加载过程

2020/10/21 posted in  Spring

本文主要内容:分析 Spring 加载 bean 的过程,重点分析下 Spring 解决循环依赖的三级缓存。

refresh方法

IoC 初始化过程中核心方法 refresh :

org.springframework.context.support.AbstractApplicationContext#refresh :

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		// 刷新上下文
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		// 在子类中启动 refreshBeanFactory。 该方法执行完成之后,xml中的bean已经加载完成,但是 注解方式的bean 在
		// invokeBeanFactoryPostProcessors 加载
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			// 设置BeanFactory的后置处理
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			// 调用BeanFactory的后处理器,这些后处理器是在Bean定义中项容器注册的,这里面会实例化 后置处理器的bean对象
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			// 注册Bean的后处理器,在Bean创建过程中调用
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			// 对上下文中的消息源进行初始化
			initMessageSource();

			// Initialize event multicaster for this context.
			// 初始化上下文中的事件机制
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			// 初始化其他特殊Bean
			onRefresh();

			// Check for listener beans and register them.
			// 检查监听Bean并且将这些Bean向容器注册
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			// 实例化所有的单例bean
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			// 发布容器事件,结束Refresh过程。
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			// 为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单例bean
			destroyBeans();

			// Reset 'active' flag.
			// 重置 active 标志
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

bean 加载过程分析

xml 方式(ClassPathXmlApplicationContext) 和 注解方式(AnnotationConfigApplicationContext) 启动 Spring 容器,bean 加载过程处理方式不太一样

xml 方式

xml 方式加载过程主要是 obtainFreshBeanFactory 这个方法,先通过文件流读取 xml 文件中的信息,loadBeanDefinitions 方法解析 xml 文件,将其中的内容加载成 beanDefinition,放到 beanDefinitionMap 中,通过 BeanFactory.getBean 方法生产bean

注解方式

注解方式加载过程主要是 invokeBeanFactoryPostProcessors 这个方法,AnnotationConfigApplicationContext 在 new AnnotatedBeanDefinitionReader 的时候会注册一些 bean 后置处理器

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor

通过 BeanFactory.getBean 方法生产bean

BeanFactory.getBean

BeanFactory 中最重要的方法就是 getBean,这个方法既可以获取 bean、也可以生产 bean,获取 bean 是从缓存中去拿,如果缓存中没有在去生产 bean 。

三级缓存解决循环依赖

假设这么一个场景

class A{
	@Autowired
	private B b;

}

class B{
	@Autowired
	private A a;
}

A 依赖 B,B 又依赖 A,但是 Spring 却没有出现 循环依赖,其解决方式是通过 三个缓存 map 。

一级缓存,存的是实例化、初始化完成的 bean,最终的 bean,可以直接拿来用的
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存,存放的是完成实例化,但是还未进行初始化的 bean
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存,进入实例化阶段的单例 bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

通过一个流程图来看下 A、B 两个类在 IOC 加载创建 bean 的流程,注意,图中省略了好多步骤、细节部分,但是不影响主流程。

Spring-IOC16032812571910
Spring-IOC16032812571910

竖向表示当前方法的执行顺序,横向表示进入到该方法。

这个流程图分为了 三列,第一列创建 bean A 的流程,在创建 A 的时候发现要依赖 B,第二列就是创建 bena B 的流程,在创建 B 的时候又发现依赖 A,第三列就是 创建 B 的时候 创建 A 的流程。

图中用颜色标注的是表示用到三级缓存的地方。

  1. 第一个是在获取缓存的方法 getSingleton 中,先从第一级缓存去查找,如果没有则去第二级缓存中查找,没有再去第三级缓存中查找,如果第三级缓存中查到了,将其从第三级缓存中删掉,放到第二级缓存中。
  2. 第二个用到缓存的地方是 doCreateBean 中,bean 通过反射获取到实例后,先放入到第三级缓存中
  3. 第三个是 doCreateBean 完成后,bean 都初始化好,放入到第一级缓存中

创建 A 的时候发现要依赖 B,于是递归调用 getBean(B) 去创建 B,这个时候三个缓存中只有第三级缓存中有 A

创建 B 的时候发现要依赖 A,于是递归调用 getBean(A)去创建 A ,这个时候第三级缓存中有 A、B,于是从第三级缓存中取出 bean A,放到二级缓存中去,接着执行创建 B 的流程,B 初始化完成后,将 B 放到第一级缓存

然后在执行创建 A 的流程,初始化实例 A,完事后将 A 也加入到第一级缓存中。

每个步骤对应的三个缓存中的内容:

以上;