Spring之IoC源码分析及设计思想(一)——BeanFactory
关于Spring的IOC
Spring 是一个开源的 Java 平台,它提供了一种简化应用程序开发的框架。它是一个分层的框架,包括两个主要的内核:控制反转(IOC)和面向切面编程(AOP)。IOC 允许应用程序将组件之间的依赖关系交给框架来管理,从而实现松耦合;而 AOP 则允许应用程序以声明式的方式实现横切关注点,如日志、事务、安全等。
Spring 的 IOC(Inversion of Control)是其核心特性之一,它允许应用程序将组件之间的依赖关系交给框架来管理,从而实现松耦合。 在传统的程序设计中,对象之间的依赖关系是由程序代码直接控制的,而在 IOC 的思想下,对象之间的依赖关系是由 Spring 容器控制的,程序代码只需要声明依赖关系,而不需要直接创建或管理对象。换言之,当我们通过配置声明好对象之间的依赖后,Spring容器会根据我们的配置帮我们创建对象实例并完成对象中各个成员变量的装配,而不需要我们手动创建或者查找各个对象实例来进行组装。我们可以认为IOC与JNDI相反——后者从容器中主动查找依赖(所需要的对象),而前者在容器初始化某个对象时不等对象请求就主动将依赖传递给它。
IOC的底层采用了工厂模式,所有的Bean(对象实例)都将由BeanFactory也就是Bean工厂来完成实例化,且实例化后理论上都需要被注册到容器中,由容器负责Bean的生命周期的管理(Bean的创建、依赖装配、初始化、销毁)。 开发者只需要按照Spring约定好的方式提供Bean的定义信息(主要以XML配置和注解配置为主,有时候也可以在运行期间通过某些方式实时提供)即可,Bean工厂会根据这些Bean定义来完成Bean的生成。因此,对Bean工厂的认识与理解正是认识SpringIOC的关键(Bean工厂是SpringIOC的逻辑实现)。
从片面的角度上来说,我们可以认为Spring就是一个针对Bean生命周期进行管理的容器。
关于BeanFactory
前面提到Bean工厂是SpringIOC的逻辑实现,因此正确理解Bean工厂正是认识SpringIOC的关键(这里的Bean工厂并不指的是BeanFactory接口,而是包含BeanFactory接口在内的一整套类与接口的实现)。下图是Bean工厂的实现类图:
从整个类图来看,我们可以发现所有的接口与类最终汇聚到了DefaultListableBeanFactory上了。DefaultListableBeanFactory这个类包含了SpringIOC完整的逻辑实现,是Spring默认的BeanFactory实现。事实上,在我们使用Spring时最熟悉的ClassPathXmlApplicationContext与FileSystemXmlApplicationContext两个类都是通过DefaultListableBeanFactory来实现SpringIOC的功能。具体源码可见下图:
在图中我们可以看到ApplicationContext的getBean逻辑正是调用了DefaultListableBeanFactory来实现的Bean的获取。其中AbstractApplicationContext正是前面提到的ClassPathXmlApplicationContext与FileSystemXmlApplicationContext的高层父类。
因此,我们对于SpringIOC的源码和分析只需要局限于DefaultListableBeanFactory即可。DefaultListableBeanFactory已经封装了SpringIOC中对Bean操作的完整逻辑。从前面第一张图(Bean工厂的实现类图)可以看到,在DefaultListableBeanFactory之上有着大量的接口和类,组成了非常复杂的类继承结构。但是其中最顶层的接口就只有BeanFactory、AliasRegistry以及SingletonBeanRegistry。三者分别赋予了Bean工厂不同的能力。而从DefaultListableBeanFactory的类名来看,也可以发现Spring对它的定义就是一个BeanFactory。本文主要讨论BeanFactory接口在SpringIOC设计中所承担的职责与角色,而不着重于类图中其他类与接口的能力。
BeanFactory接口
BeanFactory接口是Spring定义的顶层接口,被定义为是Bean容器的客户端视图。即我们对Bean容器中Bean的获取可以通过BeanFactory接口来实现,而不需要关心其获取逻辑。换句话说,BeanFactory接口赋予了Bean容器向外提供Bean的能力。下图是BeanFactory接口中定义的方法列表:
从图中看,我们能够发现BeanFactory接口中差不多有近一半的方法都是以getBean为名通过Bean名称、Bean类型去获取符合条件的Bean实例。至于剩下的基本也都是与Bean及其特性相关的操作,比如是否包含Bean、判断Bean是单例Bean还是原型Bean、类型是否匹配、获取Bean的类型和别名等。
因此,我们也能够确认BeanFactory接口确实与之前所说一致,是一个被设计用来访问Bean容器中Bean实例的客户端视图,定义了实现该接口的类获取Bean的能力。
本章不谈具体的实现逻辑,因为IOC的实现是一个复杂的过程,在不了解Spring设计意图的前提下盲目去讨论其直接实现类AbstractBeanFactory的实现逻辑会容易让人迷茫,因为其中参杂着其他许多类与接口的部分。相信跟着源码debug过的同学深有体会。其中Bean的缓存是由DefaultSingletonRegistry实现的,Bean的构建和装配等逻辑又是由AbstractAutowireCapableBeanFactory类实现的,这些部分的逻辑又被嵌入在获取Bean的逻辑中,所以通过debug去追溯源码执行逻辑的方式并不可取。
接口方法说明
下面提供对BeanFactory中函数的简单说明,可看可不看。因为大概的作用在前面已经点的差不多了,有了解的同学可以不用看这部分。
Object getBean(String name) throws BeansException;
根据name或者alias获取容器中的Bean
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
通过传入的name查找到Bean然后转成requiredType的类型,如果找不到会抛出 NoSuchBeanDefinitionException.
如果转化类型失败会抛出BeanNotOfRequiredTypeException.
Object getBean(String name, Object... args) throws BeansException;
返回一个实例,该实例可以是指定bean的共享或独立的。
允许指定显式构造函数自变量/工厂方法自变量,并覆盖Bean定义中指定的默认自变量(如果有) 。
注意,如果Bean已经被创建了,那么通过这个方式就无法将参数放进去了。
<T> T getBean(Class<T> requiredType) throws BeansException;
根据类型查找Bean,如果找不到Bean会抛出 NoSuchBeanDefinitionException;
如果找到不止一个,则抛出NoUniqueBeanDefinitionException
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
根据type查找Bean,如果该Bean未被实例化,那么可以将传入的参数对Bean进行DI
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
getBeanProvider()方法用于获取指定bean的ObjectProvider。
ObjectProvider是用与延迟构造Bean的,因为正常直接通过getBean来获取Bean会使得容器直接实例化Bean,但有些时候我们可能并不希望这样。ObjectProvider正好可以用来解决这个问题,它使得我们只有调用了ObjectProvider中的getObject方法才会出发Bean的实例化。这个类不需要深究,不影响IOC的源码理解。
boolean containsBean(String name);
容器中是否包含Bean,按照name或者alias进行查找
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
判断当前的Bean是单例还是原型的作用域
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
检查具有给定信息的Bean是否与指定的类型匹配。更具体地说,检查对给定名称的getBean调用是否将返回可分配给指定目标类型的对象。将别名转换回相应的规范bean名称。将询问父工厂是否在该工厂实例中找不到该bean。
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
获取bean对应的class
String[] getAliases(String name);
返回Bean的别名数组
总结
Spring的IOC实际上是由两个部分组成的,以Regisrty结尾的接口赋予了类缓存Bean以及其他需要用到的对象的缓存能力,也就是容器的能力。而本文谈到的BeanFactory及其以BeanFactory结尾的子接口则赋予了实现类访问容器的能力,使得我们可以通过这些接口完成Bean的构建和获取等操作。因此对于IOC的认识需要分为BeanFactory和Registry两个脉络去了解,两条脉络最终在DefaultListableBeanFactory这个类上完成交汇,从而提供真正的IOC功能。
本系列将沿着两条脉络逐步解析,来完成对SpringIOC源码的分析,从而了解Spring的设计思想。
相关文章:

