Spring IoC——IoC 容器的使用

1. 应用分层

应用分层是一种软件开发设计思想,它将应用程序分成 N 个层次,这 N 个层次分别负责各自的职责,多个层次之间协同提供完整的功能,根据项目的复杂度,可以分成三层,四层或更多层,MVC 就是把整体的程序分成了 Model(模型), View(视图), Controller(控制器)三个层次

由于后端开发,不需要过多的关注前端,所以又有了一种分层架构:把整体架构分为表现层,业务逻辑层,数据层,又称为“三层架构”

  1. 表现层:用来展示数据结果和接收用户指令,是最接近用户的一层
  2. 业务逻辑层:负责处理业务逻辑,包含业务逻辑的具体实现
  3. 数据层:负责存储和管理与应用程序相关的数据

在 Spring 的实现中可以分为下面三个部分:

Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据

Service:业务逻辑层。处理具体的业务逻辑

Dao:数据访问层,也被称为持久层。负责数据访问,操作(增删查改)

2. IoC 的介绍

IoC:也就是控制反转

Spring IoC 是一种设计模式,用于解耦对象之间的依赖关系,在之前创建的项目中对象通常会主动创建和管理自己所依赖的对象,例如,一个UserService类可能会在自己的内部使用new关键字来创建一个UserRepository对象用于数据访问,这样设计看似没有问题,但是可维护性却很低,当有很多类创建了各自的对象时,并且这些对象之间还有依赖关系,例如创建 Car ,Framework,Bottom,Tire 类,从左到右依次存在依赖关系,当其中有一个类的底层代码改变之后,调用链上的代码都需要修改

public class NewCarExample {
    public static void main(String[] args) {
        Car car = new Car(20);
        car.run();
    }
    //Car类
    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);
        }
    }
}

修改轮胎的构造方法之后,底盘也需要修改,当修改底盘之后,上层调用也需要修改

而在 IoC 模式下,对象的创建和管理这些控制权被反转了,不再由对象自身来控制,而是交给外部的容器(IoC 容器)来管理,下面演示一下使用 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);
        }
    }
}

通过这样的形式,各个组件的依赖关系就发生了反转,统一对对象进行管理,谁需要这个对象直接传过去一个对象,不需要他自己调用方法进行创建

IoC 容器的工作就是把这些对象进行统一管理

通过这种方式进行资源的统一管理,在创建实例时不需要了解其中的细节,降低了使用资源双方的依赖程度

3. IoC 容器的使用

3.1. bean 的存储

如果想要把一个对象交给 IoC 容器来管理,需要在类上添加一个 @Component 注解,此外还有其它的一些注解可以实现:

  1. 类注解:@Controller@Service@Repository@Component@Configuration.
  2. 方法注解:@Bean.
@Controller
public class UserController {
    public void say(){
        System.out.println("UserController");
    }
}
@SpringBootApplication
public class SpringIoCApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args);
        UserController bean = context.getBean(UserController.class);
        bean.say();
    }

}

ApplicationContext 可以理解为 Spring 的上下文,这个上下文就是指当前的运行环境和其他功能,也可以看作是一个容器,容器中存储了很多内容,这些内容是当前的运行环境

之后就可以通过拿到的 context 来获取 bean,关于获取 bean 有多种方式:

Object getBean(String var1) throws BeansException;

根据bean名称获取bean

T getBean(String var1, Class var2) throws BeansException;

根据bean名称和类型获取bean

T getBean(Class var1) throws BeansException;

根据类型获取bean

Object getBean(String var1, Object... var2) throws BeansException

按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean

T getBean(Class var1, Object... var2) throws BeansException;

按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的 bean

根据类型获取 bean 的话,如果存在多个相同类型的 bean 那么就不能确定具体要获取的是哪个 bean ,同理,如果只是根据名称来获取 bean,如果重名的话也是不能正确获取到 bean 的,所以就有了第三种方式,同时根据类型和名称来获取 bean

上面这三种获取 bean 的方式是比较常用的

关于 IoC 中 bean 的名称转化规则:

如果是 UserController 会被转成 userController,如果是 UController 就还是 UController

UserController bean1 = (UserController) context.getBean("userController");
bean1.say();

由于是根据名称来获取 bean,所以获取到的 bean 不确定是什么类型,会返回一个 Object 类型,需要强转一下

同时指定类型和名称:

UserController bean2 = context.getBean("userController", UserController.class);
bean2.say();

