系列文章目录
第一章 ArrayList-Java八股面试(一)
第二章 HashMap-Java八股面试(二)
第三章 单例模式-Java八股面试(三)
第四章 线程池和Volatile关键字-Java八股面试(四)
第五章 ConcurrentHashMap-Java八股面试(五)
第六章 spring之refresh流程-Java八股面试(六)
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、Spring Bean
- 1.1 处理名称,检查缓存
- 1.2 检查父工厂
- 1.3 检查DependsOn
- 1.4 按Scope创建Bean
- 1.5 创建Bean
- 1.5.1 创建Bean实例
- 1.5.2 依赖注入
- 1.5.3 初始化Bean
- 1.5.4 登记可销毁Bean
- 1.6 类型转换
- 1.7 销毁Bean
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Spring Bean
在对于Spring的所有解读中,Bean的生命周期都可谓是重中之重,甚至还有人称Spring就是个管理Bean的容器。Bean的生命周期之所以这么重要,被反复提及,是因为Spring的核心能力,比如对象创建(IOC)、属性注入(DI)、初始化方法的调用、代理对象的生成(AOP)等功能的实现,都是在bean的生命周期中完成的。
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。
对于Bean的创建可分为7个阶段。
阶段4中的scope可分为以下几种:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
- global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
需要注意的是singleton并不代表只有一个Bean,因为在程序中可能存在多个容器,自然就可能有多个Bean,所以还是视情况而论。
1.1 处理名称,检查缓存
需要注意的是三级缓存是在后续解决循环依赖时所需要的,下一篇文章会更新如何解决循环依赖。
1.2 检查父工厂
1.3 检查DependsOn
如果有2个类A和B,且B要监听A,则希望B在A之前加载到容器中。
但是Spring默认是根据文件夹中类名的顺序加载,例如字母A在B之前,则会先加载A。
@DependsOn注解可以设置Bean直接的依赖关系,被依赖的会先创建加载到Spring容器中。
下面举例:有一个老师类:Teacher,一个迟到学生类:LateStudent。
老师要检查哪些学生迟到,则老师得先到学校,在教室门口等着迟到的学生。
对于Spring容器而言,得先加载Teacher,后加载LateStudent,这样Teacher才能监听所有的LateStudent。
1.4 按Scope创建Bean
1.5 创建Bean
Bean 创建流程入口AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 中,refresh()方法的介绍在上一章中,感兴趣的可以去看看。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 实例化阶段
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
...
Object exposedObject = bean;
try {
// 属性赋值阶段
this.populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
...
}
...
}
总流程图如下:
1.5.1 创建Bean实例
对象的创建是bean生命周期的第一步,毕竟要先有1才能有0嘛。创建对象的方式有很多,比如 new、反射、clone等等,Spring是怎么创建对象的呢?绝大多数情况下,Spring是通过反射来创建对象的,不过如果我们提供了Supplier或者工厂方法,Spring也会直接使用我们提供的创建方式。
我们从源码出发,看一下Spring是如何选择创建方式的:
// 源码位于 AbstractAutowireCapableBeanFactory.javaprotected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 再次解析BeanDefinition的class,确保class已经被解析
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 1: 如果提供了Supplier,通过Supplier产生对象
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 2: 如果有工厂方法,使用工厂方法产生对象// 在@Configration配置@Bean的方法,也会被解析为FactoryMethodif (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
//...省略部分代码// 3: 推断构造方法// 3.1 执行后置处理器,获取候选构造方法
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 3.2 需要自动注入的情况if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 3.3 默认使用没有参数的构造方法return instantiateBean(beanName, mbd);
}
具体逻辑是:
先判断是否提供了Supplier,如果提供,则通过Supplier产生对象。
再判断是否提供工厂方法,如果提供,则使用工厂方法产生对象。
如果都没提供,需要进行构造方法的推断,逻辑为:
如果仅有一个构造方法,会直接使用该构造方法(如果构造方法有参数,会自动注入依赖参数)
如果有多个构造参数,会判断有没有加了@Autowired注解的构造参数:
如果没有,Spring默认选择无参构造方法;
如果有,且有@Autowired(required=true)的构造方法,就会选择该构造方法;
如果有,但是没有@Autowired(required=true)的构造方法,Spring会从所有加了@Autowired的构造方法中,根据构造器参数个数、类型匹配程度等综合打分,选择一个匹配参数最多,类型最准确的构造方法。
简而言之,我们只需要简单知道Spring会选择一个构造方法,然后通过反射创建出对象即可。
1.5.2 依赖注入
1.后处理器拓展
在依赖注入之前,MergedBeanDefinitionPostProcessor类型的后置处理器,可以对bean对应的BeanDefinition进行修改,本阶段是Spring提供的一个拓展点。Spring自身也充分利用该拓展点,做了很多初始化操作(并没有修改BeanDefinition),比如查找标注了@Autowired、 @Resource、@PostConstruct、@PreDestory 的属性和方法,方便后续进行属性注入和初始化回调。当然,我们也可以自定义实现,用来修改BeanDefinition信息或者我们需要的初始化操作。
2.三级缓存
经过后处理器的拓展之后,早期bean对象(注意还没有完成初始化,只是实例化)提前放入到三级缓存singletonFactories中,为循环依赖做支持。在后续进行属性填充时,如果发生循环依赖,可以从三级缓存中通过getObject()获取该bean,完成循环依赖场景下的自动注入。
booleanearlySingletonExposure= (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 做循环依赖的支持 将早期实例化bean的ObjectFactory,添加到单例工厂(三级缓存)中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
3.依赖注入
最后完成Spring的核心功能之一:依赖注入,包括自动注入、@Autowired注入、@Resource注入等。Spring会根据bean的注入模型(默认不自动注入),选择根据名称自动注入还是根据类型自动注入。然后调用InstantiationAwareBeanPostProcessor#postProcessProperties()完成@Autowired和@Resource的属性注入。
protectedvoidpopulateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 省略部分代码// 获取bean的注入类型intresolvedAutowireMode= mbd.getResolvedAutowireMode();
// 1: 自动注入if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValuesnewPvs=newMutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
// 根据名称注入
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// 根据类型注入
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 2: 调用BeanPostProcessor,完成@Autowired @Resource属性填充
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessoribp= (InstantiationAwareBeanPostProcessor) bp;
// 重点: 完成@Autowired @Resource属性填充PropertyValuespvsToUse= ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
// 需要注入的属性,会过滤掉Aware接口包含的属性(通过ignoreDependencyInterface添加)
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// 3: 依赖检查if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
// 4: 将属性应用到bean中if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
1.5.3 初始化Bean
该阶段主要做bean的初始化操作,包括:回调Aware接口、回调初始化方法、生成代理对象等。
invokeAwareMethods():回调BeanNameAware、BeanClassLoaderAware、BeanFactoryAware感知接口。
回调后置处理器的前置方法,其中:
ApplicationContextAwareProcessor: 回调EnvironmentAware、ResourceLoaderAware、ApplicationContextAware、ApplicationEventPublisherAware、MessageSourceAware、EmbeddedValueResolverAware感知接口。
InitDestroyAnnotationBeanPostProcessor:调用了标注了@PostConstruct的方法。
invokeInitMethods()调用初始化方法:
如果bean是InitializingBean的子类, 先调用afterPropertiesSet()。
回调自定义的initMethod,比如通过@Bean(initMethod = “xxx”)指定的初始化方法。
回调后置处理器的后置方法,可能返回代理对象。其中AbstractAutoProxyCreator和 AbstractAdvisingBeanPostProcessor都有可能产生代理对象,比如InfrastructureAdvisorAutoProxyCreator完成了@Transactional代理对象的生成,AsyncAnnotationBeanPostProcessor完成了@Async代理对象的生成。
在初始化完成后,bean会被放到单例池中,正式开始自己的使命:为项目服务,比如接收http请求,进行CRUD等等。后续有使用到该bean的地方,也是直接从单例池中获取,不会再次创建bean(仅单例的哦)。
1.5.4 登记可销毁Bean
在创建bean的时候,会判断如果bean是DisposableBean、AutoCloseable的子类,或者有 destroy-method等,会注册为可销毁的bean,在容器关闭时,调用对应的方法进行bean的销毁。