Spring IOC容器(控制反转和依赖注入)

Java知识点总结:想看的可以从这里进入

6、IoC容器


6.1、介绍

在创建Spring项目时,我们会发现,通过XML文件设置对象后,不需要我们使用 new 主动创建对象,而是变成从Spring中获取对象,而管理这些对象的容器就是IOC。

IOC是 Inversion of Control 的缩写,我们称为“控制反转”。它于1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。IOC理论提出的观点大体是:借助于“第三方”实现具有依赖关系的对象之间的解耦。

至于为什么要解耦合,其实想想我们平时带的机械手表就知道了,机械手表是由很多精密的齿轮等耦合在一起实现的,即便是其中一个很小的齿轮出现了错误,就会导致整个手表运转失误。所以我们理想的系统才需要解耦合,就是为了不至于因为其中某个小失误,导致整个系统崩溃,而且解耦的系统也方便我们排查错误进行修改。

image-20210530182856734

我们的Spring就是第三方,它通过IOC容器,使用各个Object完成互相之间解耦,这样的话,当你在实现o1的时候,根本无须再去考虑2、3、4了,它使对象之间的耦合程度尽可能达到了一种较低的水平。(在此之前我们要使用o1,需要同时把o2,o3,o4都创建一遍)

控制反转具体是什么?举个简单的例子:我们想喝一杯果汁方法有两个

  1. 自己准备材料、准备榨汁机,自己动手获取一杯饮料(制造果汁的控制权完全在自己手中,想喝只能自己买材料、自己动手制作)。
  2. 去饮品店购买,可以通过外卖、自己去买等多种方式(制造果汁的控制权在饮品店手中,我们只负责去买,至于怎么制作那就是饮品店自己的事了)。

以往的面向对象编程中,我们想在一个类中调用另一个类的对象和方法,需要使用 new 主动创建对象,以实现属性方法的调用,此时对象的控制权是在我们手中。但是控制反转就是将控制权交由第三方(IoC容器),通过该IoC容器根据描述信息去找寻使用者需要的资源,然后控制Java对象的实例化和初始化,管理对象和对象之间的依赖关系,这种控制权的转变就是控制反转的含义。其中IOC容器管理的java对象称为SpringBean。

2004年,Martin Fowler探讨了一个问题:既然IOC是控制反转,那么到底是哪些方面的控制被反转了呢?

经过详细地分析和论证后,他得出了答案:获得依赖对象的过程被反转了,它由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

Spring本质是一个生产管理Bean的第三方工厂,它按我们的需要生产管理Bean(第三方IOC容器通过我们的描述(XML、注解等),获取生产特定的对象Bean,并进行管理),所以控制反转是一种编程的思想,其中依赖注入是其解决Bean之间依赖的一种技术而Spring IOC实现的核心就是反射机制。学习这里时可以了解一下工厂模式。

IoC大大降低了对象之间的耦合,开发中甚至我们可以不用去理解,仅仅知道怎么使用就可以(但为了提升业务水平,还是需要从源头分析学习的)。

6.2、原理和实现

在 Java 软件开发过程中,系统中的各个对象之间、各个模块之间、软件系统和硬件系统之间,或多或少都存在一定的耦合关系,IOC就是通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度

它通过读取我们的xml文件中的配置,根据id标识获取Bean的全限定名,通过反射机制创建Bean对象,通过set方法注入值,然后通过getBean方法获取相应的对象。

image-20210713110815051

而Spring是通过 BeanDefinition 这样一个对象来描述它所管理的所有的Bean以及对象间的依赖关系,它将读取的Bean的各种信息统一都封装成一个个的 BeanDefinition 对象,这样就弱化了对Bean的定义,无论是通过什么方式获取的Bean对象,最终都是封装成 BeanDefinition 注册到IOC容器中,这样呢,IOC就不必关注我们各种各样的类和对象了,只需要统一操作BeanDefinition 即可,在降低耦合程度上,还增加了扩展性和灵活性。依赖反转功能都是围绕这个BeanDefinition的处理来完成的。

IOC容器的底层最核心的就是两个 Bean 工厂 接口:BeanFactory、ApplicationContext。IOC就是基于这两个接口来设计的

6.2.1、BeanFactory

是Spring中最顶层的的 factory接口(org.springframework.beans.factory.BeanFactory),采用了Java经典的工厂名模式,它也是最简单的IOC容器,它定义了IOC的基本规范,在 BeanFactory 的基础上 Spring 通过继承逐层扩充容器的能力来实现一个完整的IOC。

BeanFacotry是Spring 内部的使用接口,不提供给开发人员进行使用。这种方式采用了懒加载,容器在启动后加载配置文件时并不会马上创建对象,而是在程序中获取使用的时候才进行创建所需对象。

BeanFactory结构