把上面获取的 bean ,打印一下,发现获取的实例的地址是一样的,由此可以知道,通过这种方式获取对象是基于单例模式实现的

接下来演示一下 @Service 注解:

@Service
public class UserService {
    public void say(){
        System.out.println("UserService");
    }
}
UserService service = context.getBean(UserService.class);
service.say();

然后发现和上面一样也是可以运行的,其它的几个类注解也是一样的

那么为什么实现的功能一样,还需要分这么多不同的注解,这个是和之前的应用分层是对应的,通过不同的类注解来了解当前类的用途

@Controller:控制层,接受请求,对请求进行处理,并进行响应

@Servie:业务逻辑层,处理具体的业务逻辑

@Repository:数据访问层, 也称为持久层,负责数据访问操作

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

3.2. 方法注解@Bean

上面四个注解都是 @Component的衍生注解

类注解是添加到某个类上的,但是存在两个问题:

  1. 如果使用外部包里的类,没办法添加注解
  2. 同时,由于类注解默认创建的对象是单例对象,如果需要多个对象就需要调整

方法注解 @Bean就可以解决上述问题

例如,在一个外部包里有一个 UserInfo 类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
    private String name;
    private Integer age;
}

可以通过在获取对象的方法上加上@Bean

@Component
public class UserInfoComponent {
    @Bean
    public UserInfo userInfo(){
        return new UserInfo("zhangsan",20);
    }
}

如果说需要创建对个对象的话:

这个问题就是在获取 bean 的时候发现了具有相同类型的 bean,可以直接通过获取 bean 的名称,这里的名称和方法注解下方法名是对应的

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

上面的注解,无论是类注解还是方法注解,都可以实现重命名

重命名之后就需要使用改之后的名字了

4. 扫描路径

如果说把启动类放到其他目录下再运行就会报错,是因为上面介绍的注解如果想要生效,是需要配置扫描路径的,默认的扫描范围是 Spring Boot 启动类所在的包和它的子包,可以通过@ComponentScan来配置扫描路径

@ComponentScan源码中,是支持传入一个数组的,如果想要配置多个扫描路径可以直接传入一个数组

@ComponentScan({"com.example.service","com.example.controller"})

我的主页

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

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

相关文章

人工智能进程;算子加速的具体计算部分;大模型GPT5:参数18万亿;大模型面临问题

目录 人工智能进程 算子加速的简单理解,举例说明 一、简单理解 二、举例说明 一、算子加速的具体计算部分 二、举例说明 三、算子加速是否仅针对GPU 大模型GPT5:参数18万亿 大模型面临问题 算力集群设计框架 人工智能进程

深入理解Java集合:从基础到高级应用

深入理解Java集合:从基础到高级应用 1. 数组与集合的区别 1.1 相同点 数组和集合都是用于存储多个数据的容器,但它们的使用场景和特性各有不同。 1.2 不同点 长度:数组的长度在创建时就固定了,而集合的长度是动态可变的&…

【自动化测试之oracle数据库】MacOs如何安装oracle- client

操作系统为Mac OS,本地在pycharm上跑自动化脚本时,因为有操作oracle数据库的部分,所以需要安装oracle数据库的客户端,并install cx_oracle,本文主要介绍如何在macOS上完成安装,并在python自动化测试代码中配置&#xf…

如何在vscode中使用鼠标滑轮滚动来改变字体大小

实现内容:如何在vscode中使用鼠标滑轮滚动来改变字体大小 使用场景:我是在Ubuntu中安装的vscode 需求:因为最近在用这个,但是在使用过程中发现vscode的字体大小有点小,所以想改变下 实现滚轮滑动改变字体大小的具体步…

鸿蒙NEXT应用上架与分发步骤详解

大家好,我是 V 哥。今天的文章来聊一聊HarmonyOS NEXT应用上架。当你开发、调试完HarmonyOS应用/元服务,就可以前往AppGallery Connect申请上架,华为审核通过后,用户即可在华为应用市场获取您的HarmonyOS应用/元服务。 V 哥推荐&a…

【隐私计算篇】全同态加密应用场景案例(隐私云计算中的大模型推理、生物识别等)

1.题外话 最近因为奖项答辩,一直在忙材料准备,过程非常耗费时间和精力,很难有时间来分享。不过这段时间虽然很忙碌,但这期间有很多新的收获,特别是通过与领域内专家的深入交流和评审过程,对密码学和隐私计算…

