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.被注入的属性(可以作为:接口)类型与待注入的数据(可以作为:实现类)类型是可以是接口和实现类的关系
  • @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依赖注入的更多相关文章

  1. Spring的IOC控制反转和依赖注入-重点-spring核心之一

    IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...

  2. laravel服务容器(IOC控制反转,DI依赖注入),服务提供者,门脸模式

    laravel的核心思想: 服务容器: 容器:就是装东西的,laravel就是一个个的对象 放入:叫绑定 拿出:解析 使用容器的目的:这里面讲到的是IOC控制反转,主要是靠第三方来处理具体依赖关系的解 ...

  3. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  4. 轻松了解Spring中的控制反转和依赖注入(二)

    紧接上一篇文章<轻松了解Spring中的控制反转和依赖注入>讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作. 类 ...

  5. Spring中的控制反转和依赖注入

    Spring中的控制反转和依赖注入 原文链接:https://www.cnblogs.com/xxzhuang/p/5948902.html 我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达 ...

  6. [转载]Spring下IOC容器和DI(依赖注入) @Bean及@Autowired

    Spring下IOC容器和DI(依赖注入) @Bean及@Autowired自动装配 bean是什么 bean在spring中可以理解为一个对象.理解这个对象需要换一种角度,即可将spring看做一门 ...

  7. 轻松了解Spring中的控制反转和依赖注入(一)

    我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级Ja ...

  8. Java Web实现IOC控制反转之依赖注入

    控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控制反转一般分为两种类型,依赖注入 ...

  9. Spring框架之控制反转和依赖注入

    学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明控制反转和依赖注入. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权 ...

随机推荐

  1. SmartIDE v0.1.18 已经发布 - 助力阿里国产IDE OpenSumi 插件安装提速10倍、Dapr和Jupyter支持、CLI k8s支持

    SmartIDE v0.1.18 (cli build 3538) 已经发布,在过去的Sprint 18中,我们集中精力推进对 k8s 远程工作区 的支持,同时继续扩展SmartIDE对不同技术栈的支 ...

  2. Dubbo3 源码系列 Dubbo“纠葛”(入门篇)

    日期 更新说明 2022年5月28日 spring xml部分解读 2022年6月3日 spring annotation部分解读 人生不相见, 动如参与商. 今夕复何夕, 共此灯烛光. 少壮能几时, ...

  3. 总结 到 GDOI 2021 这个阶段

    截止本蒟蒻第一次体验省选(虽然是普及组) 本蒟蒻已经有了许多收获,却也有很多不足 优点 对一些学过的知识掌握还行 没了 缺点 会却做不出来 有一些题不难,却想不到正解 如 Day2 T1 ,就是移一下 ...

  4. SAP Web Dynpro - 教程

    SAP Web Dynpro是一种标准的SAP UI技术,用于使用图形工具和与ABAP工作台集成的开发环境来开发Web应用程序. 图形工具的使用减少了实施工作,并有助于维护ABAP工作台中的组件. 本 ...

  5. SE37 绕过权限检查 ALINK_CALL_TRANSACTION

  6. rhel挂载本地光盘为yum源

    挂载光盘 mount /dev/sr0 /mnt/cdrom mkdir /mnt/cdrom 临时挂载 mount /dev/sr0 /mnt/cdrom 永久挂载光盘 mount -a 执行挂载 ...

  7. MySql中数据表增加字段很慢怎么办

    正确的做法是这样,对于数据量很大的表,需要添加所有或者修改字段的做法是如下: 1.先创建一张一样的表 create table new_tb like tb_old; 2.修改创建表的字段 alter ...

  8. NC21181 重返小学

    NC21181 重返小学 题目 题目描述 ​ 时光依旧,岁月匆匆.转眼间,曾经的少年郭嘉烜已经长大成人,考上了一所优秀的大学--兰州大学.在经历了一年来自牛顿.莱布尼茨.拉普拉斯的精神洗礼后,他终于决 ...

  9. Cayley 定理与扩展 Cayley 定理

    Cayley 定理 节点个数为 \(n\) 的无根标号树的个数为 \(n^{n−2}\) . 这个结论在很多计数类题目中出现,要证明它首先需要了解 \(\text{Prufer}\) 序列的相关内容. ...

  10. P1189 SEARCH—搜索

    将这题加进来的原因 因为他的优化令人眼前一新! 题目传送门() 相似的题目之 血色先锋队 ↑这一题也要用到标记数组 优化!!! 对于一个位置, 如果他在同样的深度再一次被访问,那他接下来所走的路径,所 ...