【spring】(二)理解Spring IoC


本文参考
https://www.cnblogs.com/xdp-gacl/p/4249939.html
https://www.cnblogs.com/wmyskxz/p/8824597.html

一、IOC是什么

IoC ——Inversion of control,即控制反转,不是什么技术,而是一种思想。在Java 开发中,意味着将你设计好的
对象交给容器控制,而不是传统的自己直接控制。理解IOC,关键在于理解谁控制谁,控制什么,为何事反转,哪些地方反转了。那我们来分析一下。

  • 谁控制谁,控制什么:传统的Java SE程序设计中,我们直接在对象内部通过new进行创建对象,是程序主动的去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由IoC容器来控制对象的创建;谁控制谁?当然是IoC容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括文件等等

  • 为何是反转?哪些地方反转了: 有反转就由正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何事反转?因为由容器帮我们查找及注入依赖对象,对象只是被动地接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

传统程序设计如图,都是主动创建相关对象后再组合起来。
在这里插入图片描述
有了IoC/DI容器后,在客户端类中不再主动去创建这些对象了,如图
在这里插入图片描述

二、IoC能做什么

IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

三、IoC和DI

DI-Dependency Injection,即依赖注入:组件之间的依赖关系由容器在运行期间决定,形象地说,即由容器动态地将某个依赖关系注入到组件之中,依赖注入的目的并非为软件紫铜带来更多的功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:谁依赖谁,为什么需要依赖,谁注入谁。注入了什么?

  • 谁依赖谁:应用程序依赖于IoC容器
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”

四、Spring IoC容器

Spring会提供IoC容器来管理和容纳我们所开发的各种各样的Bean,并且我们可以从中获取各种发布在 Spring IoC 容器里的 Bean,并且通过描述可以得到它。

4.1 Spring IoC 容器的设计

Spring IoC容器的设计主要是基于以下两个接口

  • BeanFactory
  • ApplicationContext

其中 ApplicationContext 是 BeanFactory 的子接口之一,换句话说:BeanFactory 是 Spring IoC 容器所定义的最底层接口,而 ApplicationContext 是其最高级接口之一,并对 BeanFactory 功能做了许多的扩展,所以在绝大部分的工作场景下,都会使用 ApplicationContext 作为 Spring IoC 容器。

在这里插入图片描述

4.1.1 BeanFactory

从上图中我们可以看到,BeanFactory位于设计的最底层,它提供了Spring IoC最底层。为此,我们先看看该类中提供了哪些方法。在这里插入图片描述
由于这个接口的重要性,所以有必要在这里做一下简短的说明:

  • 【getBean】对应了多个方法来获取配置给Spring IoC容器的Bean。
    1. 按照类型拿bean:
      bean = (Bean) factory.getBean(Bean.class);
      
      要求在Spring中只配置了一个这种类型的实例,否则报错(如果有多个那 Spring 就懵了,不知道该获取哪一个)
    2. 按照bean的名字拿bean:
      bean = (Bean) factory.getBean("beanName");
      
      这种方式不太安全,IDE不会检查其安全性(关联性)
    3. 按照名字和类型拿bean(推荐)
      bean = (Bean) factory.getBean("beanName", bean.class);
      
  • 【isSingleton】用于判断是否单例,如果判断为真,其意思是该Bean在容器中是作为一个唯一单例存在的。
    【isPrototype】则相反,如果判断为真,意思是当你从容器中获取Bean,容器就为你生成一个新的实例。
    注意;在默认情况下,isSingleton为true,而isPrototype为false
  • 关于type的匹配,这是一个按 Java 类型匹配的方式
  • 【getAliases】方法是获取别名的方法
    这就是Spring IoC最底层的设计,所有关于Spring Ioc的容器将会遵守它所定义的方法

4.1.2 ApplicationContext

根据 ApplicationContext 的类继承关系图,可以看到 ApplicationContext 接口扩展了许许多多的接口,因此它的功能十分强大,所以在实际应用中常常会使用到的是ApplicationContext接口,因为BeanFactpry的方法和功能 ApplicationContext 的方法和功能较多。

