最近也是面试的时候遭到了面试官对 Spring 一些方面狠狠的拷打,现在整理如下,供大家参考
问:Spring 三级缓存了解吗?不用三级缓存能解决循环依赖问题吗?为什么?
整理之后的回答:
首先 Spring 是采用三级缓存的问题去解决循环依赖的,也就是用三个不同的 Map 去存储不用的对象;关键:一定要有一个缓存保存它的早期对象最为死循环的出口
- 一级缓存 singletonObjects 用来存完整的单例 Bean
- 二级缓存 earlySingletonOjbects 存放的是早期的 Bean,即半成品,此时还无法使用(只用于循环依赖提供)
- 三级缓存 singletonFactories (循环依赖的出口,解决了循环依赖)。它存的是一个对象工厂,用于创建对象并放入二级缓存中。同时,如果对象有 Aop 代理,则对象工厂返回代理对象。
二级缓存能不能解决循环依赖?
答:
- 如果只是循环依赖导致的死循环的问题,一级缓存就可以解决,但是无法解决在并发下获取不完整的 Bean
- 二级可以完全解决循环依赖,只是需要在实例化后就创建动态代理,这不符合 Spring 生命周期规范(如果你对 Spring Bean 创建的机制和过程比较了解的话,你就可以在这深入说一下 Spring 应用启动时是怎么扫描 Bean 注册为组件,怎么实例化、赋值以及最后形成可用的 Bean 的整个过程,如果你能很好的说出来那一定是加分项,如果不能的话这样的话术对一些小公司的技术面来说应该就足够了)
Spring 有没有解决多例 Bean 的循环依赖?
答:没有
- 因为多例的或者用官方的话说作用域为 Prototype 的 Bean 不会被缓存起来,应为每次使用的时候都会重新创建 而不进行缓存早期对象的话就无法解决循环依赖
Spring 有没有解决构造函数参数 Bean 的循环依赖?
答:没有
- 使用构造函数的函数依赖也是会报错的,但是可以通过 @Lazy 注解实现延迟加载,等到使用的时候再通过动态代理进行创建
请你简述一下 Spring Boot 应用启动的过程。。。
分析:这是一个很复杂并且很难回答的问题,如果被问到了,请仔细分析面试官的用词,是简述还是直接让你说一下,如果简述的话就直接如下回答,如果是让你描述一下,大家可以根据面试公司的规模、自己的掌握程度扩展的去说,尽量把整个方法调用流程、Bean 组件的扫描注册、Bean 实例的生成过程都叙述一下。。。。。。。但是千万别没话格勒嗓子,不会了解的话实话实说就行
大致启动步骤如下:
- 应用首先根据 @SpringBootApplication 注解所在位置去寻找启动类,然后运行 main 方法,创建 Spring 应用程序上下文并启动它(具体什么是应用程序上下文大家可以自行百度、博主解释不清楚,共勉!)。这个启动的类会被称为启动类,通过它去加载 Spring Boot 的基础配置
- 然后你的主类名.run() 方法启动后,就回去加载 Spring 应用上下文,加载各种配置和组件(Spring Boot 3 及以上版本很多东西都定义在 spring.factories 这个文件中,这也就是为什么使用Spring Boot 脚手架可以快速启动一个应用,建议大家有时间可以自行百度)
- 接下来 Spring Boot 就会自动扫描路径上的各种组件、配置文件和类,并根据条件进行配置和注入,包括自动装配和属性值注入等工作
- 然后就会加载各种外部配置文件 (.yml .properties 这些文件),这些文件中包含了一些配置信息,在应用程序中使用
- 然后就会创建 Bean 的实例 (可以说 BeanDefination 相关的,如果你了解,包括扫描的过程怎么填充,创建单例 Bean 的时候进行合并,然后创建一个实例,还有就是实例化、赋值、初始话的过程)
- 实例化之后就是进行初始化和回调(根据上一条,结合说,会显的你很懂)
- 最后就是应用程序的运行,这是应用程序上下文已经完全构建完成,如果是 Web 应用,只需要等待客户端的请求到来就好