重点面试题(今天又看了很多的博客大概有个三十来篇吧所以总结了一休休的面试题):
ps:已经入秋了为什么还是这么热!!!
1、受管 bean 的生命周期
对于普通的 Java 对象,new 的时候会去创建对象,而当它没有任何引用的时候则被垃圾回收机制回收。相较于前者,由 Spring IoC 容器托管的对象,它们的生命周期完全由容器控制。
-
对于 ApplicationContext 容器,当容器启动结束后,通过反射调用构造方法实例化所有的 Bean。
-
设置对象属性,即依赖注入,动态将依赖关系注入到对象中。
-
紧接着 Spring 会检测该对象是否实现 xxxAware 接口,并将相关的 xxxAware 实例注入给 Bean。顺序执行BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法
-
Bean 对象初始化前,循环调用实现 BeanPostProcessor 接口的预初始化方法 postProcessBeforeInitialization。
-
Bean 对象初始化时顺序执行@PostConstruct 注解方法、InitializingBean 接口方法、init-method 配置方法。
-
Bean 初始化后循环调用实现 BeanPostProcessor 接口的后初始化方法 postProcessAfterInitialization。
-
容器关闭时执行 Bean 对象的销毁方法,顺序是@PreDestroy 注解方法、DisposableBean 接口方法、destroy-method 配置方法
bean 的 后 置 处 理 器 BeanPostProcessor 会 在 生 命 周 期 的 初 始 化 前 后 添 加 额 外 的 操 作 , 需 要 实 现BeanPostProcessor 接口,且配置到 IOC 容器中,需要注意的是后置处理器不是单独针对某个 bean 生效,而是针对 IOC 容器中所有 bean 都会执行
2、循环依赖是指两个或更多的组件之间存在着互相依赖的关系。
Spring 循环依赖:直接解决通过 setter 方法进行依赖注入且是在单例模式下产生的循环依赖问题。一级缓存 singletonObjects 缓存的是已经经历了完整生命周期的 bean 对象;二级缓存 earlySingletonObjects缓存的是早期的 bean 对象,就是属性还未填充完整。早期指的是 Bean 的生命周期还没走完就把这个 Bean 放入了 earlySingletonObjects;三级缓存singletonFactories 缓存的是 ObjectFactory,表示对象工厂,用来创建
某个对象的。
-
A 创建过程中需要 B,于是 A 将自己放到三级缓存里面,去实例化 B
-
B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
-
B 顺利初始化完毕,将自己放到一级缓存里面,此时 B 里面的 A 依然是创建中状态,然后回来接着创建 A,
此时 B 已经创建结束,直接从一级缓存里面拿到 B,然后完成创建,并将 A 放到一级缓存中。因为整个过程中,都只有一个 A 原始对象,所以对于 B 而言,就算在属性注入时,注入的是 A 原始对象,也没有关系,因为 A
原始对象在后续的生命周期中在堆中没有发生变化。
在 SpringBoot 应用程序中,循环依赖通常是由三种情况引起的:1、构造函数循环依赖:两个或更多的组件在它们的构造函数中互相依赖。2、属性循环依赖:两个或更多的组件在它们的属性中互相依赖。3、方法循环依赖:两个或更多的组件在它们的方法中互相依赖。在 2.6.0 前 Spring Boot 会自动处理循环依赖的问题。2.6.0及之后的版本会默认检查循环依赖,存在该问题则会报错。
通用解决方案为延迟注入,使用@Lazy 注解延迟加载依赖项。
@Component
public class A {private final B b;public A(@Lazy B b) { //既可用于构造器参数,也可以用于设置器参数this.b = b; }
}
3、starter 工作原理
SpringBoot 这些 starter 的作用就是根据配置给初始化整合其他框架时需要初始化的一些 bean,并加载到spring 容器中。这样就减少了在 SSM 时代那些繁琐的是 xml 配置。
@EnableAutoConfiguratio
注 解 使 用 Import 引 入AutoConfigurationImportSelector
类 , 而AutoConfigurationImportSelector
类通过 SpringFactortisLoader
加载了所有 jar 包的 MATE-INF 文件夹下的spring.factories 文件,
spring.factories 包含所有需要装配的 XXXConfiguration 类的全限定名。XXXConfiguration类包含了实例化该类需要的信息,比如个数据源 Configuration 类,那么就应该有数据库驱动、用户名、密码等信息。
-
SpringBoot 在启动时会去依赖的 Starter 包中寻找 resources/META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包。
-
根据 spring.factories 配置加载 AutoConfigure 类
-
根据@Conditional 注解的条件,进行自动配置并将 Bean 注入 Spring Context
4、SpringBoot 的启动流程
@SpringBootApplication 注解实际上是 SpringBoot 提供的一个复合注解,由三个注解组成:
-
@SpringBootConfiguration 来源于@Configuration,二者功能都是将当前类标注为配置类,并将当前类里以@Bean 注解标记的方法的实例注入到 spring 容器中。
-
@EnableAutoConfiguration 启用自动配置,可以帮助 SpringBoot 应用将所有符合条件的@Configuration 配置都加载到当前 IoC 容器之中。
-
@ComponentScan 对应于 XML 配置形式中的 context:component-scan,用于将一些标注特定注解的 bean定义批量采集注册到 Spring 的 IoC 容器之中,特定注解包括@Controller、@Component、@Service 和@Repository。
因此@SpringBootApplication 注解主要作为一个配置类,能够触发包扫描和自动配置的逻辑,从而使得SpringBoot 的相关 bean 被注册进 Spring 容器。这里使用了 spring.factories 文件来完成自动配置,提高了扩展性。在启动时使用观察者模式,以事件发布的形式通知,降低耦合,易于扩展等
1 、 首 先 创 建 SpringApplication 对 象 并 初 始 化 属 性 , 例 如 资 源 加 载 器 resourceLoader 、 启 动 类mainApplicationClass 、webApplicationType 类 型 、 初 始 化 器 initializers 和 listeners 。 从 类 路 径 下 找 到META/INF/Spring.factories 配置的所有 ApplicationContextInitializer 和 ApplicationListener,然后保存起来
2、调用 springApplication.run(args)方法
2.1 、 通 过 SpringFactoriesLoader 加 载 META-INF/spring.factories 文 件 , 获 取 并 创 建SpringApplicationRunListener 对象,并自动回调启动。然后由 SpringApplicationRunListener 来发出 starting
消息。
2.2 、 创 建 参 数 , 并 配 置 当 前 SpringBoot 应 用 将 要 使 用 的 Environment 。 完 成 后 , 依 然 由SpringApplicationRunListener 来发出 environmentPrepared 消息。2.3、创建 IoC 容器 ApplicationContext 对象。初始化 ApplicationContext 并设置 Environment,加载相关配置等。由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知 SpringBoot 应用使用的ApplicationContext 已准备 OK。2.4、将各种 beans 装载入 ApplicationContext,继续由SpringApplicationRunListener 来发出 contextLoaded消息,知 SpringBoot 应用使用的 ApplicationContext 已装填 OK。2.5、refresh ApplicationContext 刷新容器,扫描、创建、加载所有组件完成 IoC 容器可用的最后一步。由SpringApplicationRunListener 来发出 started 消息。2.6、从 IoC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调完成最终的程序的启动。由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了,并停止计时器。