Spring IoC的一些知识点
在日常开发中,接触得比较多的算是Spring生态了,Spring Ioc是Spring Framework重要的组成部分,下面整理了一些Spring Ioc的知识点。
1. 什么是IoC
IoC(Inversion of Control),翻译过来就是控制反转,它是一种设计思想,这个设计思想说明了 “由谁控制,控制了什么,为何反转,反转了什么”。
- 控制
Java通过new来创建对象,而IoC有专门的容器控制对象创建,即Ioc控制了对象的创建(实际还包含更多的外部资源,如配置等)
- 反转
使用IoC容器管理对象时,对象持有的依赖对象是由IoC注入的,即对象只是被动的接受了依赖对象,也即依赖对象的获取反转了。
如:需要对 UserService 这个类进行单元测试,其中 UserService 还依赖 UserAddressService:
public class UserServiceTests {
public void test() {
// ...
}
}
public class UserService {
public UserAddressService userAddressService;
}
public class UserAddressService {
}
那么在没有IoC的情况下,UserServiceTests.test() 测试流程为:
- 创建UserService对象
- 创建UserAddressService对象
- 将UserAddressService对象注入到UserService中
而在使用IoC的情况下,UserServiceTests.test() 测试流程则为:
- UserServiceTests获取UserService对象
- IoC控制:
- 创建UserService对象
- 创建UserAddressService对象
- IoC反转:
- 将UserAddressService对象注入到UserService中
可以用“别找我们,我们找你”来总结IoC的思想,即由IoC容器帮对象找相应的依赖对象注入,而不是由对象主动去找依赖对象注入。
2. IoC的另一个角度
DI(Dependency Injection),翻译过来就是依赖注入,可以理解为从另一个角度的IoC思想,这里的依赖注入是指:
- 依赖
应用程序依赖了IoC容器,应用程序需要IoC来提供对象需要的资源(包括依赖对象,配置等)
- 注入
IoC容器注入应用程序需要的某个对象以及应用程序依赖的对象(实际还包含更多的外部资源,如配置等)。
3. BeanFactory和ApplicationContext
Spring的 org.springframework.beans 和 org.springframework.context 包是IoC的基础,其中:
- BeanFactory 接口提供了IoC容器最基本功能
- ApplicationContext 是BeanFactory的子接口,提供了更多的功能,包含SpringAOP,i18n国际化,事件,以及不同层次的context实现(如WebApplicationContext)
下面举例两者构建IoC容器的方式,这里使用XML直接开启注解的方式声明Bean:
<?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:component-scan base-package="io.michong2022.spring.bean.demo"/>
</beans>
上面spring.xml的 <context:component-scan> 配置会启用 <context:annotation-config/>,而<context:annotation-config/> 会注册下面的Bean:
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- EventListenerMethodProcessor
在 io.michong2022.spring.bean.demo 包中声明了两个Bean(UserService, UserAddressService):
/**
* @author 米虫2022
*/
@Service
public class UserService {
@Autowired
private UserAddressService userAddressService;
}
BeanFactory构建IoC容器:
@Test
public void testBeanFactory() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring.xml");
beanFactory.addBeanPostProcessor(beanFactory.getBean(AutowiredAnnotationBeanPostProcessor.class));
UserService userService = beanFactory.getBean(UserService.class);
Assertions.assertNotNull(userService);
}
ApplicationContext构建IoC容器:
@Test
public void testApplicationContext() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = context.getBean(UserService.class);
Assertions.assertNotNull(userService);
}
这里会加载7个Bean,2个自定义的,以及5个context:annotation-config注册的:
Loaded 7 bean definitions from class path resource [spring.xml]
默认情况下自定义的Bean是单例的:
Creating shared instance of singleton bean 'userService'
4. IoC Bean的构建
针对Ioc Bean的构建,BeanFactory和ApplicationContext的行为有些许不同,以ClassPathXmlApplicationContext为例,它会在refresh()的时候,
通过 finishBeanFactoryInitialization(beanFactory) 将Bean都初始化,而BeanFactory则在getBean()的时候才真正的创建Bean。
以单例Bean的构建为例,Bean构建流程如下:

