Spring IoC

前言:

我们介绍下Spring.

通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.
这么说可能还是很抽象.用一句话概括就是Spring就是一个包含了众多工具和方法的IoC容器.
所谓的容器,现实生活中很多,类似冰箱就是一个容器,里面放了很多食物
在我们之前掌握知识来看:LIst 也是一个容器,里面可以存放很多不同类型的数据.

1.什么是IoC

IoC Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器.

也就是"控制权"反转.就像是我们在JavaSE中,每一个对象都需要自己来new,但是把创建对象的任务交给了容器,我们只是需要的时候将所需对象注入进目标对象中即可.这个容器就称为"IoC容器",我们将所需对象通过注解加到目标对象中就称为"依赖注入".

"控制反转"是一种思想,而"依赖注入"是这种思想的实现形式.但是不是只有这一种实现方式,Spring用的是依赖注入

1.2IoC介绍

传统的程序开发

就类似造车:

先设计轮⼦(Tire),然后根据轮⼦的⼤⼩设计底盘(Bottom),接着根据底盘设计⻋⾝(Framework),最 后根据⻋⾝设计好整个汽⻋(Car)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮⼦

代码实现:

//轮胎
public class Tire {private int size;private String color;public Tire(int size) {System.out.println("tire size:"+size);}
}//底盘
public class Bottom {private Tire tire;public Bottom(int size) {tire = new Tire(size);System.out.println("tire init...");}
}//框架
public class Framework {private Bottom bottom;public Framework(int size) {bottom =  new Bottom(size);System.out.println("bottom init....");}
}//汽车
public class Car {private Framework framework;public Car(int size) {framework = new Framework(size);System.out.println("framework init...");}public void run() {System.out.println("car run...");}
}//启动类
public class Main {public static void main(String[] args){Car car = new Car(10);car.run();}
}

我们通过上述代码就模拟好了造车.

但是如果我们要在上述代码修改一个变量,那么所有的类就会跟着改变

牵一发而动全身

代码的耦合性太高了,根本不利于我们维护代码,可读性也大大降低

解决方案:

我们尝试换⼀种思路, 我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计 底盘,最后根据底盘来设计轮⼦. 这时候,依赖关系就倒置过来了:轮⼦依赖底盘, 底盘依赖⻋⾝, ⻋⾝依赖汽⻋
这就类似我们打造⼀辆完整的汽⻋, 如果所有的配件都是⾃⼰造,那么当客⼾需求发⽣改变的时候, ⽐如轮胎的尺⼨不再是原来的尺⼨了,那我们要⾃⼰动⼿来改了,但如果我们是把轮胎外包出去,那 么即使是轮胎的尺⼨发⽣变变了,我们只需要向代理⼯⼚下订单就⾏了,我们⾃⾝是不需要出⼒的
如何实现:
改用传递对象的方式,即使下级类修改,也没关系

1.2.1IoC实现造车

//bottom
public class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("tire init...");}
}//Car
public class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;System.out.println("framework init...");}public void run() {System.out.println("car run...");}
}//Framework
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("bottom init....");}
}//Tire
public class Tire {private int size;private String color;public Tire(int size, String color) {System.out.println("tire size:"+size+",color:"+color);}
}//Main
public class Main {public static void main(String[] args) {Tire tire = new Tire(17, "red");Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}
}
从上面我们可以看到

传统方式是创建Car再去找需求的依赖,是一个从下到上的结构

而IOC的方式是将对象依赖注入,是改进之后的控制权反转

这里我们可以看出IOC思维的优势

1.减少了类之间的耦合度

2.便于对象的集中管里

2什么是DI

 DI: Dependency Injection(依赖注⼊)

容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。
上面IoC方式造车的例子就是"依赖注入"
某个对象想使用某个资源,我们直接注入给他

3.IoC详解

前⾯我们提到IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。
也就是bean的存储
bean:Spring中的对象称为bean

3.1Bean的存储

存储Bean我们需要使用两类注解

类注解:@Controller,@Service,@Component,@Repository,@Configuration

