关于java:阿里灵魂面试30道死磕-Spring-之-IoC-篇

34次阅读

共计 15381 个字符,预计需要花费 39 分钟才能阅读完成。

题目

  • 1. 什么是 Spring Framework?
  • 2. Spring Framework 的劣势和有余?
  • 3. 你对 IoC 的了解?
  • 4. 为什么须要 IoC?
  • 5. IoC 和 DI 的区别?
  • 6. IoC 容器的职责?
  • 7. 什么是 Spring IoC 容器?
  • 8. 结构器注入和 Setter 注入
  • 9. BeanFactory 和 ApplicationContext 谁才是 Spring IoC 容器?
  • 10. Spring Bean 的生命周期?
  • 11. BeanDefinition 是什么?
  • 12. Spring 内建的 Bean 作用域有哪些?
  • 13. BeanPostProcessor 与 BeanFactoryPostProcessor 的区别?
  • 14. 依赖注入和依赖查找的起源是否雷同?
  • 15. 如何基于 Extensible XML authoring 扩大 Spring XML 元素?
  • 16. Java 泛型擦写产生在编译时还是运行时?
  • 17. 简述 Spring 事件机制原理?
  • 18. @EventListener 的工作原理?
  • 19. Spring 提供的注解有哪些?
  • 20. 简述 Spring Environment?
  • 21. Environment 残缺的生命周期是怎么的?
  • 22. Spring 利用上下文的生命周期?
  • 23. Spring 利用上下文生命周期有哪些阶段?
  • 24. 简述 ObjectFactory?
  • 25. 简述 FactoryBean?
  • 26. ObjectFactory、FactoryBean 和 BeanFactory 的区别?
  • 27. @Bean 的解决流程是怎么的?
  • 28. BeanFactory 是如何解决循环依赖?
  • 29. Spring 中几种初始化办法的执行程序?

spring 其余专题也正在码字中,如果想要更快的看到,可点击下方链接获取

最全学习笔记大厂真题 + 微服务 +MySQL+ 分布式 +SSM 框架 +Java+Redis+ 数据结构与算法 + 网络 +Linux+Spring 全家桶 +JVM+ 高并发 + 各大学习思维脑图 + 面试汇合

1. 什么是 Spring Framework?

官网文档:

Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’s needs.

这个问题很难答复,在 Spring 官网文档中的形容也很形象,答案在于你对 Spring 是如何了解的,想必每个人都有本人的答复形式,以下是我集体对于 Spring 的了解:

整个 Spring 生态在波及到 Java 的我的项目中被广泛应用,它提供了十分多的组件,可能让你在开发 Java 利用的过程变得更加容易,弹性地反对其他软件框架,能够比作一个“排插座”,其他软件框架简略地“插上”即可联合 Spring 一起应用,给开发人员带来了十分多的便当。Spring 底层 IoC 容器的设计实现也是十分完满的,在整个 Spring 利用上下文的生命周期和 Spring Bean 的生命周期的许多阶段提供了相应的扩大点,供开发者自行扩大,使得框架十分的灵便。

2. Spring Framework 的劣势和有余?

劣势:Spring 面向模块进行开发,依据不同的性能进行划分,依据需要引入对应的模块即可,对于开发人员十分敌对。例如 Spring IoC 容器,将咱们的 Java 对象作为 Spring Bean 进行治理,治理着 Bean 的整个生命周期;Spring MVC 提供“模型 - 视图 - 控制器”(Model-View-Controller)架构和随时可用的组件,用于开发灵便且涣散耦合的 Web 应用程序;Spring AOP 提供面向切面编程的接口,能够很不便的应用;还有许多其余的功能模块,就不一一讲述了。

有余:整个 Spring 体系比较复杂,对于开发人员须要肯定的学习老本,遇到相干问题时须要对底层实现有充沛的理解,这也就须要开发人员投入更多的工夫和精力去学习。当然,现在 Spring 体系整合了 Java 生态十分多的货色,为开发人员带来的便当远大于这些有余,我感觉是有必要对 Spring 进行充沛的学习,去理解 Spring 的贡献者们的设计思路,对本身也会有很大的晋升,从中能够学习到许多的货色。

