Spring IOC 控制反转(注解版)

Spring IOC 控制反转

文章目录

  • Spring IOC 控制反转
    • 一、前言
      • 什么是控制反转(IOC)
      • 什么是依赖注入(DI)
    • 二、介绍 IOC
      • 2.1 传统思想代码
      • 2.2 解决方案
      • 2.3 IOC思想代码
      • 2.4 IOC 使用(@Autowired依赖注入)
      • 2.5 IOC 优势
    • 三、IOC详解
      • 3.1 从Spring容器中获取对象:Spring上下文
      • 3.2 Bean的存储
        • 3.2.1 为什么要这么多类注解?
      • 3.3 方法注解@Bean
        • 3.3.1 @Bean方法注解一定要配合类注解使用
        • 3.3.2 @Bean方法定义多个对象
        • 3.3.3 重命名@Bean
      • 3.4 扫描路径
    • 四、DI依赖注入
      • 4.1 属性注入
      • 4.2 构造方法注入
      • 4.3 Setter注入
    • 五、@Autowired存在问题
      • 5.1 常见面试题:@Autowired与@Resource的区别
    • 六、附加总结

一、前言

Spring 框架中的核心概念之一就是控制反转(Inversion of Control,IoC)

IOC就是一种思想,而依赖注入(Dependency Injection, DI) 是控制反转的一种实现方式。

Spring本身是一个容器,存的是对象。对象这个词,在 Spring的范围内,称之为 Bean。

什么是控制反转(IOC)

控制反转(Inversion of Control,IoC)是一种设计原则,它将对象的创建和依赖关系的管理从程序代码中解耦出来,交由框架或容器进行处理。传统的编程方式中,应用程序代码主动创建和管理对象,而通过IoC,框架或容器负责对象的创建和管理,应用程序代码只需要声明依赖关系。转换对象控制权,让Spring帮我们管理或创建 bean

什么是依赖注入(DI)

依赖注入(Dependency Injection,DI)是一种设计模式,它将对象所依赖的其他对象的创建和管理职责从对象自身剥离出来,通过外部容器(如Spring IoC容器)将所需的依赖对象注入到目标对象中,从而实现对象之间的解耦和提高代码的可维护性和可测试性。

二、介绍 IOC

下面将通过案例来分析什么是IOC

需求:造一辆车

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.1 传统思想代码

public class NewCarExample {public static void main(String[] args) {Car car = new Car(20);car.run();}/*** 汽车对象*/static class Car {private Framework framework;public Car(int size) {framework = new Framework(size);System.out.println("Car init....");}public void run(){System.out.println("Car run...");}}/***车身类*/static class Framework {private Bottom bottom;public Framework(int size) {bottom = new Bottom(size);System.out.println("Framework init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom(int size) {this.tire = new Tire(size);System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {// 尺寸private int size;public Tire(int size){this.size = size;System.out.println("轮胎尺寸:" + size);}}
}

从上述代码中可以看到,以上程序的问题是代码耦合性过高,导致修改底层代码后,需要调整整体的代码。

2.2 解决方案

利用IOC思想,控制反转。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具体操作是将原来由我们自己创建的下极类,改为传递的方式(也就是注入的方式)。

因为我们不需要在当前类中创建下极类了,所以下极类及时发生变化,当前类本身也无需修改任何代码,这样就完成了解耦合。

2.3 IOC思想代码

public class IocCarExample {public static void main(String[] args) {Tire tire = new Tire(20);Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}static 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...");}}static class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("Framework init...");}}static class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("Bottom init...");}}static class Tire {private int size;public Tire(int size) {this.size = size;System.out.println("轮胎尺寸:" + size);}}
}

2.4 IOC 使用(@Autowired依赖注入)

Spring 作为一个IOC容器帮我们管理对象,其主要功能就是

存:存的是对象bean,可以使用 @Component或者其他注解(下文中会讲到)

取:告诉 Spring ,从容器中取出这个对象,赋值给当前对象的属性。也就是依赖注入 使用注解 @Autowired

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.5 IOC 优势

传统开发中,对象创建的顺序是:Car -> Framework -> Bottom -> Tire

改进之后解耦代码的创建顺序是:Tire -> Bottom -> Framework -> Car

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

上述改进后的程序main中的代码就是IOC容器需要存储的数据。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上面也可以看出来, IoC容器具备以下优点:

资源不由使用资源的双方管理,而由不使用资源的第三方管理。

