Spring IoCDI

devtools/2024/11/14 13:04:23/

一.引言

1.什么是Spring

前面我们说Spring是一个开源框架,它让我们的开发更为简单,它支持广泛的应用场景,有活跃庞大的社区。但这样说还是很抽象。今天我们就对他进行具象的描述

一句话说,Spring就是包含了众多工具的IoC容器。

Spring的两大核心思想就是IOC和AOP。

什么是容器?

容器就是盛放东西的,比如list/map是盛放数据的容器,tomcat是盛放web项目的容器,而Spring则是盛放对象的容器

什么是IOC?

IoC:Inverse of Control,控制反转。也就是说Spring是一个控制反转的容器。

什么是控制反转?即控制权的反转,控制权表示创建对象的控制权,反转指从代码/程序反转转移到Spring。

也就是说,当需要某一个对象时,或者说依赖于某个对象时,传统方式下java源代码就要进行new操作,但现在就不需要创建对象了,把创建对象的任务交给Spring来实现,想要哪个对象,直接从spring中取即可(也就是进行依赖注入DI)

2.IoC介绍

我们以造一辆车为例进行讲述

传统程序开发

汽车依赖于车身,车身依赖于底盘,地盘依赖于轮胎,所以有了下面几个类

但现在就会发现,tire尺寸是固定的,那要是我们想在造车时进行指定呢?那就是在new car时传入size参数,然后car的依赖framework在创建时也要传递这个参数,然后再framework的依赖bottom创建时还要传递这个参数,直到层层传递才能传递到tire那里,这就导致我们要修改好多出代码,如下:

这就出现了问题:当最底层代码进行修改后,整个调用链上的代码都得进行修改(其实不管是哪里修改,对应的调用链上的代码就得全部修改)

也就是说程序的耦合程度非常高

解决方案

在上面的开发中,汽车依赖车身,车身依赖底盘,底盘依赖轮胎;但我们要是将依赖关系转换过来:车身大小根据汽车整体大小进行选择,底盘大小根据车身大小进行选择,轮胎大小根据底盘大小进行选择;也就是底盘以来车身,轮胎依赖底盘

IoC开发

这样,要是想换一个轮胎大小,直接修改一处代码即可

也就完成了代码间的解耦合。

我们来和传统开发对比一下:在传统开发中,程序的入口是直接创建汽车,然后建车身,然后是底盘,最后是轮胎。而在IoC开发中,则是先创建好轮胎,轮胎准备好了再供底盘使用……

在传统开发中每个类里面对于依赖的对象都是主动new出来,而在ioc思想中,每个类里面以来的对象都是外部提供的

IoC开发的实际以及优点

在ioc开发中,下面这部分都是用Spring进行的

Spring创建这些对象,我们只需要使用即可,比如Spring创建了tire,我们只要在CarBottom中使用即可。Spring创建了CarBottom,我们只要在carFramework中使用即可

优点:

资源不由使用资源的各方进行管理,而由不使用资源的其他一方进行管理。首先,资源集中管理,实现了资源的可配置和易管理;其次,降低了使用资源的各方的依赖程度,也就是解耦合。

Spring就是一种IoC容器,帮助我们进行资源管理

3.DI介绍

DI即Dependency Injection,依赖注入

容器在运行过程中,动态的为应用程序提供运行时所依赖的资源,就是依赖注入

二.IOC DI的使用

Spring是一个容器,容器就有两个功能,一个就是存,另一个就是取

Spring存取的是对象,我们将这些对象称之为Bean。我们只需要通过程序,告知Spring哪些对象要进行管理,也就是告诉Spring哪些对象要进行存,以及如何从Spring中取出对象

1.存对象

bean的存储是通过注解实现的,可以分为两类:

1.类注解(五大注解):@Controller @Service @Component @Repository @Configuration

2.方法注解:@Bean

五大注解/类注解

@Controller(控制器存储)

那如何执行到这个类中的方法呢?如何观察到这个对象已经存在于Spring容器中呢?

先看一下Spring的启动类(加了@SpringBootApplication注解的就是启动类):

这里面自带一个run方法,他的返回值就是Spring容器,我们应该用ApplicationContext来接收

ApplicationContext常被称为Spring上下文,其实应该理解为应用环境,也就是Spring运行的环境

 ctx中存放到就是受到Spring管理的对象,然后我们通过getBean方法传入对象的类型来获取到对象。但要是将UserController类上的@Controller给删除掉,就会报错没有bean的定义