3. 你对 IoC 的了解?

Inversion of Control(IoC)是面向对象中的一种编程思维或准则。能够先回到传统形式,当我依赖一个对象,我须要被动去创立它并进行属性赋值,而后我能力去应用这个对象。对于 IoC 这种形式来说,它使得对象或者组件的创立更为通明,你不须要过多地关注细节,如创建对象、属性赋值,这些工作交都由 IoC 容器来实现,已达到解耦的目标。

IoC 管制反转,简略来了解其实就是把获取依赖对象的形式,交由 IoC 容器来实现,由“被动拉取”变为“被动获取”。

4. 为什么须要 IoC?

实际上,IoC 是为了屏蔽结构细节。例如 new 进去的对象的生命周期中的所有细节对于应用端都是晓得的,如果在没有 IoC 容器的前提下,IoC 是没有存在的必要,不过在简单的零碎中,咱们的利用更应该关注的是对象的使用,而非它的结构和初始化等细节。

5. IoC 和 DI 的区别?

DI 依赖注入不齐全等同于 IoC,更应该说 DI 依赖注入是 IoC 的一种实现形式或策略。

依赖查找 依赖注入 都是 IoC 的实现策略。依赖查找 就是在应用程序外面被动调用 IoC 容器提供的接口去获取对应的 Bean 对象,而 依赖注入 是在 IoC 容器启动或者初始化的时候,通过结构器、字段、setter 办法或者接口等形式注入依赖。依赖查找 相比于 依赖注入 对于开发者而言更加繁琐,具备肯定的代码入侵性,须要借助 IoC 容器提供的接口,所以咱们总是强调后者。依赖注入 在 IoC 容器中的实现也是调用相干的接口获取 Bean 对象,只不过这些工作都是在 IoC 容器启动时由容器帮你实现了,在应用程序中咱们通常很少被动去调用接口获取 Bean 对象。

6. IoC 容器的职责?

次要有以下职责:

  • 依赖解决,通过依赖查找或者依赖注入
  • 治理托管的资源(Java Bean 或其余资源)的生命周期
  • 治理配置(容器配置、内部化配置、托管的资源的配置)

IoC 容器有十分多,例如 JDK 的 Java Beans,Java EE 的 EJB,Apache Avalon,Google guice,Spring,其中 Spring 是最胜利的的一个,目前被广泛应用。

其中 Spring 借鉴了 JDK 的 Java Beans 设计思维,也应用到其中相干类(例如 java.beans.PropertyEditor 属性编辑器),开发过 IDE 的 GUI 界面的搭档应该对 Java Beans 比拟相熟。

7. 什么是 Spring IoC 容器?

Spring 框架是一个 IoC 容器的实现,DI 依赖注入是它的实现的一个准则,提供依赖查找和依赖注入两种依赖解决,治理着 Bean 的生命周期。Spring 还提供了 AOP 形象、事件形象、事件监听机制、SPI 机制、弱小的第三方整合、易测试性等其余个性。

8. 结构器注入和 Setter 注入

结构器注入:通过结构器的参数注入相干依赖对象

Setter 注入:通过 Setter 办法注入依赖对象,也能够了解为字段注入

对于两种注入形式的认识:

  • 结构器注入能够防止一些难堪的问题,比如说状态不确定性地被批改,在初始化该对象时才会注入依赖对象,肯定水平上保障了 Bean 初始化后就是不变的对象,这样对于咱们的程序和维护性都会带来更多的便当;
  • 结构器注入不容许呈现循环依赖,因为它要求被注入的对象都是成熟态,保障可能实例化,而 Setter 注入或字段注入没有这样的要求;
  • 结构器注入能够保障依赖的对象可能有序的被注入,而 Setter 注入或字段注入底层是通过反射机制进行注入,无奈齐全保障注入的程序;
  • 如果结构器注入呈现比拟多的依赖导致代码不够优雅,咱们应该思考本身代码的设计是否存在问题,是否须要重构代码构造。

除了下面的注入形式外,Spring 还提供了接口回调注入,通过实现 Aware 接口(例如 BeanNameAware、ApplicationContextAware)能够注入相干对象,Spring 在初始化这类 Bean 时会调用其 setXxx 办法注入对象,例如注入 beanName、ApplicationContext