方法注解:@Bean

在学习这些注解之前我们要先学会使用传统方式,来获取Bean.这里我们使用

ApplicationContext来获取
ApplicationContext 翻译过来就是: Spring 上下⽂
因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下文.
关于上下⽂的概念
在计算机领域, 上下⽂这个概念, 咱们最早是在学习线程时了解到过, ⽐如我们应⽤进⾏线程切换的时 候,切换前都会把线程的状态信息暂时储存起来,这⾥的上下⽂就包括了当前线程的信息,等下次该 线程⼜得到CPU时间的时候, 从上下⽂中拿到线程上次运⾏的信息
这个上下⽂, 就是指当前的运⾏环境, 也可以看作是⼀个容器, 容器⾥存了很多内容, 这些内容是当前运⾏的环境
@SpringBootApplication
public class SpringIoc3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringIoc3Application.class, args);}}

获取对象bean的方式:

ApplicationContext 也提供了其他获取bean的⽅式, ApplicationContext 获取bean对象的功能, 是⽗ 类BeanFactory提供的功能.
public interface BeanFactory {//以上省略...// 1. 根据bean名称获取beanObject getBean(String var1) throws BeansException;// 2. 根据bean名称和类型获取bean<T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的beanObject getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取bean<T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;//以下省略...
}

Bean的命名约定:

在Spring官方文档中指出,如果是类注解以名字来访问,就要注意名字必须和对象名保持一致,并且在getBean()方法中名字的第一个首字母要小写,特殊情况下,对象名前两个字母都是大写的情况下直接使用原名,不需要小写.

使用方法注解的时候.直接使用方法名即可.

⽐如
类名: UserController, Bean的名称为: userController
类名: AccountManager, Bean的名称为: accountManager
类名: AccountService, Bean的名称为: accountService
前两个字母都是大写
⽐如
类名: UController, Bean的名称为: UController
类名: AManager, Bean的名称为: AManager

3.1.1@Controller(控制器存储)

接下来我们使用上述传统方式来获取Bean并观察结果

首先我们要将UserController对象用@Controller注解存放到IoC容器中

这样我们就把这个类放到了Spring中.

接下来我们使用getBean()方法来获取UserController对象

@SpringBootApplication
public class SpringIoc3Application {public static void main(String[] args) {ApplicationContext context=SpringApplication.run(SpringIoc3Application.class, args);UserController bean = context.getBean(UserController.class);//通过类型扫描UserControllerbean.say();UserController userController = (UserController) context.getBean("userController");userController.say();UserController bean1 = context.getBean("userController", UserController.class);bean1.say();}}

最后我们看到结果成功输出也就是获取到了UserController对象.

地址一样,说明是一个对象

我们获取对象的功能,是Application的父类BeanFactory的功能.

3.1.2@Service(服务存储)

 

3.1.3 @Repository(仓库存储)

3.1.4 @Component(组件存储)

 3.1.5@Configuration(配置存储)

3.2为什么使用这么多类注解

这个也是和咱们前⾯讲的应⽤分层是呼应的. 让程序员看到类注解之后,就能直接了解当前类的⽤途.
@Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.
@Servie:业务逻辑层, 处理具体的业务逻辑.
@Repository:数据访问层,也称为持久层. 负责数据访问操作
@Configuration:配置层. 处理项⽬中的⼀些配置信息

3.3 ⽅法注解 @Bean

类注解是添加到某个类上的, 但是存在两个问题:
1. 使⽤外部包⾥的类, 没办法添加类注解
2. ⼀个类, 需要多个对象, ⽐如多个数据源
这种场景, 我们就需要使⽤⽅法注解 @Bean
代码案例1:这里我们将类注解先注释掉.
//@Configuration
public class UserConfig {public void say(){System.out.println("hi,UserConfig");}@Beanpublic User user(){return new User("张三");}
}

这里出现了报错

错误内容是没有名为“userConfig”的bean可用.

原因是我们没有加上类注解,切记@Bean要搭配类注解使用

加上类注解

@Configuration
public class UserConfig {public void say(){System.out.println("hi,UserConfig");}@Beanpublic User user(){return new User("张三");}
}

代码案例2:定义多个对象,使用类的类型扫描

这里我们定义两个User对象并且都通过@Bean注解添加到容器中

@Beanpublic User user(){return new User("张三");}@Beanpublic User user1(){return new User("李四");}

通过类的类型扫描

这里出现了报错,通过类的类型扫描.此时容器中有两个User对象,我们根据类型获取对象,此时Spring不知道你要获取哪个对象,所以报错了.

解决办法:用类的名字扫描

3.4扫描路径

我们把启动类放到其他的目录下面

再次启动程序

