深入理解Spring IOC(七) 、 总结,含常见面试题

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 深入理解Spring IOC(七) 、 总结,含常见面试题

总结


我们在之前的文章中,对spring加载bean的流程做了详细的讲解,我们知道要将xml中的各个节点解析成真正的bean要经过下面的主要过程:

1、将xml中的信息解析成BeanDefinition,这其中,XmlBeanDefinitionReader专门负责去读取xml,并且将xml中的信息解析成BeanDefinition放到DefaultListableBeanFactory中。

2、BeanFactoryPostProcessor去根据自己的需要修改BeanFactory中已经加载好的BeanDefinition

3、拿到所有的beanName,一个一个的去对单例调用getBean方法去初始化,这个过程分为了以下步骤:

  • (1)将这个beanName对应的BeanDefinition转化成RootBeanDefinition,这也是出于初始化的需要,RootBeanDefinition才是标准的初始化流程需要的BeanDefinition,里面包含了很多解析过程的缓存,以及其他重要的属性,这步主要是DefaultListableBeanFactory的preInstantiateSingletons中做的。
  • (2)接下来是要看是通过工厂方法还是构造方法去创建这个对象,如果有工厂方法,则去解析需要的工厂方法和其参数,如果没有工厂方法,则需要使用构造方法去创建对象,这时候就需要判断究竟使用哪个构造方法,判断流程为先使用AutowiredAnnotationBeanPostProcessor中的determineCandidateConstructors方法来确定候选的构造方法,这里确定的原则主要是根据注解来确定,如果你有多个@Autowired标注的构造方法,那么只能有一个的required为true,determineCandidateConstructors方法可能会返回多个方法,这时候需要根据参数匹配的原则来匹配最合适的构造方法并解析其参数,这时候可能会触发其他bean的初始化(createArgumentArray方法),最合适的构造方法和参数都拿到后,则去调用它创建对象。这一步主要是在AbstractAutowireCapableBeanFactory中的createBeanInstance中完成。
  • (3)获取到上一步创建的对象之后,则要进行属性填充的工作,这时候主要处理的是@AutoWired绑定的属性和方法,@Value绑定的属性,@Inject绑定的属性。这个工作主要是在AbstractAutowireCapableBeanFactory中的populateBean方法中完成。
  • (4)属性填充完毕之后,则去执行各种初始化的方法,其中有InitializingBean,以及@PostConstruct和在xml中定义的init-method都会在这里执行,这个工作是在AbstractAutowireCapableBeanFactory中的initializeBean中完成。
  • (5)判断是不是需要提前曝光bean。

相关面试题


话说其实我是不怎么愿意讲面试题的,因为我认为你把源码看懂之后根本就不需要看这样的东西,但是大多数读者貌似都有这样的需求,所以也就没办法,说说最常见的一些面试题


顺便求一波赞,祝点赞的人面试一飞冲天😄~


  1. IOC的好处:这个在我的这篇文章(深入理解Spring IOC(一) 、统一资源的加载)的开篇说的很清楚了,好处是解耦,具体的你可以回过头再去看看。
  2. Spring怎么解决循环依赖: 循环依赖主要是通过三层缓存来解决的,三层缓存并不是官方概念,当然我也不知道是谁提出来的这个概念,就姑且这么叫吧。这三层缓存在源码中分别是singletonObjects,earlySingletonObjects,以及singletonFactories这三个map。其中singletonObjects是用来保存创建好的成品对象,earlySingletonObjects是用来保存提前曝光出来的对象,singletonFactories是用来保存获取半成品对象的ObjectFactory的。它们是怎么配合起来解决循环依赖的呢?我们来看个最简单的例子:
    有两个类A和B,A中有类型为B的成员属性,B中又有类型为A的成员属性,注意不能是prototype,也不能是构造器依赖。当创建了一个不完整的A对象时候(其实也就是只调用了A的构造方法而没有进行属性填充),这时候会把这个“不完整”的A对象包装成ObjectFactory后放到singletonFactories中,再去做属性填充的动作,然后就在属性填充的时候发现了需要初始化B,就跑去初始化B去了,然后也是先创建了一个不完整的B并且包装成ObjectFactory放到了singletonFactories后去填充属性,这时候发现需要一个A,然后又去初始化A,但是此时singletonFactories里面的话已经有个不完整的A的ObjectFactory了,这时候就可以通过这个singletonFactories来获取到半成品的A的ObjectFactory,再通过A的ObjectFactory获取到半成品A,注意这里还会把这个半成品A的ObjectFactory从singletonFactories移除,然后还会把半成品A放到earlySingletonObjects中去,拿到半成品A后去填充B,B创建完了之后再用创建完的B填充A,这样A和B的创建过程就完成了