  • 资源的集中管理:IOC会帮我们管理一些资源(对象等),需要的时候,直接去IOC中取即可。
  • 在创建实例的时候不需要了解其中的细节,降低了使用资源双方的依赖程度,降低耦合度。

三、IOC详解

前面提到的IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。也就是存储bean。

3.1 从Spring容器中获取对象:Spring上下文

在学习如何存储对象之前,先来看如何从Spring容器中获取对象?

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {//String上下文ApplicationContext context = SpringApplication.run(DemoApplication.class, args);//获取到这个类的对象context.getBean(Class<T> aClass);//根据bean名称获取beancontext.getBean(String s);}
}

获取Bean的三种常用方法外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过Bean名称来获取Bean,如果没有显式的提供名称(BeanId),Spring容器将为该bean生成唯一的名称。

Bean的命名约定:查看官方文档

其大致意思是,bean名称以小写字母开头,然后使用驼峰式大小写

比如:

类名:UserController,Bean的名称为:userController;

类名:AccountManager,Bean的名称为:accountManage;

也有特殊情况:

比如 :

类名:UController,Bean的名称为:UController;

类名:AController,Bean的名称为:AController;

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {//上下文ApplicationContext context = SpringApplication.run(DemoApplication.class, args);//通过类名获取beanUserController usercontroller1 = context.getBean(UserContorller.class);//通过Bean名获取beanUserController usercontroller2 = context.getBean(userController);system.out.println(usercontroller1);system.out.println(usercontroller2);}
}

3.2 Bean的存储

在Spring中,要把某个对象交给IOC容器管理,需要在类上添加注解,下文中就会讲到Spring框架为服务web应用程序,提供了丰富的注解。

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

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

观察下面类注解的源代码,都是@component的衍生类,因此@Component的作用范围更广。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

@Controller控制存储器

@Service服务存储

@Repository仓库存储

@Component组件存储

@Configuration配置存储

3.2.1 为什么要这么多类注解?

最直接的一个原因就是,可以让程序员看到类注解之后,就能直接了解当前类的用途。

  • @Controller:控制层, 接收请求, 对请求进行处理, 并进行响应.
  • @Servie:业务逻辑层, 处理具体的业务逻辑.
  • @Repository:数据访问层,也称为持久层. 负责数据访问操作
  • @Configuration:配置层. 处理项目中的一些配置信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.3 方法注解@Bean

五大注解只能加在类上,并且只能加在自己的代码上,如果我引入了一个第三方jar包,也希望交给Spring管理,是没有办法加五大注解。比如说:数据库操作,定义多个数据源

@Bean方法一定要配合类注解使用

使用@Bean注解时,bean的名称是方法名。

3.3.1 @Bean方法注解一定要配合类注解使用

在 Spring 框架的设计中,方法注解 要配合类注解才能将对象正常的存储到 Spring 容器中,下代码所示:

@Component
public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}
3.3.2 @Bean方法定义多个对象

对于同一个类,如何定义多个对象呢?

比如说,多数据源的场景,类是同一个,但是配置不同,指向的数据源也不同。

@Component
public class BeanConfig {@Beanpublic User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2(){User user = new User();user.setName("lisi");user.setAge(19);return user;}
}

此时,如果通过类型获取对象的话,Spring就会给我们报错,因为有两个对象,Spring不知道取哪个。接下来根据名称来获取bean对象。

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(DemoApplication.class, args);//根据bean名称, 从Spring上下文中获取对象// User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");System.out.println(user1);System.out.println(user2);}
}
3.3.3 重命名@Bean
@Bean(name = {"u1","user1"})
public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;
}

3.4 扫描路径

SpringBoot 特点就是约定大于配置。其中之一的体现就是扫描路径。

默认扫描路径:启动类所在的目录及其子孙目录

如果更改启动类所在目录,而未进行路径的标注就会出现报错。

通过@ComponentScan()这个注解可以指定扫描路径。

@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(DemoApplication.class, args);//从Spring上下文中获取对象User u1 = (User) context.getBean("u1");//使用对象System.out.println(u1);}
}

四、DI依赖注入

上面我们讲解了控制反转IoC的细节,接下来呢,我们学习依赖注入DI的细节。依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。在上面程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作。

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

  • 属性注入
  • 构造方法注入
  • Setter注入

4.1 属性注入

属性注入是使用@Autowired实现的。

下面是将Service类注入到Controller类中。

//Service类
import org.springframework.stereotype.Service;
@Service
public class UserService {public void sayHi() {System.out.println("Hi,UserService");}
}
@Controller
public class UserController {//注入方法1: 属性注入@Autowiredprivate UserService userService;public void sayHi(){System.out.println("hi,UserController...");userService.sayHi();}
}

4.2 构造方法注入

构造方法注入是在类的构造方法中实现注入,代码如下:

@Controller
public class UserController2 {//注入方法2: 构造方法private UserService userService;@Autowiredpublic UserController2(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController2...");userService.sayHi();}
}

4.3 Setter注入

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

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

五、@Autowired存在问题

当一个类存在多个bean时,使用@Autowored会存在问题