 为什么会出错呢?

为什么没有找到bean对象呢?
使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解
也就是通过 @ComponentScan 来配置扫描路径.
@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//从Spring上下⽂中获取对象User u1 = (User) context.getBean("u1");//使⽤对象System.out.println(u1);}
}
那为什么前⾯没有配置 @ComponentScan注解也可以呢?
@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
@SpringBootApplication 中了.默认扫描的范围是SpringBoot启动类所在包及其⼦包
在配置类上添加 @ComponentScan 注解, 该注解默认会扫描该类所在的包下所有的配置类
ApplicationContext VS BeanFactory(常⻅⾯试题)
继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和
ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽
ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外, 它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽
BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)

4.DI(依赖注入)详解

 依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象.

在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作.
简单来说, 就是把对象取出来放到某个类的属性中.
在⼀些⽂章中, 依赖注⼊也被称之为 "对象注⼊", "属性装配", 具体含义需要结合⽂章的上下⽂来理解.
关于依赖注⼊, Spring也给我们提供了三种⽅式:
1. 属性注⼊(Field Injection)
2. 构造⽅法注⼊(Constructor Injection)
3. Setter 注⼊(Setter Injection

4.1属性注入

4.2构造方法注入

我们可以看到,只有一个构造方法的时候即使不加@Autowired也可以获取数据

但是,我们要是加一个空的构造方法看看效果

我们看下报错信息,也是我们的老朋友了,就是空指针异常.为什么会空指针异常呢?

因为程序启动的时候会首先调用无参数的构造方法,如果没有会调用我们写的,但是两个都有的话就会调用无参数的,此时UserService并没有真正new对象,去调用UserService的say()方法就会出现空指针异常

解决办法:就是在想要注入的构造方法中添加@Autowired注解

4.3Setter注入

@Controller
public class UserController3 {//注⼊⽅法3: Setter⽅法注⼊private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController3...");userService.sayHi();}
}

4.4三种注入优缺点分析

1.属性注⼊
优点: 简洁,使⽤⽅便;
缺点:
只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指
针异常)
不能注⼊⼀个Final修饰的属性
2.构造函数注⼊(Spring 4.X推荐)
优点:
可以注⼊final修饰的属性
注⼊的对象不会被修改
依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法
是在类加载阶段就会执⾏的⽅法.
通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
缺点:
注⼊多个对象时, 代码会⽐较繁琐
3.Setter注⼊(Spring 3.X推荐)
优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
缺点:
不能注⼊⼀个Final修饰的属性
注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险

4.5@Autowired存在的问题

当我们new两个User对象的时候,我们使用@Autowired注入User对象会出现报错.

如果注入对象的名和创建的对象名字不一样就会报错.

解决办法:

1.使用@Primary

在有两个对象的时候可以在其中一个对象上加上@Primary.作用是默认选择,如果有两个对象并且对象名不同会使用加上@Primary的默认对象

2.使用@Qualifie

在注解后边括号指定当前要注⼊的bean对象。 在@Qualifier的value属性中,指定注⼊的bean的名称

3.@Resource

使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称

@Autowird 与 @Resource的区别(面试题)
@Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
@Autowired 默认是按照类型注⼊,⽽@Resource是按照名称注⼊. 相⽐于 @Autowired 来说, @Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何配置虚拟机的IP上网

