[Spring] IoC 控制反转和DI依赖注入和Spring中的实现以及常见面试题

目录

1.  什么是Spring

2.什么是IoC容器

3.通过实例来深入了解IoC容器的作用

3.1造一量可以定义车辆轮胎尺寸的车出现的问题

3.2解决方法

3.3IoC优势

4.DI介绍

5.Spring中的IoC和DI的实现

5.1.存对象

5.1.2 类注解

5.1.3 方法注解

5.2取对像 (依赖注入)

5.2.1.属性注入

5.2.2.构造方法注入(Spring4.x推荐的)

5.2.3Setter注入(Sping 3.x推荐)

5.3三种注入方式的优缺点:

5.4@Autowired存在的问题

5.5常见面试题:


1.  什么是Spring

    Spring是一个开源框架,他让我们的开发更加简单,它支持广泛的应用场景,有着活跃而庞大的社区,这也是Spring能够长久不衰的原因.这个概念还是相对于比较抽象,我们用通俗易懂的话来讲,Spring是包含了众多工具方法的IoC容器 那么问题来了,容器是什么.什么是IoC容器

2.什么是IoC容器

容器是用来容纳某种物品的装置,IoC是Spring的核心思想,我们在类上面加入@RestControlle和@Controller注解,就是把这个对象交给Spring来管理,Spring框架在启动的时候就会加载该类,把对象交给Spring来管理就是IoC思想

IoC:  inversion of Con\rol(控制反转) 也就是说Spring是一个"控制反转容器"

那么什么是控制反转呢?也就是控制劝的反转.即获取对象的过程被反转了.

也就是说,当需要某个对象的时候,传统开发模式需要我们在类里面自己new对象,现在不需要我们自己去创建,而是把创建对象的任务交给容器,程序只需要依赖注入就可以了,这个容器被称为IoC容器,Spring是一个IoC容器,所以Spring有时候也被称为Spring容器.

控制反转是一种思想,在生活中也随处可见,比如,自动驾驶,在传统驾驶模式中,我们对于车的控制权是司机,但是在自动驾驶的时候,我们对于车辆的控制权就交给了自动驾驶系统.这也是一种控制反转

3.通过实例来深入了解IoC容器的作用

3.1造一量可以定义车辆轮胎尺寸的车出现的问题

我们想造一辆车,但是造车的时候,需要车身,造车身又需要底盘,底盘需要轮胎,我们当我造一辆车的时候,可以这样造:

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(){
        System.out.println("Bottom init ...");
    }
}
//轮胎
public class Tire {
    int size; //轮胎的尺寸
    public Tire(){
        System.out.println("车胎的尺寸是");
    }
}

我们没写轮胎的尺寸的时候,相安无事,看起来很好,但是如果我们在轮胎的构造方法中加入它的尺寸.

就会出问题了,我们要想在造车的时候就传入轮胎的大小,就得把这一系列的代码构造方法全部改了,每一个都得加入int类型的size参数,这样无异于是很麻烦的.

那么有没有一种简单一些的办法可以让我们优化这些步骤呢?

3.2解决方法

我们尝试换一种方法,我们先设计汽车的大概样子,然后根据汽车来设计车身,在根据车身来设计地盘,在根据底盘来设计轮胎.

package com.example.demo.Car;

public class Car {
    private  Framework framework; //我们在创建车的时候需要一个车身 这时候还需要一个车身类
    public Car(Framework framework){
        this.framework = framework;
        System.out.println("Car init....");
    }
    public void run(){
        System.out.println("Car run ....");
    }

    public static void main(String[] args) {
        Tire tire = new Tire(14);
        Bottom bottom = new Bottom(tire);
        Framework framework1 = new Framework(bottom);
        Car car = new Car(framework1);
        car.run(); 
// 我们先造一个轮胎,然后轮胎指定了尺寸,
//把轮胎给底盘,底盘给车身,车身给车 这样我们如果想改车胎的参数,
//只需要改车胎的构造方法就行了,不需要把其它的都一起改
    }
}
// 车身
public class Framework {
    private Bottom bottom; //底盘
    public Framework(Bottom bottom){
        this.bottom = bottom;
        System.out.println("Framework init...");
    }
}
//底盘
public class Bottom {
    private Tire tire; //轮胎
    public Bottom(Tire tire){
        this.tire = tire;
        System.out.println("Bottom init ...");
    }
}
//轮胎
public class Tire {
    int size; //轮胎的尺寸
    public Tire(int size){
        System.out.println("车胎的尺寸是"+size);
    }
}

