Spring 两大核心思想(一):IoC

Spring 两大核心思想(一):IoC

  • [1. 什么是 IoC](#1. 什么是 IoC)
  • [2. Bean 的存储和获取](#2. Bean 的存储和获取)
    • [2.1 类注解](#2.1 类注解)
    • [2.2 方法注解 @Bean](#2.2 方法注解 @Bean)
    • [2.3 Bean 的命名规则](#2.3 Bean 的命名规则)
    • [2.4 获取 Bean](#2.4 获取 Bean)
  • [3. DI 依赖注入](#3. DI 依赖注入)
    • [3.1 三种注入方法](#3.1 三种注入方法)
    • [3.2 同一类型有多个 Bean 时怎么注入](#3.2 同一类型有多个 Bean 时怎么注入)
  • [4. IoC 的好处](#4. IoC 的好处)

IoC 是 Spring 的两大核心思想之一,而 DI 是 IoC 的具体落地实现,二者是 Spring 所有功能的基石,也是 Java 后端面试的高频核心考点。本文从概念本质出发,详细梳理 IoC 相关知识点,讲解详细,欢迎收藏!

1. 什么是 IoC

IoC:Inversion of Control(控制反转

IoC 是一种思想,将对象的控制权交给 IoC 容器,由 IoC 容器来创建对象,而不是自己去 new 一个对象。使用时,将依赖注入(DI)进来即可,这里的依赖其实就是对象。

简单来说,对象的控制权从程序员代码交给了 IoC 容器,就是控制权实现了反转。

Spring 框架实现了 IoC 思想,所以,Spring 是一个包含了众多方法的 IoC 容器

2. Bean 的存储和获取

被 Spring IoC 管理的对象,叫作 Bean。要想让对象被 Spring 管理,我们要把它们存储到 IoC 容器中。可以通过以下注解实现。

2.1 类注解

我们可以通过五大注解 实现对象的存储,这五大注解是类注解,加在类上:

  1. @Controller:控制层
  2. @Service:业务逻辑层
  3. @Repository:数据访问层
  4. @Component:其他
  5. @Configuration :配置层

这五大注解与程序的应用分层是相呼应的,让程序员看到注解后就能直接了解当前类的用途。根据类所在的层,在类上加上对应的注解就可以实现 Bean 的存储。

@Controller、@Service、@Repository、@Configuration 中都实现了 @Component,它们可以理解为是 @Component 的"子类",所以在一些分层比较模糊的场景,我们不用纠结到底使用哪个注解,它们也可以通用。

2.2 方法注解 @Bean

还有一个方法注解@Bean

@Bean 可以解决两种情况:

  1. 当想要使用的类在外部的包(第三方依赖)中时,我们没法对该类添加注注解
  2. 当一个类中需要创建多个对象时

此时,在对应方法上@Bean 即可。

【注意】@Bean 必须搭配五大注解一起使用。

Bean 的命名规则:被 @Bean 修饰的方法名,就是当前 Bean 的默认名称。

2.3 Bean 的命名规则

开发人员不需要为 Bean 指定名称,Spring 容器会自动为 Bean 生成唯一的名称:

  1. 如果类的名称是大驼峰形式,Bean 的名称是将类名第一个字母小写,即变为小驼峰。(UserCroller --> userController)
  2. 如果类的名称前两个字母都是大写,类名即为 Bean 的名称。(UController --> UController)
  3. 如果是 @Bean 存储的对象,@Bean 修饰的方法名即为 Bean 的名称,也可通过 @Bean("name") 自定义名称。

2.4 获取 Bean

有三种方式:根据名称获取、根据类型获取、根据名称 + 类型获取

首先从需要调用 ApplicationContext 提供的 getBean 方法,从 Spring 上下文中获取对象。假设我们要获取的对象类型为 UserController,代码如下:

java 复制代码
@SpringBootApplication
public class SpringApplication {
	public static void main(String[] args) {
		// 获取 Spring 上下文
		ApplicationContext context = 
		SpringApplication
		.run(SpringIocDemoApplication.class, args);
		
		// 从 Spring 上下文中获取对象
		// 1. 根据名称获取
		UserController userController1 = 
		(UserController) context.getBean("userController");
		
		// 2. 根据类型获取
		UserController userController2 = 
		(UserController) context.getBean(UserController.class);
		
		// 3. 根据名称 + 类型处理
		UserController userController3 = 
		(UserController) context.getBean("userController", UserController.class);
	}
}

在上面的代码中,我们用不同方式三次获取了 Bean,打印他们的地址可以发现,它们是同一个对象。也就是说,Spring 创建的对象是单例的。因为 Spring 默认使用单例池,只创建一个对象,全局复用。

此外,ApplicationContext 获取 Bean 对象的功能,是其父类 BeanFactory 提供的功能。ApplicationContext 和 BeanFactory 的主要区别有:

  1. 继承关系方面,ApplicationContext 继承自 BeanFactory,BeanFactory 提供了基础的访问容器的功能,而 ApplicationContext 除了继承了 BeanFactory 的所有功能之外,还添加了对国际化支持、资源访问支持以及事件传播等方面的支持。
  2. 性能方面,ApplicationContext 是一次性加载并初始化所有 Bean 对象,BeanFactory 是用到了才去加载,因此 BeanFactory 更加轻量。

3. DI 依赖注入

DI (Dependency Injection) 依赖注入:是 IoC 的具体实现方式

DI 是一个过程,是指 IoC 容器在创建 Bean 时,动态地去提供运行时所依赖的资源,这里的资源就是对象。简单来说,就是容器自动把依赖的 Bean 取出,放到某个类的属性中的过程。

3.1 三种注入方法

关于依赖注入,Spring 给我们提供了 3 种方法:

  1. 属性注入:将 @Autowired 方法加在属性上
    代码示例:将 userService 注入到 UserController 中
java 复制代码
@Controller
class UserController {
	@Autowired
	private UserService userService;
	...
}

优点 :代码简单,使用方便。
缺点:不能注入 final 修饰的对象;只能用于 IoC 容器,非 IoC 容器不可用。

  1. 构造方法注入:在类的构造方法上加上@Autowired
    代码示例:将 userService 注入到 UserController 中
java 复制代码
@Controller
class UserController {
	private UserService userService;
	
	@Autowired
	public UserController(UserService userService) {
		 this.userService = userService;
	}
	...
}

如果构造方法唯一,可以不加 @Autowired,会自动注入;如果有多个构造方法,必须在一个上加上 @Autowired,指定需要使用哪个构造方法。

【注意】使用构造函数必须把无参构造函数显示地添加(不管用不用),否则报错。

优点 :可以修饰 final 修饰的对象;注入对象不会被修改;依赖对象在使用前一定会被初始化,避免了空指针异常(因为是在构造方法中注入的,而构造方法在类加载阶段就执行了);通用性好,此方法是 JDK 支持的,其他框架也适用。
缺点:注入多个对象时,代码繁琐。

  1. Setter 方法注入:在需要注入属性的 Setter 方法上加上 @Autowired
    代码示例:将 userService 注入到 UserController 中
java 复制代码
@Controller
class UserController {
	private UserService userService;
	
	@Autowired
	public void setUserService(UserService userService) {
		 this.userService = userService;
	}
	...
}

优点 :方便在类实例化后,重新对该对象进行配置或修改。
缺点:不能注入 final 修饰的属性;注入的对象可能会被改变,因为 setter 方法可能会被多次调用。

3.2 同一类型有多个 Bean 时怎么注入

当我们使用 @Bean 注解,一个类中将多个 Bean 交给 IoC 容器,这时,同一类型的 Bean 就会有多个。这种情况下 @Autowired 就会报错,原因是非唯一的 Bean。Spring 提供了以下几种解决方案:

  1. @Primary:设置默认优先的 Bean
    在其中一个对象上加上 @Primary,表示是默认要注入的 Bean。
java 复制代码
@Component
public UserInfo {
	@Primary
	@Bean("user1") //对对象进行重命名
	public User user1() {
		...
	}
	@Bean("user2")
	public User user2() {
	...
	}
}
  1. @Qualifier:指定 Bean 的名称
    在 @Qualifier 的属性 value 中,指定要注入的 Bean 的名称。
    【注】@Qualifier 不能单独使用,必须搭配 @Autowired 一起使用。
java 复制代码
public class UserController {
	@Qualifier(value="userService1")
	@Autowired
	private UserService userService;
	...
}
  1. @Resourse:默认按名称注入
    默认根据名称匹配 Bean,在 @Resourse 的 name 属性中指定要注入的 Bean 的名称。
java 复制代码
public class UserController {
	@Resourse(name="userService2")
	private UserService userService;
	...
}

@Autowired 和 @Resourse 的区别:

  1. @Autowired 是 Spring 提供的注解,而 @Resourse 是 JDK 提供的注解。
  2. @Autowired 支持按类型注入,而 @Resourse 默认按名称注入。
  3. 相比于 @Autowired,@Resourse 支持更多属性设置,如 name 属性,根据名称获取 Bean。

4. IoC 的好处

最后总结一下 IoC 的好处:

  1. 资源集中管理:Ioc 容器会帮我们管理一些资源(对象等),我们使用时,只需从 Ioc 容器中去取就可以。
  2. 我们在创建实例的时候不再需要了解其中的细节,无需手动 new 对象,降低了使用资源双方的依赖程度(耦合度)。
  3. 支持单例复用:Spring 默认单例,减少了对象的创建开销,提升性能。
相关推荐
熊文豪2 小时前
标量子查询消除:一次让查询性能提升千倍的优化器手术
数据库·电科金仓
他们叫我阿冠2 小时前
Day4学习--MySQL高级
数据库·学习·mysql
国冶机电安装2 小时前
非标系统控制柜制造:从特殊工况到定制控制的完整解析
数据库·制造
雨辰AI2 小时前
完整版信创微服务国产化架构实战:Nacos+Seata+Redis + 人大金仓(生产可落地)
数据库·redis·微服务·架构·政务
网管NO.12 小时前
SQL 模糊查询 + NULL 空值。LIKE 通配符 % 和_、IS NULL
数据库
Mr. zhihao2 小时前
Redis 内存管理深度解析:过期删除与内存淘汰策略
数据库·redis·缓存
九皇叔叔2 小时前
高斯性能分析【第一天】单表执行计划分析
java·数据库·性能分析·执行计划·gauss
苦逼的猿宝2 小时前
基于springboot的社区团购系统设计(源码+论文)
java·毕业设计·springboot·计算机毕业设计
电魂泡哥2 小时前
RocketMQ Dledger 集群与 Raft 协议
java·rocketmq·java-rocketmq