一.配置vm虚拟机网段 在虚拟机主页点击编辑->虚拟网络编辑器&#xff0c;选择VMnet8&#xff0c;要改动两个地方&#xff1a; 1.子网IP改成192.168.10.0 2.NAT设置->192.168.10.2 让所有的vm配置的虚拟机使用NAT时&#xff0c;它们的网段都是一致的。注意:这里的第三个部…

Java GC 基础知识快速回顾

目录 一、Java 垃圾回收&#xff08;GC&#xff09;基本概念和重要性分析 &#xff08;一&#xff09; Java 垃圾回收&#xff08;GC&#xff09;基本概念回顾 1.GC 三种常见语义 2.Mutator&#xff1a;应用程序的内存管理角色 3.TLAB&#xff08;线程本地分配缓存&#x…

【Linux】Linux权限

1 Linux权限概念 Linux下有两种用户&#xff1a;超级用户(root)&#xff0c;普通用户 超级用户&#xff1a;可以再linux系统下做任何事情&#xff0c;不受限制。 普通用户&#xff1a;在linux下做有限的事情。 超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是…

Linux笔记---缓冲区

1. 什么是缓冲区 在计算机系统中&#xff0c;缓冲区&#xff08;Buffer&#xff09; 是一种临时存储数据的区域&#xff0c;主要用于协调不同速度或不同时序的组件之间的数据传输&#xff0c;以提高效率并减少资源冲突。它是系统设计中的重要概念&#xff0c;尤其在I/O操作、网…

Tomcat基础知识及其配置

1.Tomcat简介 Tomcat是Apache软件基金会&#xff08;Apache Software Foundation&#xff09;的Jakarta 项目中的一个核心项目&#xff0c;由Apache、Sun和其他一些公司及个人共同开发而成。 Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服…

机器学习数学通关指南

✨ 写在前面 &#x1f4a1; 在代码的世界里沉浸了十余载&#xff0c;我一直自诩逻辑思维敏捷&#xff0c;编程能力不俗。然而&#xff0c;当我初次接触 DeepSeek-R1 并领略其清晰、系统的思考过程时&#xff0c;我不禁为之震撼。那一刻&#xff0c;我深刻意识到&#xff1a;在A…

数据结构之八大排序算法详解

目录 一、冒泡排序&#xff08;Bubble Sort&#xff09;原理代码实现时间复杂度 二、选择排序&#xff08;Selection Sort&#xff09;原理代码实现时间复杂度 三、插入排序&#xff08;Insertion Sort&#xff09;原理代码实现时间复杂度 四、希尔排序&#xff08;Shell Sort&…

RocketMQ的运行架构

目录 1. 核心组件(1) NameServer(2) Broker(3) Producer(4) Consumer 2. 消息流转流程3. 高可用机制4. 扩展性与负载均衡5.容错机制5. 特殊功能支持6. 典型部署架构总结 RocketMQ 是一款高性能、高可靠的分布式消息中间件&#xff0c;其运行架构设计为分布式、可扩展、高可用的…

【AIGC系列】5:视频生成模型数据处理和预训练流程介绍(Sora、MovieGen、HunyuanVideo)

AIGC系列博文&#xff1a; 【AIGC系列】1&#xff1a;自编码器&#xff08;AutoEncoder, AE&#xff09; 【AIGC系列】2&#xff1a;DALLE 2模型介绍&#xff08;内含扩散模型介绍&#xff09; 【AIGC系列】3&#xff1a;Stable Diffusion模型原理介绍 【AIGC系列】4&#xff1…

算法日记32:15届蓝桥C++B填空(握手问题+小球反弹)

握手问题 一、题解 1、通过观察我们可以发现&#xff0c;题目属于数论中的蜂巢问题&#xff0c;但是我们这里不使用结论&#xff0c;而是通过分析推出 2、假设我们不考虑特殊情况(也就是那 7 7 7个人的情况)&#xff0c;那么问题的答案应该为 int res0; for(int i49;i>1;i…