Spring之IoC源码分析及设计思想(一)——BeanFactory
关于Spring的IOC Spring 是一个开源的 Java 平台,它提供了一种简化应用程序开发的框架。它是一个分层的框架,包括两个主要的内核:控制反转(IOC)和面向切面编程(AOP)。IOC 允许应用程序将组件之…...

⛳ 面向对象面试题
面向对象面试题目录 ⛳ 面向对象面试题🚜 一,成员变量,局部变量,类变量存储在内存的什么地方?🐾 1.1,类变量(静态成员变量)📝 1.2,成员变量⭐ 1.3…...
Java中使用Gson操作json数据
Java中使用Gson操作json数据 引入依赖 <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.9.0</version></dependency>Gson工具类 package cn.test.util;import com.google.gso…...

Verilog语法学习——LV10_使用函数实现数据大小端转换
LV10_使用函数实现数据大小端转换 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 在数字芯片设计中,经常把实现特定功能的模块编写成函数&…...

Leetcode-每日一题【剑指 Offer II 009. 乘积小于 K 的子数组】
题目 给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。 示例 1: 输入: nums [10,5,2,6], k 100输出: 8解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。 需要注意的是 [10,5,2]…...
html/javascript-表格的创建和使用
html中表格的创建和使用 一 摘要二 使用html table标签创建表格(在html文件中)三 使用javascript创建表格(在js文件中)四 表格属性的设置:4.1. 右边框的设置:4.2. 只给表格单元格加右边框4.3. 动态设置右边…...
[点微]同城原生微信小程序 小程序原生版 1.0.7(tom_xiaofenlei)
注意!!!这是点微后出的原生版小程序!!! 依赖点微同城分类主插件、点微同城小程序后端插件!!! 【以下为模块路径】 同城首页 pages/index/index 个人中心 pages/index/my 好店首页 pages/module/tcshop 商城首页 pages/module/tcmall 抢购首页 pages/module/tcqianggou…...

