长文干货!一文搞懂IoC的依赖注入(ioc依赖注入名词解释)
toqiye 2024-10-14 17:31 10 浏览 0 评论
有梦想,有情怀,有温度,干货满满; 关注一下我吧。
一、注解驱动IoC
xml驱动的IoC容器使用的是ClassPathXmlApplicationContext读取xml内bean信息
注解驱动的IoC容器使用的是AnnotationConfigApplicationContext读取Java类中的bean信息
1. AnnotationConfigApplicationContext 的注册使用
相比于xml文件作为驱动, 注解驱动需要指明配置类 一个配置类可以理解为"相当于"一个xml 配置类只需要在类上标注注解 @Configuration
@Configuration
public class DemoConfiguration {
}
在xml中声明bean的方式
<bean id="person" class="com.huodd.bean.Person"></bean>
在配置类中使用的是@Bean注解
@Bean
public Person person() {
return new Person();
}
说明: 向IoC容器注册一个类型为Persion,id为Person的Bean
方法名表示的是bean的id 返回值表示的是注册的bean的类型
@Bean注解也可以显示的声明bean的id 如 @Bean("person1")
2. 注解IoC容器的初始化
public class AnnotationConfigApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoConfiguration.class);
Person person = ctx.getBean(Person.class);
System.out.println(person);
}
}
运行后Person控制台打印结果
com.huodd.bean.Person@55536d9e
3. 组件的注册和扫描
上述初始化时 我们在使用AnnotationConfigApplicationContext时传递了参数 Class<?>... componentClasses
翻看AnnotationConfigApplicationContext的构造方法可以发现还可以传递参数的参数类型还有 String... basePackages
这里就涉及到组件的注册和扫描
这里可以思考一个问题, 如果我们要注册的组件特别多, 那进行编写这些@Bean的时候代码工作量也会特别多,这时候该如何解决呢?
Spring 给我们提供了几个注解,可以帮助我们快速注册需要的组件, 这些注解被称为模式注解(stereotype annotations)
@Component
@Component可以说是所有组件注册的根源 在类上标注 @Component 代表该类被注册到IoC容器中作为一个Bean
@Component
public class Person {
}
如果未指定 Bean 的名称 默认规则是 "类名称首字母小写" 上面的bean名称默认会是 person
如果要自定义bean的名称 可以在@Component声明value的值即可 如
@Component("person1")
public class Person {
}
在xml中相当于
<bean id="person1" class="com.huodd.bean.Person"/>
@ComponentScan
这个时候 如果我们直接运行启动类 获取Person的bean对象,会报错NoSuchBeanDefinitionException 这是为什么呢?
因为我们只是声明了组件,而后直接启动了IoC容器,这样容器是感知不到有@Component存在的,
解决方案1:
我们需要在写配置类时再额外标注一个新的注解@ComponentScan
目的是告诉IoC容器 我要扫描哪个包下面的带有@Component注解的类
@Configuration
@ComponentScan("com.huodd.bean")
public class DemoComponentScanConfiguration {
}
注: 如果不指定扫描路径, 则默认扫描本类所在包及所有子包下带有@Component的组件
启动类代码如下:
public class AnnotationConfigApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);
Person person = ctx.getBean(Person.class);
System.out.println(person);
}
}
解决方案2:
这里也可以不写@ComponentScan 而直接在AnnotationConfigApplicationContext方法参数内传入String类型的包扫描路径 代码如下
public class AnnotationConfigApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean");
Person person = ctx.getBean(Person.class);
System.out.println(person);
}
}
PS: 组件扫描并非是注解驱动IoC所特有的, 其实在xml驱动的IoC模式下 同样可以启用组件扫描, 只需要在xml中声明一个标签即可
<context:component-scan base-package="com.huodd.bean"/>
这里需要注意下: 如需要扫描多个路径,需要写多个标签 也就是 一个标签只能声明一个根包
组件注册的补充
SpringFramework 提供了在进行Web开发三层架构时的扩展注解: 分别为 @Controller、 @Service 、@Repository 小伙伴有没有很熟悉?
分别代表 表现层、业务层、持久层 这三个注解的作用与 @Component完全一样 扒开源码我们可以看到 底层在这三个注解类上又添加了 @Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
}
这样 我们在进行符合三层架构的开发时 对于相应的如 ServiceImpl等 就可以直接标注 @Service 等注解了
@Configuration
@Configuration 底层也有标注@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration { ... }
由此可以说明,配置类不是向我们所想的那样,只是单纯的做一个配置而已, 它也会被视为 bean,也被注册到IoC容器里面
4. 注解驱动与xml驱动互相引用
4.1 xml引用注解
需开启注解配置 再注册相应配置类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解配置 -->
<context:annotation-config />
<!-- 注册配置类 -->
<bean class="com.huodd.config.AnnotationConfigConfiguration"/>
</beans>
4.2 注解引用XMl
需在配置类上标注 @ImportResource 并声明配置文件的路径
@Configuration
@ImportResource("classpath:annotation/demo-beans.xml")
public class ImportXmlAnnotationConfiguration {
}
二、IoC的依赖注入
1.Setter属性注入
创建对象 将属性值set进去 之后返回对象
@Bean
public Person person() {
Person person = new Person();
person.setId(1);
person.setName("PoXing");
person.setAge(18);
return person;
}
xml中的setter注入
<bean id="person" class="com.huodd.bean.Person">
<property name="id" value="1"/>
<property name="name" value="PoXing"/>
<property name="age" value="18"/>
</bean>
2. 构造器注入
使用构造器注入,需要在bean本身添加有参构造方法, 如在Person中添加有参构造方法如下
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
注解驱动中,我们创建bean的时候注入属性时 就需要同时指定参数值
@Bean
public Person person() {
return new Person(1, "PoXing", 18);
}
xml驱动中如下
<bean id="person" class="com.huodd.bean.Person">
<!--
index: 表示构造器的参数索引
value: 表示对应的参数值
-->
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="PoXing"/>
<constructor-arg index="2" value="18"/>
</bean>
3. 注解式属性注入
这里先说明一下,为何会有注解式属性值注入. 细心的小伙伴可能会发现 上面我们谈到的 Setter属性注入、构造器注入 好像在只能是在使用 @Bean注解的时候时候使用, 但是 如果是通过标注 @Component注解的组件呢(像前面我们的Person类中标注了@Component注解),怎么给它设定属性值, 该节主要就是说一下这部分
@Component 下的属性注入
这里我们使用Dog类做为演示(这里我悄悄的添加了@Component注解 自己尝试的小伙伴要注意哦 否则会报错的)
@Component
public class Dog {
private Integer id;
private String name;
private Integer age;
... 省略 Getter、Setter
... 省略 toString
}
这里要实现注解式属性注入,可以直接在要注入的字段上标注 @Value注解 如
@Value("1")
private Integer id;
@Value("wangcai")
private String name;
@Value("3")
private Integer age;
启动类代码如下
public class DiApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean");
Dog dog = ctx.getBean(Dog.class);
System.out.println(dog);
}
}
控制台打印结果
Dog{id=1, name='wangcai', age=3}
外部配置文件(@PropertySource)
这里主要是解决上面的@Value中注入 我们把属性值直接固定写死了,如果要修改 还要去Java代码中去修改,很不符合开发规范,
SpringFramework为我们扩展了新的注解@PropertySource 主要用来导入外部配置文件
- 这里我们创建一个 dog.properties
dog.id=1
dog.name=wangcai
dog.age=3
- 引入配置文件
@PropertySource("classpath:di/dog.properties")
@ComponentScan("com.huodd.bean")
@Configuration
public class DemoComponentScanConfiguration {
}
- Dog类中属性注入 这里@Value需要配合占位符 来获取properties配置文件中的内容
@Value("${dog.id}")
private Integer id;
@Value("${dog.name}")
private String name;
@Value("${dog.age}")
private Integer age;
- 修改一下启动类
public class DiApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);
Dog dog = ctx.getBean(Dog.class);
System.out.println(dog);
}
}
控制台打印结果如下
Dog{id=1, name='wangcai', age=3}
此时配置文件的属性已经注入成功
4.自动注入
在xml模式中有ref属性 可以将一个bean注入到另外一个bean中, 注解模式中也同样可以
@Autowired
给Dog的bean中注入 Person的Bean (即 给dog指定它的主人)
方法1 → 在属性上标注
@Component
public class Dog {
// ......
@Autowired
private Person person;
}
方法2 → 使用构造器注入方式
@Component
public class Dog {
// ......
private Person person;
@Autowired
public Dog(Person person) {
this.person = person;
}
}
方法3 → 使用setter方法注入
@Component
public class Dog {
// ......
private Person person;
@Autowired
public void setPerson(Person person) {
this.person = person;
}
}
JSR250规范下的@Resource
@Resource也是用来属性注入的注解
它与@Autowired的区别是:
@Resource 注解相当于标注 @Autowired 和 @Qualifier
@Qualifier这里简要说明下,为指定bean的名称而存在,如果存在多个相同的bean,而bean的名称不同,我们可以使用@Autowired 配置 @Qualifier注解
如: 下面表示该Dog类注入的主人Bean是名称为 xiaowang的, 而当前容器内可能存在多个 主人bean对象 比如 xiaoli、xiaoming ....
@Component
public class Dog {
// ......
@Autowired
@Qualifier("xiaowang")
private Person person;
}
下面如果使用@Resource 可以更方便些 代码如下
@Component
public class Dog {
// ......
@Resource(name="xiaowang")
private Person person;
}
JSR330规范下的@Inject
@Inject注解也是按照类型注入,与@Autowire的策略一样, 不过如要使用@Inject 需要额外的导入依赖
<!-- jsr330 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
后面的使用方法就与SpringFramework 原生的 @Autowire + @Qualifier 相同了
@Component
public class Dog {
@Inject // 等同于@Autowired
@Named("xiaowang") // 等同于@Qualifier
private Person person;
它与@Autowired的区别是:
5. 复杂类型注入
Array注入
<property name="names">
<array>
<value>PoXing</value>
<value>LaoWang</value>
</array>
</property>
List注入
<property name="tels">
<list>
<value>13000000000</value>
<value>13000000001</value>
</list>
</property>
Set注入-
<!-- 已经提前声明好的Dog -->
<bean id="wangcai" class="com.huodd.bean.ext.Dog"/>
---
<property name="dogs">
<set>
<bean class="com.huodd.bean.Dog"/>
<ref bean="wangcai"/>
</set>
</property>
Map注入
<property name="homesMap">
<map>
<entry key="1" value="main">
<ref bean="myHome1" />
</entry>
<entry key="2" value="other">
<ref bean="myHome2" />
</entry>
</map>
</property>
Properties注入
<property name="props">
<props>
<prop key="sex">男</prop>
<prop key="age">18</prop>
</props>
</property>
面试题
1.@Autowired注入原理是什么?
- 先拿属性对应的类型,去IoC容器中找相应的Bean
- 如果没有找到 直接抛出NoUniqueBeanDefinitionException异常
- 如果找到一个 直接返回
- 如果找到多个相同类型的bean 再拿属性名去与这多个bean的id进行对比
- 如果有多个或者没有 则会抛出NoUniqueBeanDefinitionException异常
- 如果只有一个 直接返回
2.依赖注入的方式有哪些,都有什么区别
注入方式 被注入对象是否可改变 是否依赖IOC框架的API 使用场景 构造器注入 不可变 否 不可变的固定注入 参数注入 不可变 否 注解配置类中 @Bean方法注册 bean 属性注入(注解式属性注入) 不可变 是(只能通过标注注解来侵入式注入) 通常用于不可变的固定注入 setter注入 可变 否 可选属性的注入
3.自动注入的注解对比
注解 注入方式 是否支持@Primary 来源 Bean不存在时处理 @Autowired 根据类型注入 是 SpringFramework原生注解 可指定required=false来避免注入失败 @Resource 根据名称注入 是 JSR250规范 容器中不存在指定Bean会抛出异常 @Inject 根据类型注入 是 JSR330规范 ( 需要导jar包 ) 容器中不存在指定Bean会抛出异常
@Qualifier :如果被标注的成员/方法在根据类型注入时发现有多个相同类型的 Bean ,则会根据该注解声明的 name 寻找特定的 bean
@Primary :如果有多个相同类型的 Bean 同时注册到 IOC 容器中,使用 “根据类型注入” 的注解时会注入标注 @Primary 注解的 bean 即默认策略
4.使用依赖注入有什么优缺点
我是Baoxing,开始做一件事情最好的时机,要么是十年前,要么就是现在。 感谢各位的 点赞、收藏和评论
相关推荐
- 基于Python查找图像中最常见的颜色
-
如果我们能够得知道一幅图像中最多的颜色是什么的话,可以帮助我们解决很多实际问题。例如在农业领域中想确定水果的成熟度,我们可以通过检查水果的颜色是否落在特定范围内,来判断它们是否已经成熟。接下来我们将使...
- 出大要几次/圣彼得堡悖论
-
程序:fromrandomimportrandomdeffn():n=1whilerandom()<0.5:n+=1returnny=[fn()...
- 使用OpenCV测量图像中物体之间的距离
-
原文链接:https://www.pyimagesearch.com/2016/04/04/measuring-distance-between-objects-in-an-image-with-op...
- 让颜色更加饱满和有冲击力:图像颜色校正
-
大家拍照或图片时,获取会遇到图像颜色与实际颜色存在色差的现象。我们看一个标准色卡的图片:第一张图片就是有色差的图片,这种现象一般是相机或光线的原因造成的,我们可以通过标准色卡进行校正。第一张图片是有色...
- Python 数据分析 : 实例
-
1、构建矩阵生成4x4形式的矩阵,矩阵中的数据是1~10之间的随机数random_list=np.random.random(16)random_list=np.round(...
- 用这些免费开源的图标库,为你的项目画龙点睛
-
精致好看的图标能够为你的项目增色不少,今天我就整理了一期图标库精选系列,希望你可以从中找到自己喜欢的图标库。下面就跟我来一场视觉的盛宴,我会一一介绍GitHub上品牌、流行、极小,各具特色的免费精...
- ICON设计规范之图标尺寸
-
编辑导语:图标设计是UI设计中不可缺少的元素,它看似简单,但其实内含门道。本篇文章里,作者就对icon设计的相关知识和icon绘制方法做出经验介绍。如果你对icon设计也想要有所了解的话,那就点进来看...
- PHP开发必备VSCode插件(大全)
-
通用chinese(simplified...):简体中文语言包liveserverhtml:实时预览prettier-codeformatter:最流行的代码格式化插件...
- 增强用户体验:前端开发中HTML5和CSS3表格属性的应用与优化研究
-
摘要:本文探讨了在前端开发中HTML5和CSS3表格属性的应用与优化。首先介绍了HTML5中常用的表格元素和CSS3中丰富的表格样式属性,旨在帮助开发人员定制表格的外观和样式。其次,研究了表格结构的优...
- 产品经理小技术:图片素材随手找,原型设计快又好
-
数十万互联网从业者的共同关注!作者:牛冰峰博客:http://uxfeng.com/画图——这项古老而精细的做法,是一代代产品狗们得以传承的立足之本。草图、线框图、思维导图、PPT插图、数据汇报图表、...
- MAUI Blazor 项目实战 - 从0到1轻松构建多平台应用UI
-
前言最近在项目中尝鲜了MAUI,总体感受下来还是挺不错的,优缺点并存,但是瑕不掩瑜,目前随着.Net版本的迭代升级对它的支持也越来越友好,相信未来可期!感兴趣的朋友欢迎关注。文章中如有不妥的地方,也请...
- webstorm常用的插件
-
1、AtomMaterialIcons推荐原因:这款插件不仅...
- 「智能家居」自动化平台nodered第三方节点dashboard的使用
-
自带节点库讲完了,开始说说第三方节点库dashboard,该库提供另一个可配置的UI页面,可以配置一些表单元素,以及图表。先来看一下别人使用dashboard制作的面板吧,是不是很漂亮。接下来我们一...
- 「炫丽」从0开始做一个WPF+Blazor对话小程序
-
大家好,我是沙漠尽头的狼。...
- MAUI使用Masa blazor组件库
-
上一篇(点击阅读)我们实现了UI在Web端(BlazorServer/Wasm)和客户端(Windows/macOS/Android/iOS)共享,这篇我加上MasaBlazor组件库的引用,并...
你 发表评论:
欢迎- 一周热门
-
-
如何评估预测值与真实值之间的匹配质量
-
如何解决npm安装依赖报错ERESOLVE unable to resolve dependency tree
-
超详细的cmder工具介绍及功能、快捷键说明
-
畅网 N5105 四口 2.5G 小主机安装 WIN10 对比 WIN11 跑分
-
常见面试第三题之Activity的几种启动模式介绍
-
软件推荐丨gocron —— 定时任务管理系统
-
一分钟带你认识了解电信光猫(电信光猫有什么用途)
-
聊聊C++20最大的变革之一 —— Coroutine,看不懂你打我(一)
-
硬核!Rust异步编程方式重大升级:新版Tokio如何提升10倍性能详解
-
Vite 4.0 正式发布(vite版本)
-
- 最近发表
- 标签列表
-
- systemproperties (65)
- npm版本管理 (61)
- localhost:15672 (59)
- materialtheme (86)
- node-ssh (68)
- 图床搭建 (62)
- vue3addeventlistener (60)
- mybatisselectone (78)
- css圆形进度条 (69)
- androidble蓝牙开发 (62)
- android-gif-drawable (60)
- appender-ref (64)
- springbootmockito (68)
- css边框渐变色和圆角 (58)
- gsonfastjson (59)
- 依赖注入的方式 (62)
- cookie跨域共享 (63)
- easyexcel导出图片 (77)
- dp数组 (61)
- js获取兄弟节点 (68)
- sysctl-a (60)
- java知音 (58)
- window.target (62)
- apimodel注解的作用 (60)
- window.onerror (66)