3.3IoC优势

传统的代码对象创建的顺序是 car-framework - bottom - tire

改进之后解耦代码的对象创建顺序是 tire - bottom - framework - car

  我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了
   Framework,Framework创建并创建了Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再
是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由
当前类控制了.
 这样的话,即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是IoC的实现思想。
这样IoC容器有以下好处

1.资源不由资源双方管理,而是不使用资源的第三方来进行管理,资源集中管理,实现了资源的可配置和易管理

2 降低了使用资源双方的依赖程度,也就是我们常说的解耦合.
 

4.DI介绍

上面学习了IoC,那么什么是DI呢? DI:Dependency Injection(依赖注⼊)

容器在运行阶段,动态的为应用程序提供运行的时候所依赖的资源,称为依赖注入.

程序运行的时候,需要哪个资源,此时容器就提供这个资源.  依赖注入和控制反转是从不同的角度来描述同一件事,通过引入IoC容器,利用依赖关系注入的方式,实现对象间的解耦合.

IoC是一种思想,也就是一种指导原则,而DI就是具体的实现,也就是说DI是IoC是一种实现

5.Spring中的IoC和DI的实现

既然是容器,那么肯定有存和取,Spring针对这两种操作都有自己的实现,下面我们来介绍一下在spring中存放对象和取对象这两种操作分别应该怎么用

5.1.存对象

共有两种注解类型可以实现:

5.1.2 类注解

@Controller (控制器存储) 

@Controller
public class TestController {
    public void func(){
        System.out.println("TestController...");
    }

}

该类注解存放的是控制器对象,可以和前端访问或者交互到

那么Spring把这个对象存到哪里了呢? 我们可以在Spring给的main函数中验证出来

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//Spring上下文 存放的是Spring容器管理的对象
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
		//通过类名和类型来找到这个对象
		TestController controller = context.getBean("testController", TestController.class);
		//执行对象中的方法
		controller.func();
	}

}

@Serivce (服务存储) 存储的是业务逻辑相关的对象

@Repository 仓库存储 存储数据相关的 也称为吃就吃

@Component (组件存储) 其它几个类存储注解都继承自这个

@Configuration 配置层 处理项目中的一些配置信息

其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".
@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,
@Repository 等.这些注解被称为 @Component 的衍⽣注解.

5.1.3 方法注解

类注解固然可以用,但是存在两个问题:

1.使用外部包里的类,没办法添加类注解

2.一个类需要多个对象

我们可以使用我们的@Bean 方法注解 在Spring中 方法注解要配合我们的类注解才能存储到Spring容器中,如下:

@Component
public class BeanConfig {

    @Bean
    public User user1() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

    @Bean
    public User user2() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

上述代码中,我们定义了多个对象.那么这些对象我们又该怎么去取呢?

我们尝试获取一下

public static void main(String[] args) {
		//Spring上下文 存放的是Spring容器管理的对象
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
		//通过类名和类型来找到这个对象
		User user = context.getBean(User.class);
		//执行对象中的方法
		System.out.println(user);
	}

运行以后发现代码报错了.

我们可以根据名字来获取到对象

public static void main(String[] args) {
		//Spring上下文 存放的是Spring容器管理的对象
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
		//通过类名和类型来找到这个对象
		User user = context.getBean("user1",User.class);
		//执行对象中的方法
		System.out.println(user);
	}


我们还可以通过@Bean注解重命名对象 

5.2取对像 (依赖注入)

依赖注入是一个过程,是在IoC容器创建Bean时,去提供运行的时候所需要以来的资源,而这个资源就是对象.我们可以提供@Autowried这个注解来完成依赖注入这个操作

关于依赖注入,Spring也给我们提供了三种方法,

1. 属性注⼊(Field Injection)
2. 构造⽅法注⼊(Constructor Injection) 
3. Setter注⼊(Setter Injection) 

5.2.1.属性注入

我们可以举个例子来说明一下:

@Controller
public class TestController {

    @Autowired
    private UserService userService;

     public void func(){
        userService.func();
    }
}
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//Spring上下文 存放的是Spring容器管理的对象
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
		//通过类名和类型来找到这个对象
		TestController testController = context.getBean(TestController.class);
		//执行对象中的方法
		testController.func();
	}

}

可以看到我们通过了依赖注入,拿到了对象,并且把它的方法使用了出来.

5.2.2.构造方法注入(Spring4.x推荐的)

这种是在构造方法中实现注入的

如果只要一个构造方法 那么@Autowried可以省略,如果有多个就需要添加@Autowried来明确指定到底要使用哪个构造方法.