安卓开发之登录页面(跳转版)

目录 前言:基础夯实:效果展示:核心代码:网盘源码: 前言: 熟悉安卓开发的基础知识,了解,弹窗,两个页面进行跳转,页面的布局,按钮,文本…

【牛客刷题实战】二叉树遍历

大家好,我是小卡皮巴拉 文章目录 目录 牛客题目: 二叉树遍历 题目描述 输入描述: 输出描述: 示例1 解题思路 问题理解 算法选择 具体思路 解题要点 完整代码(C语言) 兄弟们共勉 !&…

多个项目同时进行,如何做好项目管理?

多项目管理相较于单一项目管理,要面临更大的挑战和难度。多项目管理需要同时管理和协调多个项目,使用项目管理工具可以帮助项目经理和团队成员更好地规划、执行和监控项目。以下是七款多项目管理软件,它们各具特色,能够满足不同项…

[vulnhub] Brainpan1

https://www.vulnhub.com/entry/brainpan-1,51/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的,所以靶机IP是166 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-1…

Java避坑案例 - 线程池使用中的风险识别与应对

文章目录 线程池的基本概念创建线程池的注意事项实例1: newFixedThreadPool 使用无界队列,可能因任务积压导致 OOM实例2: newCachedThreadPool 会创建大量线程,可能因线程数量过多导致无法创建新线程。 线程池参数设置的最佳实践线…

Pytest-Bdd-Playwright 系列教程(5):仅执行测试用例的收集阶段

Pytest-Bdd-Playwright 系列教程(5):仅执行测试用例的收集阶段 一、为什么需要仅收集测试用例二、应用场景三、方法详解【方法1】:添加pytest.ini文件的addopts配置项【方法2】:通过命令行参数运行 四、CI/CD 环境下的…

机器人技术基础(4章逆运动解算和雅克比矩阵)

逆运动解算: 雅克比矩阵: 将动力学分析转向运动的物体 下图中的 n o y 反映了机器人的姿态矩阵, 最后一列 p 反应了机器人在空间中的位置:

未来已来:人工智能赋能软件开发新篇章

引言 在数字化转型的浪潮中,数据已成为推动企业创新与增长的核心资产,而人工智能(AI)则是将这些数据转化为商业价值的关键动力。随着技术的迅速演进,AI 正逐步渗透到软件开发的各个环节,从需求预测到用户体…

C#实现视频会议录制(支持Windows、Linux、银河麒麟、统信UOS)

随着远程办公与异地协作越来越频繁,视频会议系统的使用也是越来越普遍。同时,用户对视频会议系统的功能也提出了更高的要求,比如,其中之一就是希望可以将整个视频会议的过程录制下来,以备之后可以查阅观看。 我们可以…

树莓派开发相关知识四 传感器-温湿度传感器

1、概述 使用DHT11温湿度传感器,传感周期为1s。 DHT11模块一般由3/4个引脚组成,每一次收集数据为40bit。 分别为: 高位在前、8bit湿度整数数据8bit湿度小数数据8bi温度整数数据8bit温度小数数据8bit校验和 我们需要解决的问题,…

vue3+less使用主题定制(多主题定制)可切换主题

假如要使用两套主题:蓝色、红色 例如: 首先确保自己的vue3项目有less,这边不多做接入解释 1、在src目录下建一个styles文件夹,在syles文件夹下面新建两个less文件:theme.less和variables.less; theme.le…

Spring Cloud Sleuth(Micrometer Tracing +Zipkin)

分布式链路追踪 分布式链路追踪技术要解决的问题,分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节…

春季测试 2023 我的题解

T1 涂色游戏 这道题目还是比较简单的 容易发现,位于 ( x i , y i ) (x_i,y_i) (xi​,yi​) 的格子的颜色只取决于 ​ x i x_i xi​ 行与 y i y_i yi​ 列的颜色。 这时候可以想到开两个数组,分别存储列与行的绘画信息,然后发现前后的互相…

Kali Linux 新工具推荐: Sploitscan

在 2024.2 版本 Kali Linux 增加了一个新攻击工具: Sploitscan 1.简介: Sploitscan 能够发现操作系统和应用程序中的安全漏洞。 2.特点: 简单的命令行界面 扫描多个操作系统和应用程序 检测多种漏洞 提供详细信息 可定制性强 3.示例: 2024.2 及以后的版本 Kali Linux…