【三维分割】LangSplat: 3D Language Gaussian Splatting(CVPR 2024 highlight)

论文&#xff1a;https://arxiv.org/pdf/2312.16084 代码&#xff1a;https://github.com/minghanqin/LangSplat 文章目录 一、3D language field二、回顾 Language Fields的挑战三、使用SAM学习层次结构语义四、Language Fields 的 3DGS五、开放词汇查询&#xff08;Open-voca…

Windows安装sql server2017

看了下官网的文档&#xff0c;似乎只有ubuntu18.04可以安装&#xff0c;其他debian系的都不行&#xff0c;还有通过docker的方式安装的。 双击进入下载的ISO&#xff0c;点击执行可执行文件&#xff0c;并选择“是” 不要勾选 警告而已&#xff0c;不必理会 至少勾选这两…

LabVIEW图像识别抗干扰分析

问题描述 在基于LabVIEW的探针定位系统中&#xff0c;存在两个核心技术难点&#xff1a; 相机畸变导致初始定位误差&#xff1a;非线性畸变使探针无法通过坐标变换直接精确定位&#xff0c;需采用粗定位图像修正的两段式控制策略。 图像识别可靠性不足&#xff1a;复杂背景&a…

Leetcode1 两数之和 python两种方法实现

Leetcode1 两数之和 python两种方法实现 文章目录 Leetcode1 两数之和 python两种方法实现方法一&#xff1a;枚举法&#xff08;暴力解法&#xff09;方法二&#xff1a;用空间换时间。 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为…

总结前端常用数据结构 之 队列篇【JavaScript 】

推动你的事业&#xff0c;不要让你的事业推动你。——爱因斯坦 目录 队列是什么&#xff1f;JS异步、事件循环、任务队列&#xff1a;队列的实现方法&#xff1a;‌数组实现‌ - 封装队列&#xff1a;对象实现&#xff08;优化性能&#xff09;- 封装队列&#xff1a; 队列应用…

C# 数据转换

1. 文本框读取byte&#xff0c;ushort格式数据 byte addr; if (byte.TryParse(textBoxAddr.Text, out addr) true) {}2. 字节数组 (byte[]) 转换为 ASCII 字符串 byte[] bytes { 72, 101, 108, 108, 111 }; // "Hello" 的 ASCII 码 string s0 Encoding.ASCII.Ge…

golang部分语法介绍(range关键字,函数定义+特性,结构体初始化+结构体指针/方法)

目录 golang语法 range关键字 介绍 使用 原理 函数 介绍 定义 特性 结构体 介绍 初始化 结构体指针 结构体方法 方法接收者 golang语法 range关键字 介绍 用于遍历数组&#xff08;array&#xff09;、切片&#xff08;slice&#xff09;、映射&#xff08;ma…

Linux与UDP应用1:翻译软件

UDP应用1&#xff1a;翻译软件 本篇介绍 本篇基于UDP编程接口基本使用中封装的服务器和客户端进行改写&#xff0c;基本功能如下&#xff1a; 从配置文件dict.txt读取到所有的单词和意思客户端向服务端发送英文服务端向客户端发送英文对应的中文意思 配置文件内容 下面的内…

【机器学习】逻辑回归(Logistic Regression)

逻辑回归 逻辑回归逻辑回归的流程Sigmoid函数Sigmoid函数的公式及图像 逻辑回归的损失函数与最优化求解逻辑回归使用梯度下降法求解 逻辑回归 逻辑回归与线性回归都是线性模型&#xff0c;其中线性回归使用线性式来预测数值&#xff0c;逻辑回归使用线性式来进行分类任务。 逻…

IDEA - 查看类的继承结构(通过快捷键查看、通过生成类图查看)

一、通过快捷键查看 在项目中定位到目标类&#xff08;例如&#xff0c;Executor.java&#xff09; 按下快捷键 【Ctrl H】 此时会弹出 Type Hierarchy 窗口&#xff0c;展示所有相关的父类、子类、接口 二、通过生成类图查看 在项目中定位到目标类&#xff08;例如&#x…