面试常见问题丨深入解析Spring中Bean的整个初始化过程
原创 千锋侯哥
一. 前言
在面试过程中,经常有小伙伴会被问到Spring中Bean的生命周期,如果对Spring不了解可能对此类问题难以下手,就算通过百度查询到答案,也可能因为不理解而难以记忆,过段时间又忘记了,那么今天千锋侯哥就带小伙伴深入的解析下Spring中Bean的整个初始化过程。
二. IOC容器的初始化过程
我们都知道,IOC容器初始化时会进行各种Bean的初始化(单例非懒加载),因此在了解Bean的生命周期之前,我们先来看一下IOC容器的整个初始化过程。
我们先整体看下流程图,做到心中有数,理解无误。

2.1 开始初始化IOC容器
初始化IOC容器 - 基于常规的注解式容器(AnnotationConfig ApplicationContext)
//1、初始化IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class);
AppConfiguration为自定义的一个主配置类,代码如下:
//主配置类
@Configuration
//扫描包路径
@ComponentScan("com.qf")
public class AppConfiguration {}
当前实际开发过程中,IOC容器肯定是随着Web服务器启动而启动的。下图是AnnotationConfigApplicationContext容器的其中一个构造方法(基于主配置类的容器创建)

2.2 BeanFactory的创建

SpringIOC容器初始化时,会在ApplicationContext内部创建一个BeanFactory对象。
代码在AnnotationConfigApplicationContext的父类GenericApplicationContext中
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {private final DefaultListableBeanFactory beanFactory;....../*** 创建一个BeanFacotry对象,实现类为DefaultListableBeanFactory*/public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();}......
}
再来看下DefaultListableBeanFactory中的核心集合部分,这些集合主要保存Bean的描述信息(BeanDefinition)。当然这个时候,这些集合都是空的。

2.3 主配置类的加载

加载的代码较为复杂,这里就不给出了,小伙伴理解为主配置被作为普通的Bean对象,放入刚创建好的BeanFactory里就可以了,此时只是注册了Bean的信息,还未初始化为对象。
@Override
public void register(Class>... componentClasses) {Assert.notEmpty(componentClasses, "At least one component class must be specified");StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));this.reader.register(componentClasses);registerComponentClass.end();
}
2.4 BeanFactory的后置处理

这一步至关重要,其中有个BeanFactory的后置处理器 - ConfigurationClass PostProcessor,从名称可以看出,该处理器是为了初始化配置类而生的。上一步中注册到BeanFactory的主配置类,将会被这个后置处理器初始化为Bean对象,并且处理配置类上的@ComponentScan("com.qf")注解,进行其他组件的扫描,扫描的部分核心代码如下:
//基于包路径,扫描路径下的所有Bean,并且返回这些Bean的基本信息(BeanDefinition)集合
protected Set doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set beanDefinitions = new LinkedHashSet<>();//循环需要扫描的包for (String basePackage : basePackages) {//依次解析指定包下的各种Bean组件,返回BeanDefintion集合Set candidates = findCandidateComponents(basePackage);//再次完善各个Bean组件的基本信息for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);//设置Bean的作用域类型candidate.setScope(scopeMetadata.getScopeName());//获取Bean的名称String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//检查当前bean是否已经注册过,如果没有注册,则继续if (checkCandidate(beanName, candidate)) {//将BeanDefinition对象包装成BeanDefinitionHolder对象BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//放入集合beanDefinitions.add(definitionHolder);//并且将当前BeanDefinition对象注册到IOC容器中registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}
2.5 BeanFactory中其他Bean的初始化

上一步中,已经通过主配置类的扫描将各种业务Bean注册到BeanFactory了,接下来就是执行各种Bean的初始化流程了,这个过程也就是Bean的生命周期执行过程,我们稍后再来解析。
2.6 BeanFactory初始化完成
Bean初始化完成后,整个BeanFactory也就初始化完成了,IOC容器也就完成,相关数据集合也填充完毕,等待后续的业务执行。

三、Bean的初始化过程(生命周期)
接下来我们重点来看下Bean是如何初始化的,执行实际也就是上一节的第5步。当然,这个过程主要是初始化那些单例并且非懒加载的Bean,懒加载的Bean和原型的Bean是后续需要用到该Bean时才进行初始化(比如手动调用getBean等),此时才并未进行完整的初始化,暂且不讨论。
3.1 Bean初始化的流程图
老规矩,先看整体流程(红框部分)

