Spring 04: IOC控制反转 + DI依赖注入
Spring中的IOC
一种思想,两种实现方式
IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入
- 核心业务:(a)对象的创建 (b)依赖的注入
2种实现方式
- 基于xml实现IOC
- 基于注解实现IOC
基于xml的IOC在前3篇Spring博客中简单探讨过了,后面将探讨基于注解的IOC
基于注解的IOC
- DI (Dependency Injection):基于注解的IOC被称为DI,即依赖注入, 是IOC思想的一种具体实现方式
- 根据IOC的核心业务即:(a)对象创建,(b)依赖注入,对注解进行分类研究
注解类型
a. 创建对象的注解
包含:创建任意对象的注解 + 创建三层架构各层对象的注解
@Conponent可以创建任意对象
@Controller:专门用来创建控制器对象(Servlet),这种对象可以用来接收用户的请求,可以返回处理结果给客户端
@Service:专门用来创建业务逻辑层对象,负责向下访问数据访问层,并将处理结果返回给界面层
@Repository:专门用来创建数据访问层对象,负责数据库中的CRUD操作
b. 依赖注入的注解
- 包含:负责简单类型注入的注解 + 负责引用类型注入的注解
简单类型的注入
- @Value:用来给简单类型(8 + 1)注入值
引用类型的注入
- @Autowired:使用类型注入值,从整个Bean工厂中搜索同源类型的对象进行注入
- 同源类型可以是如下3种情况
- 1.被注入的属性类型与待注入的数据类型是完全相同的类型
- 2.被注入的属性(可以作为:父类)类型与待注入的数据(可以作为:子类)类型可以是父子类关系
- 3.被注入的属性(可以作为:接口)类型与待注入的数据(可以作为:实现类)类型是可以是接口和实现类的关系
- 同源类型可以是如下3种情况
- @Autowired + @Qualifier:使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入
注意
考虑到演示代码的复用性,减少代码冗余,并保证演示的清晰性,放在一起演示的代码:共用实体类 + 共用applicationContext.xml + 共用一个测试类。
不在一起演示的,另建一个新包并重新创建以上内容。
对实体类或配置文件的修改顺序,遵循博文的演示顺序。
- @Conponent + @Value 放在一起演示
- @Autowired:同源类型注入之完全相同类型 + 对应的(@Autowired + @Qualifier)名称注入 放在一起演示
- @Autowired:同源类型注入之父子类型 + 对应的(@Autowired + @Qualifier)名称注入 放在一起演示
- @Controller + @Service + @Repository 先不演示,在改造之前博客(Spring博客集中的Spring02)中的三层项目架构时再演示
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - -- - -
@Conponent注解
实体类
- Student实体类,并对实体类添加@Component注解
package com.example.s01;
import org.springframework.stereotype.Component;
@Component
public class Student {
private String name;
private int age;
public Student() {
System.out.println("Student无参构造方法被执行,实例对象被创建....");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 添加包扫描 -->
<context:component-scan base-package="com.example.s01"/>
</beans>
测试1
创建实体类对象的时机:和基于xml的IOC一样,当创建Spring容器时,创建实体类对象
具体流程:创建Spring容器时,读取Spring核心配置文件:applicationContext.xml,进行包扫描,对于被扫描到的包,如果包中的实体类添加了@Component注解,则创建实体类对象
package com.example.test;
import com.example.s01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestComponent {
//测试Component注解
@Test
public void testComponent(){
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
//获取容器中的bean对象
Student student = (Student) ac.getBean("student");
System.out.println(student);
}
}
测试输出1
Student无参构造方法被执行,实例对象被创建....
Student{name='null', age=0}
Process finished with exit code 0
注意
实体类
- 修改注解为@Component("stu")
@Component("stu")
public class Student {
//...
}
测试2
- 在获取Spring容器中的对象时根据指定的名称:"stu"来获取。注解未做特殊指定时,则遵循用类名的驼峰命名法来取
public class TestComponent {
//测试Component注解
@Test
public void testComponent(){
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
//获取容器中的bean对象
Student student = (Student) ac.getBean("stu");
System.out.println(student);
}
}
测试输出2
- 与测试输出1完全相同,不再赘述
@value注解
实体类
- 为Student实体类的简单类型的属性添加@Value注解
@Component("stu")
public class Student {
@Value("荷包蛋")
private String name;
@Value("20")
private int age;
//....
}
测试3
- 和测试1完全相同,不再赘述
测试输出3
Student无参构造方法被执行,实例对象被创建....
Student{name='荷包蛋', age=20}
Process finished with exit code 0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - -- - -
@Autowired注解
同源类型注入3种情况之一:完全相同的类型的注入
实体类
- 在新的包下构建的两个实体类:School实体类 + Student实体类
- School实体类
package com.example.s02;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class School {
@Value("nefu")
private String name;
@Value("哈尔滨")
private String address;
public School() {
System.out.println("School无参构造方法执行,实例对象被创建....");
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
- Student实体类新增对School实例对象的引用,其他内容和之前的Student类相同
package com.example.s02;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student {
//.....
@Autowired
private School school;
//.....
}
applicationContext.xml
- 添加包扫描,头文件不再赘述
<!-- 添加包扫描 -->
<context:component-scan base-package="com.example.s02"/>
测试4
package com.example.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAutowired {
//测试同源注入:完全相同的类型
@Test
public void testAutowired(){
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
//从容器中获取Student实例对象
System.out.println("学生对象: " + ac.getBean("student"));
}
}
测试输出4
School无参构造方法执行,实例对象被创建....
Student无参构造方法被执行,实例对象被创建....
学生对象: Student{name='荷包蛋', age=20, school=School{name='nefu', address='哈尔滨'}}
Process finished with exit code 0
对应的名称注入
实体类
- School实体类:将School的@Component注解改为@Component("theSchool")
@Component("theSchool")
public class School {
//.....
}
- Student实体类:新增@Qualifier注解,并必须在其后指定Bean工厂中已经注册的实体类对象的名称(类名的驼峰命名或自定义名称)
@Component
public class Student {
//.....
@Autowired
@Qualifier("theSchool")
private School school;
//.....
}
测试5和测试输出5
- 分别和测试4和测试输出4完全相同,不再赘述
注意
- 只使用@Qualifier注解标签且后面跟的Bean工厂中注册的实体类对象的名称正确时,无法完成依赖名称注册,用名称进行注入时,这两个注解标签都要出现
实体类
- Student实体类
@Component
public class Student {
//.....
@Qualifier("theSchool")
private School school;
//.....
}
测试6
- 与测试4完全相同,不再赘述
测试输出6
- 没有报错,但是根据名称进行依赖注入的操作并没有真正将引用类型的数据注入到Student实例中,引用类型school为null
School无参构造方法执行,实例对象被创建....
Student无参构造方法被执行,实例对象被创建....
学生对象: Student{name='荷包蛋', age=20, school=null}
Process finished with exit code 0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - -- - -
@Autowired注解
同源类型注入3种情况之二:父子类型的注入
实体类
构建一个新的实体类包,含有3个实体类:SubSchool,School,Student
新增实体类SubSchool,为School类的子类
package com.example.s03;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SubSchool extends School{
@Value("nefu附小")
private String name;
@Value("香坊区")
private String address;
@Override
public String toString() {
return "SubSchool{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
public SubSchool() {
System.out.println("SubSchool无参构造方法被执行,实例对象被创建....");
}
}
- Student实体类内容不变
package com.example.s03;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student {
//......
@Autowired
private School school;
//......
}
- School实体类内容不变
package com.example.s03;
import org.springframework.stereotype.Component;
@Component
public class SubSchool extends School{
//......
}
applicationContext.xml
- 添加包扫描
<!-- 添加包扫描 -->
<context:component-scan base-package="com.example.s03"/>
测试7
package com.example.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAutowiredExtend {
//测试同源类型注入:父子类型
@Test
public void testAutowiredExtend(){
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("s03/applicationContext.xml");
//从容器中获取Student实例
System.out.printf("Student实例: " + ac.getBean("student"));
}
}
测试输出7
- 为什么"School无参构造方法执行,实例对象被创建...."被输出2次?
- 原因:一次是构建School对象时,一次在构建SubSchool对象时(子类构造方法中调用父类无参构造方法)
School无参构造方法执行,实例对象被创建....
Student无参构造方法被执行,实例对象被创建....
School无参构造方法执行,实例对象被创建....
SubSchool无参构造方法被执行,实例对象被创建....
Student实例: Student{name='荷包蛋', age=20, school=School{name='nefu', address='哈尔滨'}}
Process finished with exit code 0
注意
- 为什么SubSchool和School实体类对象都被注册了,在上述测试中,只是School的实体类对象被注入Student对象?
- 原因:在同源类型的注入中,若进行父子类型的依赖注入,不是按照名称注入时,会按照注册的实体类对象的名称二次选择
- 二次选择的原则:注册的实体类对象的名称和待注入的目标属性名称相同的,优先被选择为注入数据
实体类
- School修改为
@Component("schoolFu")
public class School {
//......
}
- SubSchool修改为
@Component("school")
public class SubSchool extends School{
//......
}
测试8
- 测试7完全相同,不再赘述
测试输出8
- 此时被注入到Student实例对象中的是SubSchool实例对象
School无参构造方法执行,实例对象被创建....
Student无参构造方法被执行,实例对象被创建....
School无参构造方法执行,实例对象被创建....
SubSchool无参构造方法被执行,实例对象被创建....
Student实例: Student{name='荷包蛋', age=20, school=SubSchool{name='nefu附小', address='香坊区'}}
Process finished with exit code 0
对应的名称注入
实体类
- 为Student实体类新增注解@Qualifier("schoolFu")
@Component
public class Student {
@Autowired
@Qualifier("schoolFu")
private School school;
//......
}
测试9
- 和测试7完全相同,不再赘述
测试输出9
- 此时被注入到Student实例对象中的是School实例对象,因为@Qualifier("schoolFu")指定的注入数据和School实体类的注册类型相同,根据指定名称完成注入
School无参构造方法执行,实例对象被创建....
Student无参构造方法被执行,实例对象被创建....
School无参构造方法执行,实例对象被创建....
SubSchool无参构造方法被执行,实例对象被创建....
Student实例: Student{name='荷包蛋', age=20, school=School{name='nefu', address='哈尔滨'}}
Process finished with exit code 0
Spring 04: IOC控制反转 + DI依赖注入的更多相关文章
- Spring的IOC控制反转和依赖注入-重点-spring核心之一
IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...
- laravel服务容器(IOC控制反转,DI依赖注入),服务提供者,门脸模式
laravel的核心思想: 服务容器: 容器:就是装东西的,laravel就是一个个的对象 放入:叫绑定 拿出:解析 使用容器的目的:这里面讲到的是IOC控制反转,主要是靠第三方来处理具体依赖关系的解 ...
- Spring专题2: DI,IOC 控制反转和依赖注入
合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...
- 轻松了解Spring中的控制反转和依赖注入(二)
紧接上一篇文章<轻松了解Spring中的控制反转和依赖注入>讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作. 类 ...
- Spring中的控制反转和依赖注入
Spring中的控制反转和依赖注入 原文链接:https://www.cnblogs.com/xxzhuang/p/5948902.html 我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达 ...
- [转载]Spring下IOC容器和DI(依赖注入) @Bean及@Autowired
Spring下IOC容器和DI(依赖注入) @Bean及@Autowired自动装配 bean是什么 bean在spring中可以理解为一个对象.理解这个对象需要换一种角度,即可将spring看做一门 ...
- 轻松了解Spring中的控制反转和依赖注入(一)
我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级Ja ...
- Java Web实现IOC控制反转之依赖注入
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控制反转一般分为两种类型,依赖注入 ...
- Spring框架之控制反转和依赖注入
学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明控制反转和依赖注入. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权 ...
随机推荐
- 联发科 (MTK) sensor bring up
MT6768平台 1.添加驱动文件 2.添加硬件配置支持 3.添加硬件配置 4.添加编译配置 5.分配空间(非必要,当代码量超过当前空间大小时将会报错,根据报错log改大小即可.) 6.兼容配置 7. ...
- [杂项]从子域名接管到Subtaker
子域名接管安全性分析及落地化 能说只是为了学Go嘛?33333 Github项目直通车 简介 子域名接管,主要原因归结于失效dns记录未删除. 譬如,一条指向test.sec.com的CNAME记录未 ...
- 一文带你搞懂 SSR
欲语还休,欲语还休,却道天凉好个秋 ---- <丑奴儿·书博山道中壁>辛弃疾 什么是 SSR ShadowsocksR?阴阳师?FGO? Server-side rendering (SS ...
- 搭建个人博客,Docsify+Github webhook+JGit解决方案
一开始博客使用的 Halo,发现问题比较多啊,时不时的莫名其妙主题各种报错,有时候还要升级,麻烦的要死,于是就想弄简单点. 这两天抽空反复倒腾了一遍,不小心还把镜像给尼玛删了,发的文章都没了,痛定思痛 ...
- 【Redis】集群故障转移
集群故障转移 节点下线 在集群定时任务clusterCron中,会遍历集群中的节点,对每个节点进行检查,判断节点是否下线.与节点下线相关的状态有两个,分别为CLUSTER_NODE_PFAIL和CLU ...
- 聊聊 RPA 方向的规划:简单有价值的事情长期坚持做
「简单有价值的事情长期坚持做」 这是成功最简单,但也最难学的秘诀.不经过训练,人很难意识到时间复利的威力. 仙剑奇侠传的「十里坡剑神」和金庸群侠传的「十级野球拳」,就是简单的事情持之以恒反复做,最后就 ...
- ShardingSphere-proxy-5.0.0建立mysql读写分离的连接(六)
一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...
- python:**也不过如此嘛,这不也被我采集下来啦~
前言 嗨喽!大家好呀,这里是小熊猫 知识点: 基本流程 fiddler抓包 开发环境: python 3.8 运行代码 pycharm 2021.2 辅助敲代码 requests 第三方模块 如果安装 ...
- 到点了开始网抑云(悲)但是用python(整活)
写在前面的一点网抑云: 爱情不是随便许诺好了不想再说了没错 是我那么多的冷漠 让你感觉到无比的寂寞不过 一个女人的不仅仅渴望得到的一个承诺我害怕欺骗也害怕寂寞更害怕我的心会渐渐地凋落爱情不是随便许诺好 ...
- NC15975 小C的记事本
NC15975 小C的记事本 题目 题目描述 小C最近学会了java小程序的开发,他很开心,于是想做一个简单的记事本程序练练手. 他希望他的记事本包含以下功能: 1.append(str),向记事本插 ...