5.2.3Setter注入(Sping 3.x推荐)

Setter注⼊和属性的Setter法实现类似,只不过在设置set⽅法的时候需要加上@Autowired注
解,如下代码所⽰

  private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

5.3三种注入方式的优缺点:

1.属性注入:

优点:简洁,使用方便,

缺点:只能用于IoC容器,并且会出现NPE

不能注入一个fina修饰的属性

2.构造函数注入;
优点: 可以注入fina修饰的属性,

注入的对象不会被修改

通用性好,构造方法是JDK支持的,换了任何框架都支持

依赖对象在使用时就会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就执行的方法

缺点:注入多个对象的时候,代码繁琐

3.Setter注入

优点:方便在类实例之后,重新对改对象进行配置或者注入

缺点:不能注入final修饰的属性.注入对象夸你汇编,因为setter方法可能会被多次调用,有被修改的风险

5.4@Autowired存在的问题

同一个类型存在多个bean时 @Autowired会存在问题

@Autowired
    private User user;


    public void func(){
        System.out.println(user);
    }

会报这个错误,

那么我们该如何解决这个问题呢?

Spring给我们 提供了三种解决方案:

1.@Primary注解

使用该注解,指定默认的对象

@Component
public class BeanConfig{

    @Bean
    @Primary
    public User user1() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

    @Bean
    public User user2() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

重新运行一下:

2.@Qualifier注解

指定当前要注入的bean对象

 @Autowired
    @Qualifier("user2")
    private User user;
    public void func(){
        System.out.println(user);
    }

3.Resource注解,按照bean名称进行注入,通过name属性指定要注入bean的名称