为什么需要把半成品A放到earlySingletonObjects中去呢?你可以试着结合那部分的源码想一下A依赖B,B依赖C,C依赖D,D又依赖A、B、C的这种复杂循环依赖场景你就会明白了。另外earlySingletonObjects的设计并不仅仅是为了解决循环依赖,它是还有别的作用的(代理相关的),所以不要仅仅只是背面试题。我现在再就问你一个问题,Spring是怎样判断你的这两个Class是不是循环依赖的?估计很多只是背题的人就懵了,所以只背题而根本都不看相关源码的话,面试官总是可以花式吊打你的,不信你到我面前来试试😈


  1. bean的生命周期: 所谓生命周期,主要是涵盖着从bean的诞生一直到被销毁,网上还有文章扯到了BeanFactoryPostProcessor上面了,但是在BeanFactoryPostProcessor执行的时候,bean还没变成受精卵呢你生命周期个蛋蛋,看过我文章的都知道因为此时还是BeanDefinition而已,bean都还没有初始化。对于bean的生命周期,我认为在《spring揭秘》这本书上的这张图可以说是解释的很准确了:

1686813276337.png


为了把这个问题说的更清楚一些,我不顾让图变丑的巨大危险,给图里的步骤上面标上了表示步骤的数字🤦‍‍,

我们一步一步来看:

第1步,创建一个"不完整"的bean对象,细节在第五篇的creatBeanInstance方法中,这是创建bean必须经过的过程(除过代理);

第2步填充属性也是必须经过的,哪怕你没有属性,这一步也是要走的;

第3步这个,是先检查你的bean是不是实现了Aware接口,没有实现Aware那就直接跳过了,如果实现了Aware,还要检查是哪个Aware,然后才会给你设置某个Aware,这里会检查三个Aware,分别是BeanNameAware、BeanClassLoaderAware、BeanFactoryAware这三个Aware,注意肯定是没有ApplicationContextAware的,网上说有这个Aware的怕是自己都根本没看过源码的;

第4步这个是BeanPostProcessor的前置处理,是必须经过的;

第5步和第6步,都是先要检查,然后满足条件才调用,没有的话就跳过了;

第7步这个是BeanPostProcessor的后置处理,也是必须经过的;

第8步是你这个bean必须是个DisposableBean或者实现了java的AutoCloseable接口,或者你得实现DestructionAwareBeanPostProcessor这个接口,才会走这步,这时候会把你满足条件的bean加到disposableBeans这个map中方便后续调用(和三层缓存在一个类中);

第9步对应的是我们在业务中的使用;

第10和第11步是对应销毁bean,这个通常是因为AbstrctApplicationContext的close方法被调用,这时候会调用之前存在disposableBeans中的所有bean的相关销毁的方法,也是属于不一定会走的方法。

为什么我把图上的文字“Bean的实例化过程”用红框圈出来了呢?因为很明显,这个图不仅仅包含创建了,而是还有销毁过程,因此我觉得这个图更应该叫做bean的生命周期而不是Bean的实例化过程才对(个人观点)~


不止于源码的


到这里,整个正篇部分就完了,后边还有一些扩展篇没有写完。或许你到这里觉得spring ioc的代码很牛,但其实真正牛的是ioc的这种思想,它真正意义上解决了面向对象的痛点。很多人都很认同spring为java续命了十几年,这种说法我一定程度上是认同的,现在spring系列的东西为我们的开发生产带来了太多的便利,当然也淘汰了一大批人,很多人只会crud的逐渐适应不了这种变化,也有很多人抱怨变化太快,但是如果你一直也只是会用别人封装的东西的话那其实说真的,不淘汰你淘汰谁?

话又说回来,Spring解决了面向对象编程的痛点,那又有什么缺点呢?我们现在做微服务的小伙伴肯定都有这样的体验,就是一个springboot应用,明明我们业务代码在里面没有多少,但是这个应用真正跑起来都很很费内存,这个就和spring应用在运行期间有自动装载了很多的bean有着千丝万缕的关系,这一点其实不太符合微服务的这个味道。

至于为什么要读源码,我想看到我说这些心里话的这块的人,我们是有一个共同的目标,那就是希望自己能成为一个比别人更好一些的工程师。我自己是的目标便是要自己做的能够比别人更好。Ioc这部分源码看完了之后,觉得看很多框架层面报的错都变简单了很多,更重要的是去看好多框架和Spring整合的源码这里是真的变得很容易,这并不是我在装逼,而是自己真真实实的收获和提高。

或许你虽然看到了这里但是前面的文章却都还看的不太明白,这其实很正常,我还是建议你能从我第一篇给出来的github上面把我的代码拉下来,进去对着我的注释跟一跟,研究源码,从来不可能只是看看别人的文章就能明白那里面的东西,那是必须一遍遍的看,一遍遍的debug。也有很多读者很有野心,总是想在面试的时候反手把面试官按在地上摩擦一下证明证明自己,但是那绝对不是说是你背多少题就能做到的,要想经得起考验,肯定是要把内功练好的。想要变得比别人强这点恒心还是需要有的,我看源码看的头昏眼花,但是最后都坚持下来了。如果有什么问题实在不明白,也可以和我来沟通,我如果有时间会给你讲,我的邮箱是554013882@qq.com,文章有什么问题,也可以发到这里来。

目录
相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
87 2
|
5天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
96 69
|
3天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
36 21
|
10天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
8天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
30天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
53 2
|
1月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
2月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
4月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
2月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
45 0