深入了解Spring IOC

发布时间:2024-02-24 18:30

1.你对Spring IoC 的理解?

IOC容器实际就是指的spring容器。从容器概念、控制反转、依赖注入三个方面回答这个问题:
1.容器概念
实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、@repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里、扫描到上述注解的类,通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI(依赖)注入(autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。

2.控制反转
没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

3.依赖注入
“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

2. IoC 和 DI 的区别?

DI 依赖注入不完全等同于 IoC,Spring 框架是一个 IoC 容器的实现,DI 依赖注入是它的实现的一个方式或策略,IOC提供依赖查找和依赖注入两种依赖处理,管理着 Bean 的生命周期。

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

3.DI依赖注入的方式区别(构造器注入和 Setter 注入)

构造器注入:通过构造器的参数注入相关依赖对象

Setter 注入:通过 Setter 方法注入依赖对象,也可以理解为字段注入

对于两种注入方式的看法:

  • 构造器注入可以避免一些尴尬的问题,比如说状态不确定性地被修改,在初始化该对象时才会注入依赖对象,一定程度上保证了 Bean 初始化后就是不变的对象,这样对于我们的程序和维护性都会带来更多的便利;
  • 构造器注入不允许出现循环依赖,因为它要求被注入的对象都是成熟态,保证能够实例化,而 Setter 注入或字段注入没有这样的要求;
  • 构造器注入可以保证依赖的对象能够有序的被注入,而 Setter 注入或字段注入底层是通过反射机制进行注入,无法完全保证注入的顺序;
  • 如果构造器注入出现比较多的依赖导致代码不够优雅,我们应该考虑自身代码的设计是否存在问题,是否需要重构代码结构。

Spring Bean 的生命周期?
生命周期:

1-3属于 BeanDefinition 配置元信息阶段,BeanDefinition 是 Spring Bean 的“前身”,其内部包含了初始化一个 Bean 的所有元信息,在 Spring 初始化一个 Bean 的过程中需要根据该对象生成一个 Bean 对象并进行一系列的初始化工作。

  1. Spring Bean 元信息配置阶段,可以通过面向资源(XML 或 Properties)、面向注解、面向 API 进行配置
  2. Spring Bean 元信息解析阶段,对上一步的配置元信息进行解析,解析成 BeanDefinition 对象,该对象包含定义 Bean 的所有信息,用于实例化一个 Spring Bean
  3. Spring Bean 元信息注册阶段,将 BeanDefinition 配置元信息 保存至 BeanDefinitionRegistry 的 ConcurrentHashMap 集合中

4-5 属于实例化阶段,想要生成一个 Java Bean 对象,那么肯定需要根据 Bean 的元信息先实例化一个对象;

  1. Spring BeanDefinition 合并阶段,定义的 Bean 可能存在层次性关系,则需要将它们进行合并,存在相同配置则覆盖父属性,最终生成一个 RootBeanDefinition 对象
  2. Spring Bean 的实例化阶段,首先的通过类加载器加载出一个 Class 对象,通过这个 Class 对象的构造器创建一个实例对象,构造器注入在此处会完成。在实例化阶段 Spring 提供了实例化前后两个扩展点。

接下来的 6 属于属性赋值阶段,实例化后的对象还是一个空对象,我们需要根据 Bean 的元信息对该对象的所有属性进行赋值;

  1. Spring Bean 属性赋值阶段,在 Spring 实例化后,需要对其相关属性进行赋值,注入依赖的对象。首先获取该对象所有属性与属性值的映射,可能已定义,也可能需要注入,在这里都会进行赋值(反射机制)。提示一下,依赖注入的实现通过 CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和 AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)两个处理器实现的。

后面的 789 属于初始化阶段,在 Java Bean 对象生成后,可能需要对这个对象进行相关初始化工作才予以使用

  1. Aware 接口回调阶段,如果 Spring Bean 是 Spring 提供的 Aware 接口类型(例如 BeanNameAware、ApplicationContextAware),这里会进行接口的回调,注入相关对象(例如 beanName、ApplicationContext)
  2. Spring Bean 初始化阶段,这里会调用 Spring Bean 配置的初始化方法,执行顺序:@PostConstruct 标注方法、实现 InitializingBean 接口的 afterPropertiesSet() 方法、自定义初始化方法。在初始化阶段 Spring 提供了初始化前后两个扩展点(BeanPostProcessor 的 postProcessBeforeInitialization、postProcessAfterInitialization 方法)
  3. Spring Bean 初始化完成阶段,在所有的 Bean(不是抽象、单例模式、不是懒加载方式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象,如果是 SmartInitializingSingleton 类型则调用其 afterSingletonsInstantiated() 方法,这里也属于 Spring 提供的一个扩展点

最后面的 1011 属于销毁阶段,当 Spring 应用上下文关闭或者主动销毁某个 Bean 时,可能需要对这个对象进行相关销毁工作,最后等待 JVM 进行回收。

  1. Spring Bean 销毁阶段,当 Spring 应用上下文关闭或者你主动销毁某个 Bean 时则进入 Spring Bean 的销毁阶段,执行顺序:@PreDestroy 注解的销毁动作、实现了 DisposableBean 接口的 Bean 的回调、destroy-method 自定义的销毁方法。这里也有一个销毁前阶段,也属于 Spring 提供的一个扩展点,@PreDestroy 就是基于这个实现的
  2. Spring 垃圾收集(GC)

上面

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号