bean对象的其他获取方式:

进入到getBean方法,我们会发现这个方法是BeanFactroy这个接口的,而不是ApplicationContext这个接口的,因为ApplicationContext继承了BeanFactory

它里面有多个重载方法,常用的就是以下三个

name指的就是Bean的名称。当将对象交给Spring进行管理时,如果没有指定bean的名字,那么Spring就会给它一个默认的名字。一般来说就是类名的首字母小写。但当类名的前两个字母都是大写时,bean的名称就是类名本身

示例如下:

@Service(服务存储)

和@Controller的使用方法一模一样

@Component(组件存储)
@Configuration(配置存储)
@Repository(仓库存储)

以上五大注解的使用方法完全一样,只要加在类上面即可。

为什么要这么多类注解?

这和前面提到的应用分层相呼应:让程序员看到类注解之后,就能直接了解到当前类的用途

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

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

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

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

这样有了分工,整个调用流程就有了框架,前端调用@Controller层代码,发送请求。@Controller通过调用@Service层代码帮助完成响应的形成,而@Service层又会通过调用@Repository层代码完成数据访问,进而完成整体业务处理

类注解之间的关系

查看@Controller/@Service/@Repository/@Configuration注解的源码,发现它们里面都有一个注解@Component,说明它们都是@Component的子类,是他的衍生注解。@Component就是一个元注解,也就是说注解其他类注解

方法注解@Bean

五大注解存在问题:

1.五大注解只能加在类上,并且是自己写的代码上,但是要是使用外部包(第三方包)里面的类,就没有办法添加类注解了

2.一个类可能需要多个对象,比如多个数据源,但是通过类注解反复获取对象其实获取到的是同一个对象,如下:

返回结果是true

这时就要转换使用@Bean方法注解。我们先来看看方法注解如何使用:创建一个config(表示配置,在这个例子里就是Spring创建对象,对对象进行配置)包,UserInfo类

再写一个BeanConfig类

然后尝试获取对象

报错啦!

没有bean定义

方法注解要搭配类注解

Spring对对象的管理不是说有个@Bean就会去创建对象,要想扫描到@Bean,首先要能够让Spring扫描到@Bean修饰的方法所在的类,所以要给BeanConfig类加上类注解

这样就能够扫描到啦

定义多个对象

这回又报错啦:

他说没有唯一的对象,找到啦两个

所以当定义了多个对象时,就不能仅仅使用类来进行获取,而应该是用bean名称进行获取。那么Bean的名称是啥?

由报错信息可以分析出,bean的名称就是方法名!!!即userInfo1,userInfo2

重命名bean

可以通过设置name属性进行命名

这两种方法都可以

如果只有一个名字,可以不加{}

Spring扫描路径

使用前面学习的五大注解声明的Bean一定会生效吗?不一定

因为要想生效,就要让该类被Spring扫描到。下面我们将启动类放到carController包下面,而BeanConfig还在config包下面

这次就报错了

这是因为,Spring没有扫描到BeanConfig类,要想被扫描到,就要加上@ComponentScan注解

那为啥之前不加就可以?

之前@ComponentScan虽然没有显式配置,但是@SpringBootApplication中已经包含了。并且默认扫描路径是SpringBoot启动类所在包及其子包

更为推荐到做法是将启动类放到我们期望扫描到的包底下

2.取对象——DI详解

上面我们将来控制反转的细节,现在我们来讲一讲依赖注入的细节

依赖注入表示:IoC容器在创建Bean时,去提供运行时所依赖的资源,而这里的资源指的就是对象。

简单来说就是把对象给取出来,放到某个类的属性中

关于依赖注入,有三个方法

属性注入:

属性注入是使用@Autowired注解完成。例如

实际上就是Spring已经通过@Service这个注解将UserService这个类的对象创建出来了,然后在构造UserController对象时,通过@Autowired注解将已经存在的Service对象取出来赋值给Controller对象

构造方法注入:

直接对该类加上一个构造方法即可

但注意:如果这个类只有一个构造方法,并且对成员变量进行了赋值,那么后续调用func方法就不会报错。但如果有多个构造方法,就会报错:

第一种情况是一个有参构造,一个无参构造,那么Spring就会默认使用无参构造,那么在调用func方法时就会出现空指针异常

第二种情况是两个有参构造,没有无参构造,那么就会连编译都不会通过,因为如下:

上面写了不知道要匹配哪个构造方法。所以这时候就应该通过@Autowired、注解来指定要使用哪个构造方法

Setter注入:

在Setter方法上要加上@Autowired注解

三种注入的优缺点

属性注入

优点:简洁

缺点:只能用于IoC容器,非IoC容器不可用;并且无法注入一个final修饰的属性。final修饰的属性值不可变,要么在定义时赋值,要么在构造方法中赋值

构造方法注入

优点:可以注入final修饰的属性;注入的对象不会被修改;依赖对象会在使用前完全被初始化,因为依赖对象是在类的构造方法中执行,而构造方法会在类加载阶段执行

缺点:当yilai对象多时,代码冗余

Setter方法注入

优点:方便在类实例后,重新对对象进行配置或赋值

缺点:不能注入final属性,注入对象可能会被改变,因为setter方法可能会被多次调用

@Autowired存在的问题

当同一个类型存在多个Bean对象时,简单使用@Autowried就会出现问题

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

如何解决上述问题

修该成员变量的名称

这个名称和其中一个Bean的名称相同

@Primary

使用@Primary注解,确认默认的实现使哪个对象

@Qualifier

在@Qualifier中指定要使用的Bean对象的名称。这个注解不能单独使用,只能搭配@Autowried注解使用

@Resource

这个注解表示按照Bean的名称进行注入。不用搭配@Autowired注解

@Resource和@Autowried的区别:

@Autowried是Spring框架提供的注解。@Resource是JDK提供的

@Autowried默认是按照类型进行注入,@Resource是按照名称进行注入

@Resource支持更多参数设置,例如name设置,从而根据名称获取Bean


http://www.ppmy.cn/devtools/34321.html

相关文章

通用代码审查清单

通用代码审查清单是一个确保代码质量、可读性、可维护性和遵循最佳实践的重要工具。 以下是一个汇总的审查清单,结合了多个来源的信息,适用于多种编程语言和项目: 功能实现 • 代码是否实现了设计文档或需求规格说明中规定的所有功能&…

Dragonfly 拓扑的路由算法

Dragonfly 拓扑的路由算法 1. Dragonfly 上的路由 (1)最小路由(2)非最小路由 2. 评估 Dragonfly 拓扑的路由算法 John Kim, William J. Dally 等人在 2008 年的 ISCA 中提出技术驱动、高度可扩展的 Dragonfly 拓扑。而文章中也提到了 针对 Dragonfly 拓扑的路由算法。本文对…

还有谁……想知道“线下与线上布局之间的本质区别”

还有谁……想知道 线下与线上布局之间的本质区别 hello,亲爱的你们好.… 我是你们的好朋友,正博,今天是非常特殊的一天,给每一位读者准备了一份特殊的神秘礼物…… 在分享【特殊礼物】之前,请允许我分享一下《线下营销与线上销售的9大核心差异》…… 1、注意力; 2、销…

探索设计模式的魅力:分布式模式让业务更高效、更安全、更稳定

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》 💪🏻 制定明确可量化的目标,坚持默默的做事。 ✨欢迎加入探索分布式模式之旅✨ 在数字化时代,企业面临着前所未有的挑战和机遇。…

(论文阅读-多目标优化器)Multi-Objective Parametric Query Optimization

目录 摘要 一、简介 1.1 State-of-the-Art 1.2 贡献和大纲 二、定义 三、相关工作 四、问题分析 4.1 分析 4.2 算法设计影响 五、通用算法 5.1 算法概述 5.2 完备性证明 六、分段线性代价函数算法 6.1 数据结构 6.2 基本运算实现 6.3 复杂度分析 七、实验评估 …

原来spring也可以AI

最近大模型是相当的火,尤其是在自然语言处理(NLP)、图像识别、语音识别等领域的应用,那对于工程同学来说应该如何接住这波破天的富贵呢? 想啥来啥,前段时间LangChain给我们整了一套钢铁战甲,让…

四工位自动攻丝机自动控制

/**************进料检测********************/ /***缺料无限次循环 手动退出 超时报警*******/ void check_Pon() { zstatus0; //缺料报警计数器归零 Signauto1; //…

kubectl_进阶_安全

安全 在前面的学习中,我们知道对于资源对象的操作都是通过 APIServer 进行的,那么集群是怎样知道我们的请求就是合法的请求呢? 这就涉及到k8s的安全相关的知识了。 1. API对象 Kubernetes有一个很基本的特性就是它的所有资源对象都是模型…