9. BeanFactory 和 ApplicationContext 谁才是 Spring IoC 容器?

BeanFactory 是 Spring 底层 IoC 容器,ApplicationContext 是 BeanFactory 的子接口,是 BeanFactory 的一个超集,提供 IoC 容器以外更多的性能。ApplicationContext 除了表演 IoC 容器角色,还提供了这些企业个性:面向切面(AOP)、配置元信息、资源管理、事件机制、国际化、注解、Environment 形象等。咱们个别称 ApplicationContext 是 Spring 利用上下文,BeanFactory 为 Spring 底层 IoC 容器。

10. Spring Bean 的生命周期?

生命周期:

  1. Spring Bean 元信息配置阶段,能够通过面向资源(XML 或 Properties)、面向注解、面向 API 进行配置
  2. Spring Bean 元信息解析阶段,对上一步的配置元信息进行解析,解析成 BeanDefinition 对象,该对象蕴含定义 Bean 的所有信息,用于实例化一个 Spring Bean
  3. Spring Bean 元信息注册阶段,将 BeanDefinition 配置元信息 保留至 BeanDefinitionRegistry 的 ConcurrentHashMap 汇合中
  4. Spring BeanDefinition 合并阶段,定义的 Bean 可能存在层次性关系,则须要将它们进行合并,存在雷同配置则笼罩父属性,最终生成一个 RootBeanDefinition 对象
  5. Spring Bean 的实例化阶段,首先的通过类加载器加载出一个 Class 对象,通过这个 Class 对象的结构器创立一个实例对象,结构器注入在此处会实现。在实例化阶段 Spring 提供了实例化前后两个扩大点(InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation、postProcessAfterInstantiation 办法)
  6. Spring Bean 属性赋值阶段,在 Spring 实例化后,须要对其相干属性进行赋值,注入依赖的对象。首先获取该对象所有属性与属性值的映射,可能已定义,也可能须要注入,在这里都会进行赋值(反射机制)。提醒一下,依赖注入的实现通过 CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和 AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)两个处理器实现的。
  7. Aware 接口回调阶段,如果 Spring Bean 是 Spring 提供的 Aware 接口类型(例如 BeanNameAware、ApplicationContextAware),这里会进行接口的回调,注入相干对象(例如 beanName、ApplicationContext)
  8. Spring Bean 初始化阶段,这里会调用 Spring Bean 配置的初始化办法,执行程序:@PostConstruct 标注办法、实现 InitializingBean 接口的 afterPropertiesSet() 办法、自定义初始化办法。在初始化阶段 Spring 提供了初始化前后两个扩大点(BeanPostProcessor 的 postProcessBeforeInitialization、postProcessAfterInitialization 办法)
  9. Spring Bean 初始化实现阶段,在所有的 Bean(不是形象、单例模式、不是懒加载形式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象,如果是 SmartInitializingSingleton 类型则调用其 afterSingletonsInstantiated() 办法,这里也属于 Spring 提供的一个扩大点
  10. Spring Bean 销毁阶段,当 Spring 利用上下文敞开或者你被动销毁某个 Bean 时则进入 Spring Bean 的销毁阶段,执行程序:@PreDestroy 注解的销毁动作、实现了 DisposableBean 接口的 Bean 的回调、destroy-method 自定义的销毁办法。这里也有一个销毁前阶段,也属于 Spring 提供的一个扩大点,@PreDestroy 就是基于这个实现的
  11. Spring 垃圾收集(GC)

总结:

  1. 下面 123 属于 BeanDefinition 配置元信息阶段,算是 Spring Bean 的前身,想要生成一个 Bean 对象,须要将这个 Bean 的所有信息都定义好;
  2. 其中 45 属于实例化阶段,想要生成一个 Java Bean 对象,那么必定须要依据 Bean 的元信息先实例化一个对象;
  3. 接下来的 6 属于属性赋值阶段,实例化后的对象还是一个空对象,咱们须要依据 Bean 的元信息对该对象的所有属性进行赋值;
  4. 前面的 789 属于初始化阶段,在 Java Bean 对象生成后,可能须要对这个对象进行相干初始化工作才予以应用;
  5. 最初面的 1011 属于销毁阶段,当 Spring 利用上下文敞开或者被动销毁某个 Bean 时,可能须要对这个对象进行相干销毁工作,最初期待 JVM 进行回收。

11. BeanDefinition 是什么?

BeanDefinition 是 Spring Bean 的“前身”,其外部蕴含了初始化一个 Bean 的所有元信息,在 Spring 初始化一个 Bean 的过程中须要依据该对象生成一个 Bean 对象并进行一系列的初始化工作。

12. Spring 内建的 Bean 作用域有哪些?

起源阐明
singleton默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例
prototype原型作用域,每次依赖查找和依赖注入生成新 Bean 对象
request将 Spring Bean 存储在 ServletRequest 上下文中
session将 Spring Bean 存储在 HttpSession 中
application将 Spring Bean 存储在 ServletContext 中

13. BeanPostProcessor 与 BeanFactoryPostProcessor 的区别?

BeanPostProcessor 提供 Spring Bean 初始化前和初始化后的生命周期回调,容许对关怀的 Bean 进行扩大,甚至是替换,其相干子类也提供 Spring Bean 生命周期中其余阶段的回调。

BeanFactoryPostProcessor 提供 Spring BeanFactory(底层 IoC 容器)的生命周期的回调,用于扩大 BeanFactory(理论为 ConfigurableListableBeanFactory),BeanFactoryPostProcessor 必须由 Spring ApplicationContext 执行,BeanFactory 无奈与其间接交互。

14. 依赖注入和依赖查找的起源是否雷同?

否,依赖查找的起源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的起源还包含 Resolvable Dependency(Spring 利用上下文定义的可已解决的注入对象,例如注入 BeanFactory 注入的是 ApplicationContext 对象)以及 @Value 所标注的内部化配置

15. 如何基于 Extensible XML authoring 扩大 Spring XML 元素?

Spring XML 扩大

  1. 编写 XML Schema 文件(XSD 文件):定义 XML 构造
  2. 自定义 NamespaceHandler 实现:定义命名空间的处理器
  3. 自定义 BeanDefinitionParser 实现:绑定命名空间下不同的 XML 元素与其对应的解析器
  4. 注册 XML 扩大(META-INF/spring.handlers 文件):命名空间与命名空间处理器的映射
  5. 编写 Spring Schema 资源映射文件(META-INF/spring.schemas 文件):XML Schema 文件通常定义为网络的模式,在无网的状况下无法访问,所以个别在本地的也有一个 XSD 文件,可通过编写 spring.schemas 文件,将网络模式的 XSD 文件与本地的 XSD 文件进行映射,这样会 优先 从本地获取对应的 XSD 文件

Mybatis 对 Spring 的集成我的项目中的 <mybatis:scan /> 标签就是这样实现的,能够参考:NamespaceHandler、MapperScannerBeanDefinitionParser、XSD 等文件

具体实现逻辑参考后续 《解析自定义标签(XML 文件)》 一文

16. Java 泛型擦写产生在编译时还是运行时?

运行时。编译时,泛型参数类型还是存在的,运行时会疏忽。

17. 简述 Spring 事件机制原理?

次要有以下几个角色:

  • Spring 事件 – org.springframework.context.ApplicationEvent,实现了 java.util.EventListener 接口
  • Spring 事件监听器 – org.springframework.context.ApplicationListener,实现了 java.util.EventObject 类
  • Spring 事件公布器 – org.springframework.context.ApplicationEventPublisher
  • Spring 事件播送器 – org.springframework.context.event.ApplicationEventMulticaster

Spring 内建的事件:

  • ContextRefreshedEvent:Spring 利用上下文就绪事件
  • ContextStartedEvent:Spring 利用上下文启动事件
  • ContextStoppedEvent:Spring 利用上下文进行事件
  • ContextClosedEvent:Spring 利用上下文敞开事件

Spring 利用上下文就是一个 ApplicationEventPublisher 事件公布器,其外部有一个 ApplicationEventMulticaster 事件播送器(被观察者),外面保留了所有的 ApplicationListener 事件监听器(观察者)。Spring 利用上下文公布一个事件后会通过 ApplicationEventMulticaster 事件播送器进行播送,可能解决该事件类型的 ApplicationListener 事件监听器则进行解决。

18. @EventListener 的工作原理?

@EventListener 用于标注在办法下面,该办法则能够用来解决 Spring 的相干事件。

Spring 外部有一个处理器 EventListenerMethodProcessor,它实现了 SmartInitializingSingleton 接口,在所有的 Bean(不是形象、单例模式、不是懒加载形式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象时会执行该处理器对该 Bean 进行解决。在 EventListenerMethodProcessor 中会对标注了 @EventListener 注解的办法进行解析,如果符合条件则生成一个 ApplicationListener 事件监听器并注册。

19. Spring 提供的注解有哪些?

外围注解有以下:

  • Spring 模式注解
Spring 注解场景阐明起始版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

Spring 模式注解都是 @Component 的派生注解,Spring 为什么会提供这么多派生注解?

@Component 注解是一个通用组件注解,标注这个注解后表明你须要将其作为一个 Spring Bean 进行应用,而其余注解都有各自的作用,例如 @Controller 及其派生注解用于 Web 场景下解决 HTTP 申请,@Configuration 注解通常会将这个 Spring Bean 作为一个配置类,也会被 CGLIB 提供,帮忙实现 AOP 个性。这也是 畛域驱动设计 中的一种思维。

畛域驱动设计:Domain-Driven Design,简称 DDD。过来系统分析和零碎设计都是拆散的,这样割裂的后果导致需要剖析的后果无奈间接进行设计编程,而可能进行编程运行的代码却扭曲需要,导致客户运行软件后才发现很多性能不是本人想要的,而且软件不能疾速追随需要变动。DDD 则突破了这种隔膜,提出了畛域模型概念,对立了剖析和设计编程,使得软件可能更灵便疾速追随需要变动。

  • 拆卸注解
Spring 注解场景阐明起始版本
@ImportResource替换 XML 元素 <import>2.5
@Import导入 Configuration 类2.5
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1
  • 依赖注入注解
Spring 注解场景阐明起始版本
@AutowiredBean 依赖注入,反对多中依赖查找形式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5
  • @Enable 模块驱动
Spring 注解场景阐明起始版本
@EnableWebMvc启动整个 Web MVC 模块3.1
@EnableTransactionManagement启动整个事务管理模块3.1
@EnableCaching启动整个缓存模块3.1
@EnableAsync启动整个异步解决模块3.1

@Enable 模块驱动是以 @Enable 为前缀的注解驱动编程模型。所谓“模块”是指具备雷同畛域的性能组件汇合,组合所造成一个独立的单元。比方 Web MVC 模块、AspectJ 代理模块、Caching(缓存)模块、JMX(Java 治理扩大)模块、Async(异步解决)模块等。

这类注解底层原理就是通过 @Import 注解导入相干类(Configuration Class、ImportSelector 接口实现、ImportBeanDefinitionRegistrar 接口实现),来实现引入某个模块或性能。

  • 条件注解
Spring 注解场景阐明起始版本
@Conditional条件限定,引入某个 Bean4.0
@Profile从 Spring 4.0 开始,@Profile 基于 @Conditional 实现,限定 Bean 的 Spring 应用环境4.0

20. 简述 Spring Environment?

对立 Spring 配置属性的存储,用于占位符解决和类型转换,还反对更丰盛的配置属性源(PropertySource);

通过 Environment Profiles 信息,帮忙 Spring 容器提供条件化地拆卸 Bean。

21. Environment 残缺的生命周期是怎么的?

在 Spring 利用上下文进入刷新阶段之前,能够通过 setEnvironment(Environment) 办法提前设置 Environment 对象,在刷新阶段如果没有 Environment 对象则会创立一个新的 Environment 对象

22. Spring 利用上下文的生命周期?

Spring 利用上下文就是 ApplicationContext,生命周期次要体现在 org.springframework.context.support.AbstractApplicationContext#refresh() 办法中,大抵如下:

  1. Spring 利用上下文启动筹备阶段,设置相干属性,例如启动工夫、状态标识、Environment 对象
  2. BeanFactory 初始化阶段,初始化一个 BeanFactory 对象,加载出 BeanDefinition 们;设置相干组件,例如 ClassLoader 类加载器、表达式语言处理器、属性编辑器,并增加几个 BeanPostProcessor 处理器
  3. BeanFactory 后置解决阶段,次要是执行 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的解决,对 BeanFactory 和 BeanDefinitionRegistry 进行后置解决,这里属于 Spring 利用上下文的一个扩大点
  4. BeanFactory 注册 BeanPostProcessor 阶段,次要初始化 BeanPostProcessor 类型的 Bean(依赖查找),在 Spring Bean 生命周期的许多节点都能见到该类型的处理器
  5. 初始化内建 Bean,初始化以后 Spring 利用上下文的 MessageSource 对象(国际化文案相干)、ApplicationEventMulticaster 事件播送器对象、ThemeSource 对象
  6. Spring 事件监听器注册阶段,次要获取到所有的 ApplicationListener 事件监听器进行注册,并播送晚期事件
  7. BeanFactory 初始化实现阶段 ,次要是 初始化 所有还未初始化的 Bean(不是形象、单例模式、不是懒加载形式)
  8. Spring 利用上下文刷新实现阶段,革除以后 Spring 利用上下文中的缓存,例如通过 ASM(Java 字节码操作和剖析框架)扫描进去的元数据,并公布上下文刷新事件
  9. Spring 利用上下文启动阶段,须要被动调用 AbstractApplicationContext#start() 办法,会调用所有 Lifecycle 的 start() 办法,最初会公布上下文启动事件
  10. Spring 利用上下文进行阶段,须要被动调用 AbstractApplicationContext#stop() 办法,会调用所有 Lifecycle 的 stop() 办法,最初会公布上下文进行事件
  11. Spring 利用上下文敞开阶段,公布以后 Spring 利用上下文敞开事件,销毁所有的单例 Bean,敞开底层 BeanFactory 容器;留神这里会有一个钩子函数(Spring 向 JVM 注册的一个敞开以后 Spring 利用上下文的线程),当 JVM“敞开”时,会触发这个线程的运行

总结:

  • 下面的 12345678 都属于 Sping 利用上下文的刷新阶段,实现了 Spring 利用上下文一系列的初始化工作;
  • 9 属于 Spring 利用上下文启动阶段,和 Lifecycle 生命周期对象相干,会调用这些对象的 start() 办法,最初公布上下文启动事件;
  • 10 属于 Spring 利用上下文进行阶段,和 Lifecycle 生命周期对象相干,会调用这些对象的 stop() 办法,最初公布上下文进行事件;
  • 11 属于 Spring 利用上下文敞开阶段,公布上下文敞开事件,销毁所有的单例 Bean,敞开底层 BeanFactory 容器。

23. Spring 利用上下文生命周期有哪些阶段?

参考Spring 利用上下文的生命周期

  • 刷新阶段 – ConfigurableApplicationContext#refresh()
  • 启动阶段 – ConfigurableApplicationContext#start()
  • 进行阶段 – ConfigurableApplicationContext#stop()
  • 敞开阶段 – ConfigurableApplicationContext#close()

24. 简述 ObjectFactory?

ObjectFactory(或 ObjectProvider)可关联某一类型的 Bean,仅提供一个 getObject() 办法用于返回指标 Bean 对象,ObjectFactory 对象被依赖注入或依赖查找时并未实时查找到关联类型的指标 Bean 对象,在调用 getObject() 办法才会依赖查找到指标 Bean 对象。

依据 ObjectFactory 的个性,能够说它提供的是 提早依赖查找。通过这一个性在 Spring 解决循环依赖(字段注入)的过程中就应用到了 ObjectFactory,在某个 Bean 还没有齐全初始化好的时候,会先缓存一个 ObjectFactory 对象(调用其 getObject() 办法可返回以后正在初始化的 Bean 对象),如果初始化的过程中依赖的对象又依赖于以后 Bean,会先通过缓存的 ObjectFactory 对象获取到以后正在初始化的 Bean,这样一来就解决了循环依赖的问题。

留神这里是 提早依赖查找 而不是提早初始化,ObjectFactory 无奈决定是否提早初始化,而须要通过配置 Bean 的 lazy 属性来决定这个 Bean 对象是否须要提早初始化,非提早初始化的 Bean 在 Spring 利用上下文刷新过程中就会初始化。

提醒:如果是 ObjectFactory(或 ObjectProvider)类型的 Bean,在被依赖注入或依赖查找时返回的是 DefaultListableBeanFactory#DependencyObjectProvider 公有外部类,实现了 ObjectProvider<T> 接口,关联的类型为 Object。

25. 简述 FactoryBean?

FactoryBean 关联一个 Bean 对象,提供了一个 getObject() 办法用于返回这个指标 Bean 对象,FactoryBean 对象在被依赖注入或依赖查找时,理论失去的 Bean 就是通过 getObject() 办法获取到的指标类型的 Bean 对象。如果想要获取 FactoryBean 自身这个对象,在 beanName 后面增加 & 即可获取。

咱们能够通过 FactoryBean 帮忙实现简单的初始化逻辑,例如在 Spring 继集成 MyBatis 的我的项目中,Mapper 接口没有实现类是如何被注入的?其实 Mapper 接口就是一个 FactoryBean 对象,当你注入该接口时,理论的到的就是其 getObject() 办法返回的一个代理对象,对于数据库的操作都是通过该代理对象来实现。

26. ObjectFactory、FactoryBean 和 BeanFactory 的区别?

依据其名称能够晓得其字面意思别离是:对象工厂,工厂 Bean

ObjectFactory、FactoryBean 和 BeanFactory 均提供依赖查找的能力。

  • ObjectFactory 提供的是提早依赖查找,想要获取某一类型的 Bean,须要调用其 getObject() 办法能力依赖查找到指标 Bean 对象。ObjectFactory 就是一个对象工厂,想要获取该类型的对象,须要调用其 getObject() 办法生产一个对象。
  • FactoryBean 不提供提早性,在被依赖注入或依赖查找时,失去的就是通过 getObject() 办法拿到的理论对象。FactoryBean 关联着某个 Bean,能够说在 Spring 中它就是某个 Bean 对象,无需咱们被动去调用 getObject() 办法,如果想要获取 FactoryBean 自身这个对象,在 beanName 后面增加 & 即可获取。
  • BeanFactory 则是 Spring 底层 IoC 容器,外面保留了所有的单例 Bean,ObjectFactory 和 FactoryBean 本身不具备依赖查找的能力,能力由 BeanFactory 输入。

27. @Bean 的解决流程是怎么的?

Spring 利用上下文生命周期,在 BeanDefinition(@Component 注解、XML 配置)的加载完后,会执行所有 BeanDefinitionRegistryPostProcessor 类型的处理器,Spring 外部有一个 ConfigurationClassPostProcessor 处理器,它会对所有的 配置类 进行解决,解析其外部的注解(@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean),其中 @Bean 注解标注的办法会生成对应的 BeanDefinition 对象并注册。具体步骤可查看后续文章:《死磕 Spring 之 IoC 篇 – @Bean 等注解的实现原理》

28. BeanFactory 是如何解决循环依赖?

前言,上面的“循环依赖”换成“循环依赖注入”比拟适合,在 Spring 中通过 depends-on 配置的依赖对象如果呈现循环依赖会抛出异样

阐明:这里的循环依赖指的是 单例模式 下的 Bean 字段注入 时呈现的循环依赖。结构器注入 对于 Spring 无奈主动解决(应该思考代码设计是否有问题),可通过提早初始化来解决。Spring 只解决 单例模式 下的循环依赖。

在 Spring 底层 IoC 容器 BeanFactory 中解决循环依赖的办法次要借助于以下 3 个 Map 汇合:

  1. singletonObjects(一级 Map),外面保留了所有曾经初始化好的单例 Bean,也就是会保留 Spring IoC 容器中所有单例的 Spring Bean;
  2. earlySingletonObjects(二级 Map),外面会保留从 三级 Map 获取到的正在初始化的 Bean
  3. singletonFactories(三级 Map),外面保留了正在初始化的 Bean 对应的 ObjectFactory 实现类,调用其 getObject() 办法返回正在初始化的 Bean 对象(仅实例化还没齐全初始化好),如果存在则将获取到的 Bean 对象并保留至 二级 Map,同时从以后 三级 Map 移除该 ObjectFactory 实现类。

当通过 getBean 依赖查找时会首先顺次从下面三个 Map 获取,存在则返回,不存在则进行初始化,这三个 Map 是解决循环依赖的要害。

例如两个 Bean 呈现循环依赖,A 依赖 B,B 依赖 A;当咱们去依赖查找 A,在实例化后初始化前会学生成一个 ObjectFactory 对象(可获取以后正在初始化 A)保留在下面的 singletonFactories 中,初始化的过程需注入 B;接下来去查找 B,初始 B 的时候又要去注入 A,又去查找 A,因为能够通过 singletonFactories 间接拿到正在初始化的 A,那么就能够实现 B 的初始化,最初也实现 A 的初始化,这样就避免出现循环依赖。

问题一 :为什么须要下面的  二级 Map

因为通过 三级 Map获取 Bean 会有相干 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(..) 的解决,防止反复解决,解决后返回的可能是一个代理对象

例如在循环依赖中一个 Bean 可能被多个 Bean 依赖,A -> B(也依赖 A)-> C -> A,当你获取 A 这个 Bean 时,后续 B 和 C 都要注入 A,没有下面的 二级 Map的话,三级 Map 保留的 ObjectFactory 实现类会被调用两次,会反复解决,可能呈现问题,这样做在性能上也有所晋升

问题二 :为什么不间接调用这个 ObjectFactory#getObject() 办法放入  二级 Map 中,而须要下面的 三级 Map

对于不波及到 AOP 的 Bean 的确能够不须要 singletonFactories(三级 Map),然而 Spring AOP 就是 Spring 体系中的一员,如果没有singletonFactories(三级 Map),意味着 Bean 在实例化后就要实现 AOP 代理,这样违反了 Spring 的设计准则。Spring 是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器在齐全创立好 Bean 起初实现 AOP 代理,而不是在实例化后就立马进行 AOP 代理。如果呈现了循环依赖,那没有方法,只有给 Bean 先创立代理对象,然而在没有呈现循环依赖的状况下,设计之初就是让 Bean 在齐全创立好后才实现 AOP 代理。

提醒:AnnotationAwareAspectJAutoProxyCreator 是一个 SmartInstantiationAwareBeanPostProcessor 后置处理器,在它的 getEarlyBeanReference(..) 办法中能够创立代理对象。所以说对于下面的 问题二,如果呈现了循环依赖,如果是一个 AOP 代理对象,那只能给 Bean 先创立代理对象,设计之初就是让 Bean 在齐全创立好后才实现 AOP 代理。

为什么 Spring 的设计是让 Bean 在齐全创立好后才实现 AOP 代理?

因为创立的代理对象须要关联指标对象,在拦挡解决的过程中须要依据指标对象执行被拦挡的办法,所以这个指标对象最好是一个“成熟态”,而不是仅实例化还未初始化的一个对象。

29. Spring 中几种初始化办法的执行程序?

有以下初始化形式:

  • Aware 接口:实现了 Spring 提供的相干 XxxAware 接口,例如 BeanNameAware、ApplicationContextAware,其 setXxx 办法会被回调,能够注入相干对象
  • @PostConstruct 注解:该注解是 JSR-250 的规范注解,Spring 会调用该注解标注的办法
  • InitializingBean 接口:实现了该接口,Spring 会调用其 afterPropertiesSet() 办法
  • 自定义初始化办法:通过 init-method 指定的办法会被调用

在 Spring 初始 Bean 的过程中下面的初始化形式的执行程序如下:

  1. Aware 接口的回调
  2. JSR-250 @PostConstruct 标注的办法的调用
  3. InitializingBean#afterPropertiesSet 办法的回调
  4. init-method 初始化办法的调用
正文完
 0