@Component
public class BeanConfig {@Bean()public User user1() {User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2() {User user = new User();user.setName("lisi");user.setAge(19);return user;}
}
@Controller
public class UserController {@Autowiredprivate UserService11 userService;//此时注入的user,Spring不知道是user1还是user2@Autowiredprivate User user;public void sayHi() {System.out.println("hi,UserController...");userService.sayHi();System.out.println(user);}
}

运行程序就会报错外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

报错原因是非唯一的Bean对象。

Spring提供了以下几种解决方案:

  • Primary
  • Qualifier
  • Resource

使用@Primary注解: 当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现.

@Component
public class BeanConfig {//此时Spring默认的就是user1()@Primary@Bean()public User user1() {User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2() {User user = new User();user.setName("lisi");user.setAge(19);return user;}
}

使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。

@Controller
public class UserController @Autowiredprivate UserService11 userService;@Qualifier("user2") //指定bean名称@Autowiredprivate User user;public void sayHi() {System.out.println("hi,UserController...");userService.sayHi();System.out.println(user);}
}

使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

@Controller
public class UserController @Autowiredprivate UserService11 userService;@Resource(name = "user2")private User user;public void sayHi() {System.out.println("hi,UserController...");userService.sayHi();System.out.println(user);}
}

5.1 常见面试题:@Autowired与@Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入. 相比于@Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。

六、附加总结

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

相关文章

从零开始学GeoServer源码(一)(搭建开发环境Win10+IDEA23.3.5+jdk11+geoserver2.24.x)

搭建开发环境 参考资料 0、基础环境准备0.1、idea0.2、jdk0.3、源码 1、导入工程2、配置启动环境2.1、打开新增配置面板2.2、配置工作目录2.2.1、从常用配置中选择2.2.2、直接粘贴 2.3最终效果 3、调整源码3.1、添加maven引用3.2、注释无效代码3.3、删除测试代码 4、修改运行端…

LeetCode esay mid 记录

1486. 数组异或操作 感觉一般也用不到 emmm 灵茶山艾府传送门 推导过程可以结合官网部分观看 重点由两部分的结合 将特定部分转换为常见部分 0到n的异或和表示 2595. 奇偶位数 0x555是十六进制数&#xff0c;转换为二进制为 0101 0101 0101 class Solution {public int[…

【面试 - 页面优化举例】页面跳转卡顿问题解决 - 页面跳转速度优化

目录 为何要优化如何优化优化1 - 懒加载优化2 - el-tree 子节点默认不展开 为何要优化 页面A跳转到也页面B时&#xff0c;页面出现卡顿情况&#xff1a; 【问题】页面A → 页面B时&#xff0c;页面B进入到了 created 钩子后过了六七秒才进入到 mounted 钩子&#xff1b;【分析经…

吉林省教育学院学报杂志社吉林省教育学院学报编辑部2024年第5期目录

“研培一体”理论与实践 教师培训管理共同体的职能定位与价值追求 张岩; 1-3 数字化转型背景下教师培训工作的发展路径 李春光; 4-6 挖掘数智潜能&#xff0c;推进教师培训融合创新 鲍赫; 7-9《吉林省教育学院学报》投稿&#xff1a;cn7kantougao163.com 精准培…

Apache DolphinScheduler查看版本信息

我找了半天&#xff0c;没有看到版本在哪里。然后我看配置&#xff0c;他要连接数据库&#xff0c;我去他存储数据库的表里面&#xff0c;看到了相关的版本信息。 cd /home/dolphinscheduler/dolphinscheduler/bin/env dolphinscheduler找到了里面的密码 版本是3.1.3

14.编写自动化测试(上)

标题 一、如何编写测试1.1 一些概念1.2 测试函数剖析1.3 使用assert!宏检查结果1.4 使用assert_eq!和assert_ne!宏来测试相等1&#xff09; assert_eq!2&#xff09; assert_ne! 1.5 使用 should_panic 检查 panic 二、将 Result<T, E> 用于测试 一、如何编写测试 1.1 一…

Windows defender bypass | 免杀

官方文档 在制作免杀的过程中,翻找 Windows 官方对 Windows Defender 的介绍,发现有这样一个目录:Configure Microsoft Defender Antivirus exclusions on Windows Server(在 Windows server 中配置defender排除项)。 https://docs.microsoft.com/en-us/microsoft-365/se…

N32G45XVL-STB之移植LVGL(8.4.0)

目录 概述 1 系统软硬件 1.1 软件版本信息 1.2 ST7796-LCD 1.3 MCU IO与LCD PIN对应关系 2 认识LVGL 2.1 LVGL官网 2.2 下载V8.4.0 3 移植LVGL 3.1 硬件驱动实现 3.2 添加LVGL库文件 3.3 移植和硬件相关的代码 3.3.1 驱动接口相关文件介绍 3.3.2 重新接口函数 3…