JDBC Some Templates
JDBCTemplate 是Spring对JDBC的封装,使用JDBCTemplate方便实现对数据的操作。 <!-- orm:Object relationship mapping m对象 关系 映射-->引入依赖 <!-- 基于Maven依赖的传递性,导入spring-content依赖即可导入当前所需的所有…...
dubbo启动指定ip不使用docker虚拟网络ip
java -D 配置系统属性 # 启动时加参数 -DDUBBO_IP_TO_REGISTRY 192.168.1.1 该ip为dubbo所在服务器的公网ip即可。 java -jar myDubboRpc-api.jar -DDUBBO_IP_TO_REGISTRY 192.168.1.1 # xjar启动 nohup ./xjar java -DDUBBO_IP_TO_REGISTRY11.22.33.44 -XX:UseG1GC -jar …...
Bobo String Construction
登录—专业IT笔试面试备考平台_牛客网 题目大意:给出一字符串t,求一个长为n的字符串,使tst中包含且仅包含两个t 1<n<1000;测试样例组数<1000 思路:一开始很容易想到如果t里有1,s就全0,否则s就全…...
基于java在线个人网站源码设计与实现
摘 要 随着社会及个人社交应用平台的飞速发展,人们的沟通成本逐渐降低,互联网信息的普及也进一步提升了人们对于信息的需求度,通过建立个人网站的方式来展示自己的生活信息同时利用平台结交新的朋友,借助个人网站平台的搭建不仅可…...

Ubuntu18.04下编译qgc源码
写在前面 在下载前必须说明,根据你的qgc源码版本进行下载,有的源码必须要求Qt是5.15版本以上。 个人所使用开发软件 版本QT5.12.9qgc源码V4.0Ubuntu18.04 QT下载 (1)我们可以去官网下载官网下载地址具体的下载方法这里不用多说&a…...
Ros2_windows_install的学习笔记
Ros2_windows_install安装 Iron安装 iex ((New-Object System.Net.WebClient).DownloadString(https://raw.githubusercontent.com/scottcandy34/ros2_windows_install/main/ros2_iron.ps1))启动Iron C:\dev\ros2_iron\local_setup.bat...

5、Kubernetes核心技术 - Controller控制器工作负载
目录 一、Deployments - 控制器应用 二、Deployment升级回滚和弹性收缩 2.1、创建一个 1.14 版本的 pod 2.2、应用升级 2.3、查看升级状态 2.4、查看历史版本 2.5、应用回滚 2.6、弹性伸缩 三、StatefulSet - 有状态应用 四、DaemonSet - 守护进程 五、Job - 单次任…...
【java设计模式】创建型模式介绍(工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式)
文章目录 简介一、工厂模式介绍案例 二、抽象工厂模式介绍案例 三、单例模式介绍案例 四、建造者模式介绍案例 五、原型模式介绍案例 简介 本文介绍Java设计模式中创建型模式的五种 一、工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式…...

Redis系列:Redis 的事务机制
1 复习下何为事务机制? Transaction(事务)是计算机的特有术语,它一般指单个逻辑工作单位,由一系列的操作组合而成,在这些操作执行的时候,要么都执行成功,要么都不执行,防…...

动静态网页、Django创建表关系、Django框架的请求生命周期流程图
一、request对象的几个方法 在视图函数中写方法的时候,都会有一个形参requestdef index(request):passrequest.method # GET POST request.GET.get() # 它获取最后一个元素值 request.GET.getlist() # 获取到所有的request.POST.get() # 它获取最后一个元素值 req…...

神经网络的初始化方法
文章目录 1、随机初始化2、Xavier初始化3、He初始化4、权重预训练初始化5、零初始化 对于神经网络的训练过程中,合适的参数初始化方法有助于更好的处理梯度消失和梯度爆炸问题。通常有以下几种初始化方法: 1、随机初始化 随机初始化(Random…...

【SQL Server】DBCC CHECKDB只是一个数据库维护命令吗?
日期:2023年7月27日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方…...

三、Web安全相关知识
请勿用于非法用途 文章目录 一、Web源码框架二、目录结构1、静态资源2、WEB-INF(1)classes(2)lib(3)web.xml 二、web脚本语言1、脚本种类(1)ASP(2)ASP.NET&am…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...