聊聊Spring中IOC的基本原理

news/2024/7/24 11:25:41 标签: spring, java, IOC, 控制反转, 笔面试

| 作者:江夏

| 知乎:https://www.zhihu.com/people/1024-paper-96

| GitHub:https://github.com/JiangXia-1024?tab=repositories

| 博客地址:https://blog.csdn.net/qq_41153943

| 掘金:https://juejin.cn/user/651387938290686

本文大概5091字,建议阅读13分钟

前言

在我们学习spring和面试的过程中,有一个核心的内容就叫做IOCIOC(Inversion of Control),中文翻译即“控制反转”,它不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将设计好的对象交给容器控制,而不是传统的在对象内部直接控制。

所以通过以上的介绍,我们或许有这样的疑问:谁控制谁,控制什么,怎么反转,哪些方面反转了?

带着这些疑问,我们来一起学习下Ioc的知识!

IOC_21">1. IOC理论的诞生

前面说到Ioc并不是一种技术,而是一种理论一种设计思想,就跟面向对象一样。在采用面向对象设计思想开发的系统中,它底层的实现是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。就像一辆汽车,它的运行其实是有很多个独立的器械之间的相互合作来推动的。比如发动机、底盘、离合器、变数器、机箱等等,这些主要的零件又是由各类各样的螺丝、转轮等组成。

就像组成汽车的各个零件对象相互配合,少了谁也不行一样。系统的对象之间的耦合关系即是无法避免的,也是十分必要的,因为这是协同工作的基础。但是随着一些大型系统的规模越来越庞大,对象之间的依赖关系也变得越来越复杂,这就导致出现对象之间的多重依赖性关系。对象之间耦合关系过于复杂的系统,它的设计、迭代也会变得复杂,因为这些对象和系统之前的关系会出现牵一发而动全身的情形。

为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”,这个理论已经被成功地应用到实践当中,比如现在很多的项目都采用了IOC设计思想的Spring框架。

IOC_30">2. 什么是IOC

IOC是Inversion of Control的缩写,翻译成中文即“控制反转”。

对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。举个例子,如果我们想吃宫保鸡丁,然后自己做,这时候我们就需要去买食材、然后做饭、吃完饭之后还得处理垃圾、洗碗,这个过程其实是很复杂的,因为我们必须自己设计和面对整个过程。但是如果采用了Ioc的设计思想,我们这时候只需要做一件事事情——去饭店点菜。不论我们想吃宫保鸡丁、水煮肉片、烤鱼还是什么任何其他口味、菜系或者类型的食物,饭店都会按照我们的需求提供给我们,吃完之后我们也不需要处理垃圾(销毁对象)。这个整个的过程都不需要我们自己参与控制,而是通过饭店这样一个类似于容器的来进行控制。

Spring的Ioc设计思想就是这样,所有的类都会在Spring容器中登记,告诉Spring你是个什么东西,需要什么东西,然后Spring就会在系统运行到恰当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西,当你不需要的时候就在销毁。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。也就是借助于“第三方”工具(IOC容器)来实现具有依赖关系的对象之间的解耦,类似于下图:

图片

通过使用中间的IOC容器,使得A、B、C、D这4个对象没有了直接的耦合关系,它们之间的传动全部依靠“第三方”的Ioc容器。

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

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

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

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

IOC_53">3. 什么IOC容器

前面说到了控制反转的核心是Ioc容器,那么什么是Ioc容器呢?

Ioc容器就是具有依赖注入功能的容器,Ioc容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring IOC容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IOC容器最基本功能;而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。简单说, BeanFactory提供了IOC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。

由Ioc容器管理的那些对象一般称之为它Bean, Bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别。

所以Ioc容器实际上就是一个map,里面存的是各种对象,在项目启动的时候会读取配置文件中的bean节点,根据全限定类名使用反射创建对象放到map里,扫描到打上@repository、@service、@controller、@component这些注解的类然后通过使用反射创建对象放到map中。这时候map中就存在各种对象了,后面代码在需要使用到里面的对象的时候,再通过依赖注入(DI)。

4. 依赖注入(DI)

前面说到项目启动的一开始通过读取配置文件将对象全部存放到ioc容器中,然后再在合适的时机进行依赖注入。我们知道控制反转就是将获得依赖对象的控制权交给了ioc容器。也就是说反转的具体就是获得依赖对象的过程。

控制被反转之后,获得依赖对象的过程就由本来的自动生成变为了交给IOC容器来主动注入。所以控制反转的另一个名字叫做依赖注入(DI,Dependency Injection)。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中,依赖注入也就是实现Ioc的方法。

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

比如对象A依赖于对象B,当对象A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂(BeanFactory),需要什么,就会给你送去什么,然后直接使用就可以了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办,这也是工厂方法设计模式的思想了。Ioc就是典型的工厂模式,通过sessionfactory去注入实例。

5. 举个小栗子

前面介绍了这么多,接下来看一个简单的例子。

创建一个spring项目,工程结构如下图所示:

图片

首先创建一个helloservice接口,然后创建一个其实现类,代码分别如下:

public interface HelloService {
    public void say();
}
public class HelloServiceImp implements HelloService {
    public void say(){
        System.out.println("Hello World");
    }
}

接口和其实现类以及方法都开发好了,那如何使用Spring IOC容器来管理它们呢?这就需要配置文件,让IOC容器知道要管理哪些对象。创建一个配置文件helloworld.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- id 表示组件的名字,class表示组件类 -->
    <bean id="helloService" class="com.jiang.HelloServiceImp" />
</beans>

最后我们就需要获取IOC容器并实现功能。

首先应该实例化一个IOC容器,然后从容器中获取需要的对象,然后调用接口完成我们需要的功能,创建一个测试代码代码示例如下:

package com.jiang;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestHello {
    @Test
    public void test(){
        // 1、读取配置文件实例化一个IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("helloword.xml");
        // 2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现”
        HelloService helloService = context.getBean("helloService",HelloService.class);
        // 3、执行业务逻辑
        helloService.say();
    }
}

输出结果如下:

图片

6、总结

以上就是我对于ioc的理解。ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是耦合度。这样就提高系统的可维护性,而且非常便于进行单元测试。并且各个组件之间的依赖性降低了,就提高了系统的可重复性和可拓展性

ioc中有两个核心的可以理解为技术点的就是工厂设计模式以及反射。IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用反射,根据配置文件中给出的类名生成相应的对象。

如果你觉得本文不错,就点赞分享给更多的人吧!

如果你觉得文章有不足之处,或者更多的想法和理解,欢迎指出讨论!
在这里插入图片描述

相关推荐:

  • Spring注解(三):@scope设置组件作用域

  • Spring常用注解大全,值得你的收藏!!!

  • Spring注解(七):使用@Value对Bean进行属性赋值

  • SpringBoot开发Restful风格的接口实现CRUD功能

  • Spring注解(六):Bean的生命周期中自定义初始化和销毁方法的四种方式
    图片


http://www.niftyadmin.cn/n/625365.html

相关文章

分布式缓存中间件Redis入门

| 作者&#xff1a;江夏 | 知乎&#xff1a;https://www.zhihu.com/people/1024-paper-96 | GitHub&#xff1a;https://github.com/JiangXia-1024?tabrepositories | CSDN&#xff1a;https://blog.csdn.net/qq_41153943 | 掘金&#xff1a;https://juejin.cn/user/65138…

简单聊聊负载均衡的那些事

| 作者&#xff1a;江夏 | 知乎&#xff1a;https://www.zhihu.com/people/1024-paper-96 | GitHub&#xff1a;https://github.com/JiangXia-1024?tabrepositories | 博客地址&#xff1a;https://blog.csdn.net/qq_41153943 | 掘金&#xff1a;https://juejin.cn/post/6…

IDEA最详细配置让开发效率起飞,建议收藏!

| 作者&#xff1a;江夏 | CSDN&#xff1a;blog.csdn.net/qq_41153943 | 掘金&#xff1a;juejin.cn/user/651387… | 知乎&#xff1a;www.zhihu.com/people/1024… | GitHub&#xff1a;github.com/JiangXia-10… 本文大概2165字&#xff0c;建议阅读15分钟 1、前言 In…

从零开始学设计模式(一):什么是设计模式

作者的其他平台&#xff1a; | CSDN&#xff1a;https://blog.csdn.net/qq_41153943 | 掘金&#xff1a;https://juejin.cn/user/651387938290686 | 知乎&#xff1a;https://www.zhihu.com/people/1024-paper-96 | GitHub&#xff1a;https://github.com/JiangXia-1024?t…

Java正则表达式学习

作者的其他平台&#xff1a; | CSDN&#xff1a;https://blog.csdn.net/qq_41153943 | 掘金&#xff1a;https://juejin.cn/user/651387938290686 | 知乎&#xff1a;https://www.zhihu.com/people/1024-paper-96 | GitHub&#xff1a;https://github.com/JiangXia-1024?t…

SourceTree使用教程图文详解

作者的其他平台&#xff1a; | CSDN&#xff1a;https://blog.csdn.net/qq_41153943 | 掘金&#xff1a;https://juejin.cn/user/651387938290686 | 知乎&#xff1a;https://www.zhihu.com/people/1024-paper-96 | GitHub&#xff1a;https://github.com/JiangXia-1024?t…

Drop、Truncate和Delete究竟怎么删除

作者&#xff1a;江夏 | 知乎&#xff1a;www.zhihu.com/people/1024… | GitHub&#xff1a;github.com/JiangXia-10… | CSDN&#xff1a;blog.csdn.net/qq_4115394… | 掘金&#xff1a;juejin.cn/user/651387… | 公众号&#xff1a;1024笔记 本文大概2176字&#xff…

从零开始学设计模式(二):单例模式

作者的其他平台&#xff1a; | CSDN&#xff1a;blog.csdn.net/qq_4115394… | 掘金&#xff1a;juejin.cn/user/651387… | 知乎&#xff1a;www.zhihu.com/people/1024… | GitHub&#xff1a;github.com/JiangXia-10… | 公众号&#xff1a;1024笔记 本文大概6503字&am…