因为后续的内容会用到,所以这里要跟大家区分两个概念:
-
Bean的创建
-
Bean的初始化
所谓的Bean的创建,其实就是指Bean对象通过构造方法,在堆内存中被创建出来的过程,此时站在Java的角度,该对象已经完成了初始化,但是内部的属性只拥有默认值,没有任何业务意义。
所谓的Bean的初始化,是站在Spring的角度,还需要对已经创建好的Bean进行后续的一些操作,比如依赖注入之类的,这个过程会给Bean进行一些业务操作,成为一个真正的"成品",可以即拿即用。而这个过程正是Bean的生命周期过程。
3.2 单例Bean初始化入口
接下来我们看下初始化Bean的核心方法,从名字就可以看出,完成BeanFactory的初始化,注释意为,初始化所有的非懒加载单例Bean。

其中的核心调用方法,初始化所有单例Bean
@Override
public void preInstantiateSingletons() throws BeansException {....//获取前面注册过的所有Bean的名称,转储到一个新集合List beanNames = new ArrayList<>(this.beanDefinitionNames);//循环所有Bean的名称,依次处理for (String beanName : beanNames) {//根据Bean的名称获取Bean的基本信息(BeanDefinition)RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);//判断是否为非抽象、单例、非懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//判断是否为FactoryBean对象,if (isFactoryBean(beanName)) {//处理FactoryBean的初始化}else {//普通Bean的初始化过程.....getBean(beanName);}}}// 触发部分Bean的初始化的回调方法//.....
}
3.3 单例Bean的创建(基于反射)
getBean方法,是通过Bean的名称获取Bean对象,该方法中就会判断IOC容器中是否存在Bean对象,如果不存在就会执行初始化流程,核心方法如下:

//该方法在getBean方法中被调用
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {//获得bean的名称String beanName = transformedBeanName(name);Object beanInstance;//从缓存中获取单例Bean的缓存(为了解决循环依赖的设计)Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {//从缓存已经获取缓存的Bean对象的处理逻辑......} else {//缓存中没有找到单例Bean对象的处理逻辑if (mbd.isSingleton()) {//单例Bean的初始化逻辑【核心】sharedInstance = getSingleton(beanName, () -> {try {//创建Bean对象,底层通过反射实现,具体分析见下方return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {//原型模式的处理逻辑,IOC容器初始化时并不会触发原型模式的初始化.....} else {//其他作用域类型的处理逻辑.....}}return adaptBeanInstance(name, beanInstance, requiredType);
}//createBean方法中的核心方法doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {//核心 - 反射创建Bean对象,并且封装到BeanWrapper对象(Bean的包装器)中instanceWrapper = createBeanInstance(beanName, mbd, args);}//获得当前Bean对象Object bean = instanceWrapper.getWrappedInstance();Class> beanType = instanceWrapper.getWrappedClass();.....Object exposedObject = bean;try {//核心 - 给Bean的属性进行依赖注入,方法翻译过来叫填充BeanpopulateBean(beanName, mbd, instanceWrapper);//核心 - 然后执行Bean的后续初始化过程,具体细节见下方exposedObject = initializeBean(beanName, exposedObject, mbd);}.....//返回Bean对象 return exposedObject;
}
3.4 单例Bean的初始化
上一步已经展示了,Spring合适创建了Bean对象,以及依赖注入和初始化过程。这里重点来看下Bean的后续初始化过程,核心代码如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {//核心 - 调用Bean的各种XxxxAware接口的方法,进行相关属性填充invokeAwareMethods(beanName, bean);//核心 - 调用BeanPostProcessor的前置处理方法Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}//核心 - 调用Bean的初始化方法(init-method或者@PostConstruct注解标记的方法)try {invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}//核心 - 调用BeanPostProcessor的后置处理方法if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}
四、总结
因为Spring拥有很庞大的体系结构,没办法介绍的面面俱到,因此摘了部分核心代码,配合流程图讲解,基本把整个Bean的初始化乃至整个IOC容器的初始化过程,简述了出来。如果小伙伴想要更加深入的了解Spring源码,可以配合debug,走一遍IOC初始化的流程。
最后再总结下整个Bean的整个生命周期过程:
-
1、Spring通过反射创建Bean对象;
-
2、完成当前Bean的依赖注入(成员变量填充);
-
3、如果Bean有实现Aware接口,则调用各种Aware接口的方法设置属性;
-
4、执行BeanPostProcessor的前置方法(通常是一个集合,形成一个调用链,依次执行);
-
5、完成开发者自定义的初始化方法(init-method或者@PostConstruct注解标记的方法);
-
6、执行BeanPostProcessor的后置方法(通常是一个集合,形成一个调用链,依次执行);
-
7、完成初始化,将Bean放入BeanFactory集合中(核心是一个Map集合);
-
8、Bean对象随着BeanFactory关闭而销毁,执行开发者自定义的销毁方法(destory-method或者@PreDestory注解标记的方法)。

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