其中:Bean单例对象由DefaultSingletonBeanRegistry的singletonObjects管理,DefaultListableBeanFactory的父类继承了DefaultSingletonBeanRegistry。
5. Bean的循环依赖
Bean的循环依赖,指Bean直接或间接的依赖构成了环,如:A -> B, B -> C, C -> A,下面是一个例子:
@Service
public class UserService {
// UserService依赖UserAddressService
@Autowired
private UserAddressService userAddressService;
}
@Service
public class UserAddressService {
// UserAddressService也依赖UserService
@Autowired
private UserService userService;
}
- Spring解决了单例Bean的Setter的循环依赖,对于构造函数循环依赖和非单例的Bean循环依赖会抛出异常。
DefaultSingletonBeanRegistry中有一些容器管理着Bean创建过程的状态:
- singletonObjects: 创建成功的单例Bean对象缓存池
- singletonFactories: 提前暴露的Bean对象工厂缓存池,工厂提供Bean完成了实例化,并没有完成依赖注入
- earlySingletonObjects: 提前暴露的Bean对象缓存池
- registeredSingletons: 已经注册的Bean名称
- singletonsCurrentlyInCreation: 正在创建的Bean
Bean的依赖注入流程如下:

在流程图的黄色部分,Spring会根据下面的顺序获取依赖的Bean对象:
- 尝试从
singletonObjects获取已经成功创建的Bean - 如果(1)获取不到,判断需要获取的Bean是否处理构建中,即是否登记在
singletonsCurrentlyInCreation中 - 如果需要获取的Bean登记在
singletonsCurrentlyInCreation中,那么尝试从earlySingletonObjects获取Bean - 如果(3)获取不到,那么从singletonFactories获取Bean的工厂,获取Bean对象,将Bean对象放入
earlySingletonObjects中,且将工厂从singletonFactories移除
注意:从Bean工厂获取的Bean,并不是完成注入的Bean对象。
按上面的流程,例子中,测试用例 beanFactory.getBean(UserService.class); 获取Bean执行的流程如下:
- 构建UserService,登记
singletonsCurrentlyInCreation和singletonFactories - 为UserService注入UserAddressService对象(UserAddressService此时没有创建,暂停执行步骤3.)
- 构建UserAddressService,登记
singletonsCurrentlyInCreation和singletonFactories - 为UserAddressService注入UserService对象,从singletonFactories获取UserService工厂,从而获取UserService对象(UserAddressService属性为空)放入earlySingletonObjects中,并将UserService的工厂从singletonFactories移除,将获取的UserService对象注入
- UserAddressService对象构建完成,将放入singletonObjects中,并将UserAddressService工厂从singletonFactories移除
- 完成Bean的构建流程,在
afterSingletonCreation中从singletonsCurrentlyInCreation移除 - 继续步骤2.
下面是流程图黄色部分的源码实现:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
不支持循环依赖的Bean构建:
- 构造函数注入循环依赖,抛
org.springframework.beans.factory.UnsatisfiedDependencyException异常 - 非单例对象注入循环依赖,抛
org.springframework.beans.factory.UnsatisfiedDependencyException异常
7. 自动装配
自动装配是指Spring自动的注入对象,Spring可以通过注解@Autowired来完成自动装配,默认情况下,@Autowired优先通过属性名称注入,即byName注入优先。
下面这个例子,自动装配会发生异常:
public interface Address {
}
@Service("provinceAddress")
public class ProvinceAddress implements Address {
}
@Service("cityAddress")
public class CityAddress implements Address {
}
@Service
public class UserService {
@Autowired
private Address address;
}
出现如下异常:
No qualifying bean of type 'io.michong2022.spring.bean.demo.Address' available: expected single matching bean but found 2: cityAddress,provinceAddress
可以通过如下方式解决:
- 修改变量名
address为想要注入的Bean名称,如cityAddress或provinceAddress - 为CityAddress或ProvinceAddress加上
@Primary注解,标注为默认的注入对象 - 给变量
address加上@Qualifier("cityAddress")注解,指定注入的对象
8. Bean的生命周期
Spring Bean的声明周期如下:
- 构造函数
- BeanPostProcessor.postProcessBeforeInitialization()
- @PostConstruct (CommonAnnotationBeanPostProcessor)
- afterPropertiesSet (InitializingBean)
- init-method (Bean配置指定)
- BeanPostProcessor.postProcessAfterInitialization()
- @PreDestroy (CommonAnnotationBeanPostProcessor)
- destroy (DisposableBean)
- destroy-method (Bean配置指定)
Spring IoC的一些知识点的更多相关文章
- Spring IOC知识点一网打尽!
前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总 ...
- spring中的IOC/DI的知识点
IOC(Inverse of control):控制反转;其实就是一个装对象的容器,以前我们在controller中调用service中的方法,都要先new 一个service对象,这样不符合模式设计 ...
- spring原理案例-基本项目搭建 03 创建工程运行测试 spring ioc原理实例示例
下面开始项目的搭建 使用 Java EE - Eclipse 新建一 Dynamic Web Project Target Runtime 选 Apache Tomcat 7.0(不要选 Apache ...
- 案例学编程系列:案例认识 Spring IOC
本文spring libs 地址:https://github.com/yizhiamumu/springlibs Spring 能帮我们做什么 ①.Spring 能帮我们根据配置文件创建及组装对象之 ...
- Spring IOC 容器源码分析
声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...
- 关于Spring IOC (DI-依赖注入)需要知道的一切
关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 <Spring入门经典>这本书无论对于初学者或者有 ...
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
- 关于Spring IOC (DI-依赖注入)
<Spring入门经典>这本书无论对于初学者或者有经验的工程师还是很值一看的,最近花了点时间回顾了Spring的内容,在此顺带记录一下,本篇主要与spring IOC相关 ,这篇博文适合初 ...
- Spring IOC 巨多 非常 有用
关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 <Spring入门经典>这本书无论对于初学者或者有 ...
- Spring IOC 容器源码分析(转)
原文地址 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢 ...
随机推荐
- POJ2823 滑动窗口 (单调队列)
来学习一下单调队列: 他只可以从队尾入队,但可以从队尾或队首出队,来维护队列的单调性.单调队列有两种单调性:元素的值单调和元素的下标单调. 单调队列可以用来优化DP.状态转移方程形如dp[i]=min ...
- 通过刷题HTML遇到的问题
通过刷题HTML遇到的问题 1.有关选择器的权重问题 1.通配符选择器和继承:权重为0, 2.标签选择器:权重为0001 3.类选择器:权重为0010 4.id选择器:权重为0100 5.行内样式:权 ...
- servlet过滤器--使用过滤器统计网站访问人数的计数(注解形式)
文章目录 1.什么是过滤器? 2.过滤器核心对象 3.过滤器创建和配置 4.举例子 1.什么是过滤器? 主要用于对客户端的请求进行过滤处理,再将经过过滤后的请求转交给下一个资源. 2.过滤器核心对象 ...
- 某Hi3516EV300摄像头折腾笔记
最近因工作需要买了某款HI3516DV300开发板,但是价格死贵,于是在国内某著名电商网站上瞎逛,很巧发现一家店铺买摄像头模组,主控HI3516EV300,cmos是IMX335,价格不到200元,然 ...
- 日志处理logging
前言 什么是日志?有什么作用?日志是跟踪软件运行时所发生的事件的一种方法,简单来说它可以记录某时某刻运行了什么代码,当出现问题时可以方便我们进行定位. 由python内置了一个logging模块,用户 ...
- dp优化 | 各种dp优化方式例题精选
前言 本文选题都较为基础,仅用于展示优化方式,如果是要找题单而不是看基础概念,请忽略本文. 本文包含一些常见的dp优化("√"表示下文会进行展示,没"√"表示暂 ...
- LcdTools如何导出内置画面为bmp图片
运行LcdTools,先设置好图片所需分辨率参数,点击"画面设置"栏,修改下图所示参数 点击"画面设置"栏,在"画面资源"栏找到需要导出的画 ...
- 18.-cookies和session
一.会话定义 从打开浏览器访问一个网站,到关闭浏览器结束此次访问,称之为一次绘画 HTTP协议是无状态的,导致绘画状态难以保持 Cookies和session就是为了保持会话状态而诞生的两个存储技术 ...
- 如何使用ffmpeg缩小视频的大小?
如何使用ffmpeg缩小视频的大小? 看到这个答案.为方便起见,在下面引用: 通过将1 GB除以以秒为单位的视频长度来计算所需的比特率.因此,对于长度为16:40(1000秒)的视频,请使用10000 ...
- pod(七):静态pod
目录 一.系统环境 二.前言 三.静态pod 3.1 何为静态pod 3.2 创建静态pod 3.2.1 使用--pod-manifest-path指定静态pod目录 3.2.2 静态pod默认目录/ ...