视频合成渲染服务解决方案,数字人+PPT+视频云剪辑

在金融理财领域&#xff0c;一个生动、直观、专业的视频&#xff0c;往往能够在海量信息中脱颖而出&#xff0c;帮助客户更好地理解产品、把握市场动态。然而&#xff0c;传统的视频制作方式往往周期长、成本高、难以适应快速变化的市场需求。 美摄科技&#xff0c;作为行业领…

XZ后门故事:初始分析

2024年3月29日&#xff0c;Openwall OSS安全邮件列表上的一条消息“炸醒”了整个信息安全、开源和Linux社区&#xff1a;XZ出现了一个CVSS评分10.0的恶意后门。 这个后门库的特殊危险在于OpenSSH服务器进程sshd使用它。在多个基于systemd的发行版上&#xff08;包括Ubuntu、De…

从根源解决问题:构建体系化BOM管理机制与解决方案

BOM&#xff08;物料清单&#xff09;是设计与生产间的纽带&#xff0c;其准确及时对企业的竞争力至关重要。然而&#xff0c;维护BOM数据时&#xff0c;常遇到录入错误、信息孤岛及跨部门沟通障碍等难题&#xff0c;直接影响生产效率和成本。为此&#xff0c;道合顺将探讨确保…

Hi3861 OpenHarmony嵌入式应用入门--点灯

本篇实现对gpio的控制&#xff0c;通过控制输出进行gpio的点灯操作。 硬件 我们来操作IO2&#xff0c;控制绿色的灯。 软件 GPIO API API名称 说明 hi_u32 hi_gpio_deinit(hi_void); GPIO模块初始化 hi_u32 hi_io_set_pull(hi_io_name id, hi_io_pull val); 设置某个IO…

如何使用xurlfind3r查找目标域名的已知URL地址

关于xurlfind3r xurlfind3r是一款功能强大的URL地址查询工具&#xff0c;该工具本质上是一个CLI命令行工具&#xff0c;可以帮助广大研究人员从多种在线源来查询目标域名的已知URL地址。 功能介绍 1、从被动在线源获取URL地址以实现最大数量结果获取&#xff1b; 2、支持从Way…

遗传算法浅理解

1. 什么是遗传算法&#xff1f; ​ 遗传算法&#xff0c;又称为 Genetic algorithm(GA)Genetic algorithm(GA)。其主要思想就是模拟生物的遗传与变异。它的用途非常广泛&#xff0c;可以用于加速某些求最大或者最小值的算法&#xff08;换句话说就是加速算法收敛&#xff0c;最…

移动端专业视频剪辑解决方案,深度编辑,专业级体验

面对众多繁杂的移动端视频编辑软件&#xff0c;如何挑选一款既高效又专业的解决方案&#xff0c;成为众多企业关注的焦点。美摄科技凭借其卓越的技术实力&#xff0c;推出了面向企业的移动端专业视频剪辑解决方案&#xff0c;助力企业轻松打造高质量视频内容。 一、深度编辑&a…

OAuth 2.0:现代应用程序的授权标准

前言 随着互联网和移动应用的发展&#xff0c;应用程序之间的交互变得越来越普遍。用户希望通过单一的身份认证在多个平台上无缝体验&#xff0c;这就要求不同的应用程序能够安全地共享用户数据。而 OAuth 2.0 正是为了解决这一问题而设计的&#xff0c;它提供了一种标准机制&…

Golang 百题(实战快速掌握语法)_1

整形转字符串类型 实验介绍 本实验将展示三种方法来实现整形类型转字符串类型。 知识点 strconvfmt Itoa 函数 代码实例 Go 语言中 strconv 包的 itoa 函数输入一个 int 类型&#xff0c;返回转换后的字符串。下面是一个例子。 package mainimport ("fmt"&qu…

wps 二维数据转转一维度数据

HSTACK(TOCOL(C2:H2&A3:A8),TOCOL(B3:B8&C1:H1),TOCOL(C3:H8))

网络编程(三)UDP TFTP协议

文章目录 一、 UDP&#xff08;一&#xff09;概述&#xff08;二&#xff09;流程 二、收发函数&#xff08;一&#xff09;recvfrom&#xff08;二&#xff09;sendto 三、实现一个简单的udp服务端和客户端四、实现tftp客户端协议 一、 UDP &#xff08;一&#xff09;概述 …

Spring-事件

Java 事件/监听器编程模型 设计模式-观察者模式的拓展 可观察者对象(消息发送者) Java.util.Observalbe观察者 java.util.Observer 标准化接口(标记接口) 事件对象 java.util.EventObject事件监听器 java.util.EventListener public class ObserverDemo {public static vo…