 @Resource(name = "user2")
    private User user;
    public void func(){
        System.out.println(user);
    }

5.5常见面试题:

@Autowird与@Resource的区别

1.@Autowird是spring框架提供的注解,而@Resource是JDK提供的

2.@Autowird是按照类型注入,@Resource是按照bean名称注入.相比于@Autowired来说,@Resource支持更多的参数设置,入name的设置,根据名称获取bean

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/445157.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《深度学习风暴:掀起智能革命的浪潮》

在当今信息时代,深度学习已经成为科技领域的一股强大力量,其应用领域涵盖了从医疗到金融再到智能交互等方方面面。随着技术的不断进步和应用的不断拓展,深度学习的发展势头愈发迅猛,掀起了一股智能革命的浪潮。本文将从基本原理、应用实例、挑战与未来发展方向、与机器学习…

大模型产业落地,安全运营能否迎来“自动驾驶”时刻?

科技云报道原创。 通过一段文字描述,就能生成60秒堪比大片的视频,来自大模型Sora的出色表现,让全球都为之震撼。 无论是ChatGPT还是Sora,都只是大模型走出实验室的第一步,大模型如何在产业中落地,为具体的…

数字化运营在教育行业的技术架构实践总结

随着科技的不断进步和数字化时代的到来,教育行业也正面临着数字化转型的挑战和机遇。教育行业的数字化运营需要依靠合理的技术架构来支撑,本文将探讨教育行业数字化运营的技术架构设计。 ## 第一步:需求分析和架构设计 在构建教育行业数字化…

初识Python(helloworld、海洋距离单位换算、打印名片、文本进度条、判断水仙花数)

一、Python3的安装,IDLE的使用:使用print函数输出”hello world”; 二、 PyCharm的安装与使用:创建”hello_world.py”文件并使用print函数输出”hello world” 三、海洋单位距离换算 要求:运行代码,控制台…

七、门控循环单元语言模型(GRU)

门控循环单元(Gated Recurrent Unit,GRU)是 LSTM 的一个稍微简化的变体,通常能够提供同等的效果,并且计算训练的速度更快。 门控循环单元原理图:参考门控循环单元 原理图中各个图形含义: X(t)&a…

PyTorch搭建LeNet训练集详细实现

一、下载训练集 导包 import torch import torchvision import torch.nn as nn from model import LeNet import torch.optim as optim import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as npToTensor()函数: 把图像…

【亲测有效】解决三月八号ChatGPT 发消息无响应!

背景 今天忽然发现 ChatGPT 无法发送消息,能查看历史对话,但是无法发送消息。 可能的原因 出现这个问题的各位,应该都是点击登录后顶部弹窗邀请 [加入多语言 alapha 测试] 了,并且语言选择了中文,抓包看到 ab.chatg…

Flutter 开发环境搭建-VS Code篇

1.准备环境 Java SDK 下载及安装Flutter SDK 安装及配置环境变量 下载地址将flutter sdk解压目录下的bin目录放到系统环境变量中 检查环境,在系统终端中输入: # 打印flutter sdk版本号 flutter --version# 检查flutter运行环境 flutter doctor第一次运…

qt 格式化打印 日志 QMessagePattern 格式词法语法及设置

一、qt源码格式化日志 关键内部类 QMessagePattern qt为 格式化打印日志 提供了一个简易的 pattern(模式/格式) 词法解析的简易的内部类QMessagePattern,作用是获取和解析自定义的日志格式信息。 该类在qt的专门精心日志操作的源码文件Src\qtbase\src\corelib\global\qloggi…

专题1 - 双指针 - leetcode 11. 盛最多水的容器

leetcode 11. 盛最多水的容器 1. leetcode 11. 盛最多水的容器1. 题目详情1. 原题链接2. 基础框架 2. 解题思路1. 题目分析2. 算法原理3. 时间复杂度 3. 代码实现4. 知识与收获 1. leetcode 11. 盛最多水的容器 1. 题目详情 给定一个长度为 n 的整数数组 height 。有 n 条垂线…

软考攻略/软考详解/软考等级/软考科目

目录 前言 一、软考是什么 二、证书样式 三、软考介绍 3.1 什么是软考? 3.2 通过了软考,就算有职称了么? 3.3 哪些人可以参加软考? 3.4 软考设置了哪些资格? 3.5 哪些资格含金量比较高呢?报考建议? 四、中级资格推荐以下几个: 计算机软件类 --软件…

【AI视野·今日NLP 自然语言处理论文速览 第八十二期】Tue, 5 Mar 2024

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 5 Mar 2024 (showing first 100 of 175 entries) Totally 100 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Key-Point-Driven Data Synthesis with its Enhancement on Mathematica…

农场管理小程序|基于微信小程序的农场管理系统设计与实现(源码+数据库+文档)

农场管理小程序目录 目录 基于微信小程序的农场管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、用户信息管理 2、农场信息管理 3、公告信息管理 4、论坛信息管理 四、数据库设计 五、核心代码 七、最新计算机毕设选题推荐 八、源码获取&#x…

Day22:安全开发-PHP应用留言板功能超全局变量数据库操作第三方插件引用

目录 开发环境 数据导入-mysql架构&库表列 数据库操作-mysqli函数&增删改查 数据接收输出-html混编&超全局变量 第三方插件引用-js传参&函数对象调用 完整源码 思维导图 PHP知识点: 功能:新闻列表,会员中心&#xff0…

Stable Diffusion 模型下载:ZavyChromaXL(现实、魔幻)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 作者述:该模型系列应该是用于 SDXL 的 ZavyMix SD1.5 模型的延续。主要重点是获…

【工具】Git的24种常用命令

相关链接 传送门&#xff1a;>>>【工具】Git的介绍与安装<< 1.Git配置邮箱和用户 第一次使用Git软件&#xff0c;需要告诉Git软件你的名称和邮箱&#xff0c;否则无法将文件纳入到版本库中进行版本管理。 原因&#xff1a;多人协作时&#xff0c;不同的用户可…

M1电脑 Xcode15升级遇到的问题

遇到四个问题 一、模拟器下载经常报错。 二、Xcode15报错: SDK does not contain libarclite 三、报错coreAudioTypes not found 四、xcode模拟器运行一次下次必定死机 一、模拟器下载经常报错。 可以https://developer.apple.com/download/all/?qios 下载最新的模拟器&…

Skywalking

1、简介 Skywalking是由国内开源爱好者吴晟开源并提交到Apache孵化器的开源项目&#xff0c; 2017年12月SkyWalking成为Apache国内首个个人孵化项目&#xff0c; 2019年4月17日SkyWalking从Apache基金会的孵化器毕业成为顶级项目&#xff0c; 目前SkyWalking支持Java、 .Net、 …

lvs集群中NAT模式

群集的含义 由多台主机构成&#xff0c;但对外表现为一个整体&#xff0c;只提供一个访问入口&#xff0c;相当于一台大型的计算机。 横向发展:放更多的服务器&#xff0c;有调度分配的问题。 垂直发展&#xff1a;升级单机的硬件设备&#xff0c;提高单个服务器自身功能。 …

复盘-PPT

调整PPT编号起始页码在设计→幻灯片大小 设置所有以及文本项目符号 ## 打开母版&#xff0c;找到对应级别设置重置 当自动生成的smartart图形不符合预期时