控制反转(IoC)是什么?


学习Spring框架的小伙伴肯定都知道Spring框架的核心是IoC容器和AOP模块。
也都听说过 Spring是包含众多工具方法的IoC容器
IoC容器是Spring框架的底层基础,负责管理对象的创建和对象依赖关系的维护

IoC是一种思想,而DI就是实现这个思想的一种方法。

那么本文就带大家来了解什么是IoC

二、IoC是什么?

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制,对于spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系

在开发中的表现就是:
当需要一个对象时,不再自己通过new来创建对象,而是把创建对象的任务交给容器,在程序中只需要依赖
注入(DI)。这个容器就成为IoC容器。

三、具体程序开发举例

现在有这样的需求:造一辆车
我们根据需求来梳理思路:

我们先设计轮子(Tire),然后根据轮子的大小来设计地盘(Bottom),然后根据底盘设计车身(Framework),然后再由车身设计出整个汽车。

这里就出现了一个依赖关系:
汽车依赖车身,车身依赖底盘,底盘依赖轮子

在这里插入图片描述

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
}

public class Car {
    private Framework framework;
    public Car() {
        framework = new Framework();
        System.out.println("car init ....");
    }
    public void run(){
        System.out.println("car run ....");
    }
}

public class Framework {
        private Bottom bottom;
        public Framework() {
             bottom = new Bottom();
            System.out.println("framework init....");
        }
}

public class Bottom {
    private Tire tire;
    public Bottom() {
        tire = new Tire();
        System.out.println("bottom init....");
    }
}

public class Tire {
    private int size = 17;

    public Tire( ){
        System.out.println("Tire init....");
    }
}

运行程序

在这里插入图片描述

当车轮的尺寸需要随着不同的需求而变更时,那么底盘,车身,汽车都要受到波动。
在这里插入图片描述
这样的设计,导致耦合太高,当一部分的代码需求变更时,会影响其他很多代码,增加了改动的成本

那么如何解决呢?

我们尝试换⼀种思路, 我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计
底盘,最后根据底盘来设计轮⼦. 这时候,依赖关系就倒置过来了:轮⼦依赖底盘, 底盘依赖⻋⾝,
⻋⾝依赖汽⻋
在这里插入图片描述

我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,
⾃⼰也要跟着修改.
此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不
需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本⾝也⽆需修
改任何代码,这样就完成了程序的解耦.

在这里插入图片描述

public class Main {
    public static void main(String[] args) {
        Tire tire = new Tire(18,"red");
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }
}

这样做以后,我们传入的都是对象,当一个对象属性发生改变时,不会影响其他部分的正常运行。
达到解耦合的作用

我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了Framework,Framework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发生的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了.

这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。

在这里插入图片描述

四、控制反转(IoC)有什么用?

  1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了
  2. 解耦合:我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度

看到这是不是还有点懵,还没真正的搞懂什么是IoC,那么接下来就会使用来让你真正的认识IoC。

五、Bean的存储

在这一部分,会真正的使用IoC,来对IoC有一个深刻且全面的认识。
首先我们先搞清楚概念Bean是什么东西?

根据Bing搜索:
在这里插入图片描述

简而言之Bean就是容器内的对象。

这里的注解就是告诉Spring这里的内容已经放置到容器内了

要将创建对象的控制权交给Spring,就要事先声明给Spring哪些是需要进行控制的。
那么实现方式有两种

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration
  2. 方法注解:@Bean

getBean

ApplicationContext context = SpringApplication.run(IoCApplication.class, args);

这是启动类的代码,这里返回的ApplicationContext就是Spring的运行环境。

这里的context就相当于是一个容器了,那么要如何从容器中拿到对象呢?

这里就要使用getBean这个方法了。

getBean方法括号内常用的传参方式有三种

  1. 根据bean的名称取
  2. 根据bean的类型取
  3. 根据bean的名称和类型取
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
举例如下
这是在后面要讲解的@Controller注解

@Controller
public class Usercontroller {
    public void DoController() {
        System.out.println("Do Controller....");
    }
}
@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
       ApplicationContext context =  SpringApplication.run(IoCApplication.class, args);
        Usercontroller  bean  = context.getBean(Usercontroller.class);
        bean.DoController();

    }
}

在这里插入图片描述

getBean传参注意

这里要注意,使用名称来获取时,使用小驼峰的命名规范。
当类名前两个字母是连续大写时,就不需要变成小驼峰。
如果需要调用方法,这里需要转型

小驼峰

这有两个注意点:

  1. 小驼峰
    在这里插入图片描述
    类的方法名是UserController
    而我们使用getBean的方法名来获取bean对象的时候,就需要将类名转换为小驼峰的形式,也就是userController

在这里插入图片描述
2. 类型转换
在使用类名获取bean对象时,我们还要进行类型的转换!

两个连续大写字母开头的类名

这时就不需要变成小驼峰的形式。

这是bean的名称的内部实现方式的源码
在这里插入图片描述

1.1@Controller(控制器存储)

@Controller
public class Usercontroller {
    public void DoController() {
        System.out.println("Do Controller....");
    }
}