通过上一篇 IoC 的例子,我们来认识一个 ApplicationContext 的子类——ClassPathXmlApplicationContext。

1、先在【src】目录下创建一个 【bean.xml】 文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 通过 xml 方式装配 bean -->
    <bean name="source" class="pojo.Source">
        <property name="fruit" value="橙子"/>
        <property name="sugar" value="多糖"/>
        <property name="size" value="超大杯"/>
    </bean>
</beans>

这里就定义了一个bean,这样Spring IoC容器在初始化的时候就能找到它们,然后使用ClassPathXmlApplicationContext 容器就可以将其初始化:

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Source source = (Source) context.getBean("source", Source.class);

System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());

这样就会使用Application的实现类ClassPathXmlApplicationContext去初始化Spring IoC容器,然后开发者就可以通过IoC容器来获取资源了

关于 Spring Bean 的装配以及一些细节,会在下一篇文章中讲到

4.1.3 ApplicationContext 常见实现类:

1、ClassPathXmlApplicationContext:
读取classpath中的资源:

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

2、FileSystemXmlApplicationContext:
读取指定路径的资源:

ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");

3、XmlWebApplicationContext:
需要在web的环境下才可以运行

XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 这时并没有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext对象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路径,开头的斜线表示Web应用的根目录
ac.refresh(); // 初始化容器

4.1.4 BeanFactory 和 ApplicationContext 的区别

  • BeanFactory
    是Spring中最底层的接口,只提供最简单的IoC功能,负责配置、创建和故哪里bean。在应用中,一般不使用BeanFactory,而推荐使用ApplicationContext(应用上下文),原因如下:
  • ApplicationContext
    1、继承了BeanFactory,拥有了基本的IoC功能;
    2、除此之外,ApplicationContext还提供了以下功能:
    (1)支持国际化
    (2)支持消息机制
    (3)支持统一的资源加载
    (4)支持AOP功能

4.2 Spring IoC的容器的初始化和依赖注入

虽然Spring IoC容器的生成十分的复杂,但是大体了解一下Spring IoC初始化的过程还是必要的,这对于理解Spring的一系列行为是很有帮助的
注意:Bean的定义和初始化在Spring IoC容器是两大步骤,它是先定义,然后初始化和依赖注入的

  • Bean的定义分为3步

1、Resource定位
Spring IoC 容器先根据开发者的配置,进行资源的定位,在Spring的开发中,通过XML或者注解都是十分常见的方式,定位的内容是由开发者提供的

2、BeanDefinition 的载入
这个时候只是将 Resource 定位到的信息,保存到 Bean 定义(BeanDefinition)中,此时并不会创建 Bean 的实例。

3、BeanDefinition 的注册
这个过程就是将 BeanDefinition 的信息发布到 Spring IoC 容器中
注意:此时仍然没有对应的 Bean 的实例。

做完了以上 3 步,Bean 就在 Spring IoC 容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给 Bean,那么它还不能完全使用。

对于初始化和依赖注入,Spring Bean 还有一个配置选项——【lazy-init】,其含义就是是否初始化 Spring Bean。在没有任何配置的情况下,它的默认值为 default,实际值为 false,也就是 Spring IoC 默认会自动初始化 Bean。如果将其设置为 true,那么只有当我们使用 Spring IoC 容器的 getBean 方法获取它时,它才会进行 Bean 的初始化,完成依赖注入。

4.3 IoC 是如何实现的

最后我们简单说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? 无外乎:

  1. 读取标注或者配置文件,看看JuiceMaker依赖的是哪个Source,拿到类名
  2. 使用反射的API,基于类名实例化对应的对象实例
  3. 将对象实例,通过构造函数或者 setter,传递给 JuiceMaker

我们发现其实自己来实现也不是很难,Spring实际也就是这么做的。这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能,所以大家就别去造轮子啦!希望了解IoC更多实现细节不妨通过学习Spring的源码来加深理解!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值