基于BeanFactory设计的层次结构,BeanFactory定义了基本的IoC容器的规范(如getBean可以获取Bean对象等),其下三个子接口:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。

  • HierarchicalBeanFactory接口增加了getParentBeanFactory(),使BeanFactory具备了双亲IoC容器的管理功能
    • 其子接口ConfigurableBeanFactory定义了一些对BeanFactory的配置功能(比如通过setParentBeanFactory()设置双亲IoC容器,通过addBeanPostProcessor()配置Bean后置处理器等等)。
  • ListableBeanFactory 接口表示这些 Bean 是可列的
  • AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。
  • DefaultListableBeanFactory是BeanFactory层次中的最终实现,是一个简单IoC容器的实现。像其他IoC容器比如XmlBeanFactory等等,都是在DefaultListableBeanFactory的基础上做扩展。
它内部定义了一些基础方法
1、Object getBean(String name):通过Bean的对象名获取其实例(如果ioc有两个一样bean,那么在获取会出错)
    可以通过类型:类.class 获取
    可以通过bean标签中设置的id值获取
    可以通过 id+类型 获取
2、boolean containsBean(String name):查看bean是否存在
3、boolean isSingleton(String name):是否是单例模式
4、boolean isPrototype(String name):是否是原型模式
5、boolean isTypeMatch(String name, ResolvableType typeToMatch):查看一个bean对象是typeToMatch类型的
6、String[] getAliases(String name):返回bean 的别名(bean标签内name属性设置的名称)
7、Class<?> getType(String name):返回 FactoryBean 创建的对象类型
image-20230226145632205

BeanFactory和FactoryBean:

  • BeanFactory是IOC容器

  • FactoryBean是一个工厂Bean,可以生成某一个类型 Bean 的实例,可以让我们自定义 Bean 的创建过程。它是IOC中一个具有特色的工厂Bean。

    Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

6.2.2、ApplicationContext

因为原始的BeanFactory的功能很简单,很多技术和插件无法支持,所以衍生出了ApplicationContext 接口,它是BeanFactory的子接口,包含了BeanFactory的所有功能,并在其基础上进行了扩展,增加了许多面向框架的特性,同时对应用环境做了许多适配。

  1. 支持信息源,可以实现国际化。(MessageSource接口)
  2. 访问资源。(ResourcePatternResolver接口)
  3. 支持应用事件。(ApplicationEventPublisher)
  4. 在ApplicationContext中提供的附加服务

它采用的策略是立即加载,即在Spring容器启动,加载配置文件后,就立即创建对象。因为使用Spring框架一般都会整合其他框架,所以把这些耗时耗内存的都在项目启动的时候进行处理更加合适,所以一般开发人员使用 ApplicationContext接口。

ApplicationContext

ApplicationContext有几个常用的实现类:(根据不同的配置方式使用不同的类来实例化)

  • 子类ClassPathXmlApplicationContext(“xml文件”):从类路径ClassPath中加载指定的 XML 配置文件

  • 子类FileSystemXmlApplicationContext(“xml文件”):加载磁盘路径下的XML配置文件

  • 子类AnnotationConfigApplicationContext(类.class):用于读取(@Configuration)修饰的启动类

  • 子类XmlWebApplicationContext:从web中获取XML配置,默认情况下,配置将从“/WEB-INF/applicationContext.xml”获取根上下文,从“/WEB-INF/test-servlet.xml”获取具有命名空间“test-servlet”的上下

6.3、Bean的管理

IOC通常通过三种方式来管理Bean对象

  1. 基于xml配置文件的方式实现
  2. 在Java接口和类中实现配置(通过注解 @Beam)
  3. 隐式的Bean的发现机制和自动装配

这几种方式应该遵循约定优于配置的原则,优先选择通过 隐式的Bean的发现机制和自动装配,其次选择在Java接口和类中实现配置,最后选择XML中配置。实际使用时是以三者混合使用,但优先级不同。

6.3.1、XML文件管理

通过XML文件装配对象

6.3.2、注解管理

使用注解装配Bean

6.3.3、bean的作用域

默认情况下,所有的 Bean 都是单例的,也就是说在整个 Spring 应用中, Bean 的实例只有一个。我们可以在 bean 标签中添加 scope 属性来配置 Bean 的作用范围。

Spring 5 共提供了 6 种 scope 作用域:

  • singleton:默认值,单例模式

    • 实例化个数:1
    • 实例化机制:创建容器时创建(默认使用ApplicationContext),存储在高速缓存中
    • 生命周期:
      • 创建:应用加载,创建容器,读取配置文件,对象直接创建
      • 运行:只要容器在,对象就一直存在
      • 销毁:当应用卸载时,销毁容器,对象随之销毁
  • prototype:原型模式

    • 实例化个数:多个
    • 实例化机制:只有每次注入或者获取Bean时,才会创建对象
    • 生命周期:
      • 创建:当使用对象时,对象创建
      • 运行:只要对象在使用,就一直存活
      • 销毁:对象一定时间内不使用,被java的垃圾回收期回收。
  • request

    • 实例化机制:每次请求都会创建一次
    • 生命周期
      • 创建:每次Http请求,使用对象时,对象创建
      • 运行:和Http请求中的request对象相同
      • 销毁:处理完请求后销毁
  • session:只能在web开发中使用

    • 实例化机制:在一次会话期内创建一次
    • 生命周期
      • 创建:每一个会话期,使用对象时,对象创建
      • 运行:和一次session相同
      • 销毁:随session销毁而销毁
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值