@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
       ApplicationContext context =  SpringApplication.run(IoCApplication.class, args);
        Usercontroller  bean  = context.getBean(Usercontroller.class);
        bean.DoController();

    }
}

在这里插入图片描述

在平常,我们调用对象的方法,首先需要先创建对象,然后再调用对应的方法。
现在我们

1.2@Service(服务存储)

@Service
public class UserService {
    public void DoService(){
        System.out.println("do service...");
    }
}

@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
       ApplicationContext context =  SpringApplication.run(IoCApplication.class, args);
       //从context中获取对象
       // Usercontroller  bean  = context.getBean(Usercontroller.class);
       //  bean.DoController();

        UserService bean1 = context.getBean(UserService.class);
        bean1.DoService();
    }
}

在这里插入图片描述
getBean方法括号内常用的传参方式有三种

通过类型来获取
通过名称来获取
通过类型和名称一同获取

在这里插入图片描述
在这里插入图片描述

这里要注意,使用名称来获取时,使用小驼峰的命名规范。
当类名前两个字母是连续大写时,就不需要变成小驼峰。
如果需要调用方法,这里需要转型

在这里插入图片描述


在这里插入图片描述

1.3@Repository(仓库存储)

@Repository
public class UserRepository {
    public void DoRepository() {
        System.out.println("Do Repository...");
    }
}

@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
     
        UserRepository bean2 = context.getBean(UserRepository.class);
        bean2.DoRepository();
    }
}

在这里插入图片描述

1.4@Component(组件存储)

@Component
public class UserComponent {
    public void DoComponent(){
        System.out.println("Do Component");

    }
}
@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
 
        UserComponent bean3 = context.getBean(UserComponent.class);
        bean3.DoComponent();

    }
}

在这里插入图片描述

1.5@Configuration(配置存储)

@Configuration
public class UserConfiguration {
    public void DoConfiguration() {
        System.out.println("UserConfiguration");
    }
}

@SpringBootApplication
public class IoCApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
 
        UserConfiguration bean4 = context.getBean(UserConfiguration.class);
        bean4.DoConfiguration();
    }
}

在这里插入图片描述

五种类注解的联系

在这里插入图片描述

查看@Controller / @Service / @Repository / @Configuration的源码发现,这些注解都包含一个@Component的注解,说明四个注解都是@Component的子类。

方法注解@Bean

@Bean必须搭配类注解才能使用

public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(IoCApplication.class, args);

        UserInfo bean = context.getBean(UserInfo.class);
        System.out.println(bean);
    }
@Data
public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;
}
public class BeanConfig {
    @Bean
    public UserInfo getUserInfo() {
            UserInfo userInfo =  new UserInfo();
            userInfo.setAge(18);
            userInfo.setId(11);
            userInfo.setName("张三");
            return userInfo;
    }
}

我们运行,出现如下报错,这是因为如果我们要使用@Bean时, 必须要在类前,也就是在BeanConfig这部分代码中,提前声明告诉Spring这里面有需要管理的对象。
在这里插入图片描述

此时在BeanConfig中加入@Configuration注解,就运行成功了。

当有多个构造方法时,需要使用类名来获取bean对象。

在这里插入图片描述
如上代码,有两个构造方法,此时再用类型传参就会报错

这时就需要使用类名来传参

        UserInfo userInfo = (UserInfo)context.getBean("getUserInfo");
        System.out.println(userInfo);

这时就能获取到UserInfo对象了。

当构造方法有参数时,需要创建参数对应类型的方法

这时就需要有参数的方法getUserInfo时,就需要再使用@Bean来创建一个String类型的name对象。

在这里插入图片描述

以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞

相关推荐

  1. Spring控制(IOC)什么

    2024-06-14 20:54:02       9 阅读
  2. Go中的控制 IoC

    2024-06-14 20:54:02       23 阅读
  3. Spring中控制究竟什么

    2024-06-14 20:54:02       8 阅读
  4. Spring的IoC控制)和 DI(依赖注入)

    2024-06-14 20:54:02       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-14 20:54:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-14 20:54:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-14 20:54:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-14 20:54:02       18 阅读

热门阅读

  1. 43、Flink 自定义窗口触发器代码示例

    2024-06-14 20:54:02       8 阅读
  2. 针对oracle系列数据库慢数据量大的问题

    2024-06-14 20:54:02       8 阅读
  3. git本地配置及IDEA下Git合并部分文件

    2024-06-14 20:54:02       8 阅读
  4. Spring Boot 3 整合 Redisson 实现分布式锁

    2024-06-14 20:54:02       8 阅读
  5. gitlab克隆本地切换p分支

    2024-06-14 20:54:02       8 阅读
  6. Webrtc支持FFMPEG硬解码之Intel(一)

    2024-06-14 20:54:02       7 阅读
  7. 前端开发中的热更新原理

    2024-06-14 20:54:02       9 阅读
  8. Linux/Ubuntu/Debian常用服务管理命令

    2024-06-14 20:54:02       9 阅读
  9. 【Spring Boot实现网页会话(Session )】

    2024-06-14 20:54:02       8 阅读
  10. 嵌入式Linux:格式化I/O

    2024-06-14 20:54:02       11 阅读