Spring(学习笔记)

<context:annotation-config/>是 Spring 配置文件中的一个标签,用于开启注解配置功能。这个标签可以让 Spring 容器识别并处理使用注解定义的 bean。例如,可以使用 @Autowired 注解自动装配 bean,或者使用 @Component 注解将类标记为 bean 等。

为什么加载上下文的时候,被包含的Bean它的构造函数的代码会执行呀?好像是很多代码都执行了!!!

没有无参构造,有有参构造会报错!

也就是说必须要有无参构造!这是为什么呀 !

@Value注解:

@Value("Essence")
private String name;

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

<bean id="people" class="pojo.People" autowire="byType"><property name="name" value="Durant"/><property name="dog" ref="dog"/><property name="cat" ref="cat"/>
</bean><bean id="cat" class="pojo.Cat"/><bean id="dog" class="pojo.Dog"/>

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

<bean id="hello" class="pojo.Hello"><property name="str" value="Spring"/>
</bean>

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

<bean id="userT" class="pojo.UserT" name="user2,u2"><property name="name" value="张恒"/>
</bean>

getBean();中可以输入u2,user2

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

<bean id="address" class="pojo.Address"><property name="address" value="西安"/>
</bean><bean id="student" class="pojo.Student"><property name="name" value="张恒"/><property name="address" ref="address"/><property name="books"><array><value>红楼梦</value><value>西游记</value><value>水浒传</value><value>三国演义</value></array></property><property name="hobbys"><list><value>听歌</value><value>看电影</value><value>敲代码</value></list></property><property name="card"><map><entry key="身份证" value="411628"/></map></property><property name="games"><set><value>穿越火线</value></set></property><property name="wife"><null/></property><property name="info"><props><prop key="学号">2020</prop><prop key="性别">男</prop><prop key="姓名">小明</prop></props></property>
</bean>

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

Spring IoC底层源码分析

Spring IoC容器的启动可以概括为一下两步:

  • 创建BeanFactory
  • 实例化Bean对象

在SourceCodeLearning类中设置好断点后,下面一步步进入Spring底层代码。

ApplicationContext applicationContext = new FileSystemXmlApplication("classpath:spring.xml");

通过FileSystemXmlApplicationContext跟踪上述构造器可以发现,其主要完成了一下三个步骤:

  • 初始化父容器AbstractApplicationContext
  • 设置资源文件的位置setConfigLocations
  • 使用核心方法refresh(),其实是在超类AbstractApplicationContext中定义的一个模版方法(模版方法设计模式)。

refresh()方法的定义--ConfigurationApplicationContext接口中定义了该方法。

ConfigurationApplicationContext的基类是BeanFactory。

AbstractApplicationContext类实现了ConfigurationApplicationContext接口,重写了refresh()方法。部分重要内容如下:

AbstractApplicationContext.refresh()方法是个模版方法,定义了需要执行的一些步骤。并不是实现了所有的逻辑,只是充当了一个模版,由其子类去实现更多个性化的逻辑。

模版方法refresh()中最核心的两步:

(1)创建BeanFactory:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

(2)实例化Bean:

finishBeanFactoryInitialization(beanFactory);

创建BeanFactory

创建BeanFactory重点分析AbstractApplicationContext.obtainFreshBeanFactory()方法。其代码实现如下:

从以上代码可以发现,AbstractApplicationContext.obtainFreshBeanFactory()方法分为以下两步:

  • 刷新BeanFactory,即refreshBeanFactory()。
  • 获取BeanFactory,即getBeanFactory()。

这两步中刷新BeanFactory的方法refreshBeanFactory()是核心,接下来进一步分析refreshBeanFactory()方法。这个方法定义在AbstractApplicationContext中,是一个抽象方法,也是一个模版方法,需要AbstractApplicationContext的子类来实现逻辑。其具体实现是在其子类AbstractRefreshableApplicationContext中完成的。refreshBeanFactory()方法实现的部分代码如下:

可以发现,在refreshBeanFactory()方法的实现中,首先检查当前上下文是否已经存在BeanFactory。如果已存在BeanFactory,先销毁Bean和BeanFactory,然后创建新的BeanFactory。

DefaultListableBeanFactory beanFactory = createBeanFactory();这行代码只是创建了一个空的BeanFactory,其中没有任何Bean。因此refreshBeanFactory()方法的核心功能是在loadBeanDefinitions(beanFactory);这行代码中实现的。

loadBeanDefinitions()的具体实现是在AbstractXmlApplication类中。

loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,通过上一步创建的空的BeanFactory来创建一个XmlBeanDefinitionReader对象。XmlBeanDefinitionReader是用来解析XML中定义的bean的。

下面重点讲解loadBeanDefinitions(beanDefinitionReader)方法,这是一个重载的方法,这个方法的入参是刚刚生成的XmlBeanDefinitionReader对象。下面进入重载的loadBeanDefinitions方法进行分析,代码如下:

这个方法主要的功能是解析资源文件的位置,然后调用XmlBeanDefinitionReader对象的loadBeanDefinitions方法解析Bean的定义。

下面将对reader.loadBeanDefinitions(cinfigLocations);这段代码进行解析。

分析AbstractBeanDefinitionReader的方法loadBeanDefinitions,其方法实现如下:

 可以发现loadBeanDefinition()方法会遍历资源数组,最终会调用重载方法loadBeanDefinition(),重载方法的部分实现代码如下:

 

这个方法会解析资源文件的路径,得到Resource[]资源数组,核心逻辑是调用loadBeanDefinitions(resource)方法,进入这个方法查看其代码如下:

loadBeanDefinitions内部工作原理是遍历每个资源,依次调用loadBeanDefinitions(Resource resource)重载的方法。该重载的方法在顶层接口BeanDefinitionReader中

该方法会调用重载方法loadBeanDefinitions(EncodedResource encodedResource)。

loadBeanDefinitions(EncodeResource encodedResource)方法以流的方式读取资源文件,调用doLoadBeanDefinition()方法。doLoadBeanDefinition()是载入定义Bean的核心方法。其部分代码如下:

从doLoadBeanDefinition(InputSource inputSource, Resource resource)方法的定义可以看出,最终注册Bean的地方是在registerBeanDefinitions(doc, resource);这行代码。其代码如下:

registerBeanDefinitions(Document doc, Resource resource)方法的核心逻辑是在documentReader.registerBeanDefinitions(doc, createReaderContext(resource));这一行,这里发生了对Bean的注册。registerBeanDefinitions(Document doc , XmlReaderContext readerContext)方法代码如下:

registerBeanDefinitions(Document doc , XmlReaderContext readerContext)方法是在DefaultBeanDefinitionDocumentReader中实现的。核心是通过doRegisterBeanDefinitions()方法实现的。其代码实现如下:

doRegisterBeanDefinitions(Element root)方法的核心逻辑在parseBeanDefinition(root,this.delegate);这个方法中处理。其代码如下:

parseBeanDefinition(root,this.delegate)方法的核心逻辑是依赖parseDefaultElement(ele, delegate);方法实现的,其代码如下:

根据不同Bean的配置不同,进入不同分支执行。本书的示例是进入processBeanDefinition(ele,delegate)方法。其代码如下:

从上述方法中可知,最关键的是BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());的调用。这是注册Bean的关键代码,其代码如下:

registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());这行是将Bean的名字和BeanDefinition对象进行注册的地方。该方法的定义是在BeanDefinitionRegistry中。

本例将进入BeanDefinitionRegistry接口的实现类DefaultListableBeanFactory中,其部分代码如下:

从registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法代码可以看出,先从beanDefinitionMap这个ConcurrentHashMap对象根据beanName查找是否已经有同名的bean,如果不存在,则会调用beanDefinitionMap.put(beanName,beanDefinition)方法,以beanName为key,beanDefinition为value注册,将这个Bean注册到BeanFactory中,并将所有的BeanName保存到beanDefinitionNames这个ArrayList中。

到此,完成了IoC第一部分——创建BeanFactory的代码解析。但是,此时Bean只是完成了Bean名称和BeanDefinition对象的注册,并没有实现Bean的实例化和依赖注入。下面将要分析IoC的第二个关键部分Bean的初始化。

实例化Bean

在创建BeanFactory的过程中,BeanDefinition注册到了BeanFactory中的一个ConcurrentHashMap对象中了,并且以BeanName为key,BeanDefinition为value注册。下面将要分析实例化Bean的过程,即从上文提到的AbstractApplicationContext类的refresh()方法中的finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法开始向底层分析。

首先进入finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法,查看其部分代码如下:

从上述代码可知,beanFactory.preInstantiateSingletons();这行代码是实例化Bean的。

打开preInstantiateSingletons()方法如下:

该方法遍历beanDefinitionNames这个ArrayList对象中的BeanName,循环调用getBean(beanName)方法。该方法实际上就是创建Bean并递归构建Bean间的依赖关系。getBean(beanName)方法最终会调用doGetBean(name,null,null,false),进入该方法查看doGetBean方法的部分代码如下:

可以看到,该方法首先会获取当前Bean依赖关系mbd.getDependsOn();接着根据依赖的BeanName递归调用getBean()方法,直到调用到getSingleton()方法返回依赖Bean,即当前正在创建的Bean ,不断探寻依赖的Bean,直到依赖关系最底层的Bean 没有依赖的对象了,至此整个递归过程结束。getSingleton()方法的参数是createBean()方法的返回值。createBean()是在AbstractAutowireCapableBeanFactory中实现的。createBean(String beanName, RootBeanDefinition mbd,@Nullable Object[] args)方法部分代码如下:

该方法的核心是doCreateBean(beanName,mdbToUse,args)这个方法,doCreateBean将会返回Bean对象的实例。查看doCreateBean的部分代码如下:

这个方法中最重要的两行代码:

(1)instanceWrapper = createBeanInstance(beanName,mbd,args)用来创建实例。

(2)方法populateBean(beanName,mbd,instanceWrapper)用于填充Bean,该方法可以说就是发生了依赖注入的地方。

先看看createBeanInstance()方法其核心实现如下:

createBeanInstance()方法会调用instantiateBean()方法,其部分实现如下:

instantiateBean()方法核心逻辑是beanInstance = getInstantiationStrategy().instantiate(),发挥作用的策略对象是SimpleInstantiationStrategy,在该方法内部调用了静态方法BeanUtils.instantiateClass(),这个方法的部分实现如下:

该方法会判断是否是Kotlin类型,如果不是,则会调用Constructor的newInstance方法,也就是最终使用反射创建了该实例。

到这里,Bean的实例已经创建完成。但是Bean实例的依赖关系还没有设置,下面回到doCreateBean()方法中的populateBean()方法,该方法用于填充Bean,该方法可以说就是发生依赖注入的地方。回到AbstractAutowireCapableBeanFactory类中看一下populateBean()方法的实现。populateBean()部分代码如下:

整个方法的核心逻辑是PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues():null);这行代码,即获取该bean的所有属性,就是配置property元素,即依赖关系。最后执行applyPropertyValues()方法,其实现如下:

关键代码Object resolveValue = valueResolver.resolveValueIfNecessary(pv,originalValue);该方法是获取property对应的值。resolveValueIfNecessary()方法部分代码如下:

resolveValueIfNecessary()方法的核心是resolveReference(),该方法是解决Bean依赖关系的。进入该方法,其代码如下:

这段代码的核心是以下这一行:

bean = this.beanFactory.getParentBeanFactory().getBean();

这里将会发生递归调用,根据依赖的名称,从BeanFactory中递归得到依赖。到这段结束,就可以获取到依赖的Bean。回到applyPropertyValues入口处,获取到依赖的对象值后,将会调用bw.setPropertyValues()方法,这是将依赖值注入的地方。此方法会调用AbstractPropertyAccessor类的setPropertyValues方法,查看AbstractPropertyAccessor.setPropertyValues方法的实现,其部分代码如下:

该方法会循环Bean的属性列表,循环中调用setPropertyValue()方法,该方法是通过AbstractPropertyAccessor.setPropertyValues()方法来实现的,进入该方法的代码,其部分实现如下:

其核心是最后一行nestedPa.setPropertyValue()代码,其部分代码实现如下:

进入processLocalProperty()方法的代码,该方法非常复杂,其核心实现如下:

上述代码调用的ph.setValue()方法是BeanWrapperImpl.setValue()方法,进入这个方法的代码,查看其部分实现如下:

该方法是最后一步,这里可以看到该方法会找到属性的set方法,然后调用Method的invoke方法,完成属性注入。至此IoC容器的启动过程完毕。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/437823.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机三种网络模式详解

在电脑里开一台虚拟机&#xff0c;是再常见不过的操作了。无论是用虚拟机玩只有旧版本系统能运行的游戏&#xff0c;还是用来学习Linux、跑跑应用程序都是很好的。而这其中&#xff0c;虚拟机网络是绝对绕不过去的。本篇文章通俗易懂的介绍了常见的虚拟网络提供的三种网络链接模…

鸿蒙OpenHarmony

开源鸿蒙系统编译指南 Ubuntu编译环境配置第一步&#xff1a;Shell 改 Bash第二步&#xff1a;安装Git和安装pip3工具第三步&#xff1a;远程仓配置第四步&#xff1a;拉取代码第五步&#xff1a;安装编译环境第六步&#xff1a;本地编译源码 Windows开发环境配置第一步&#x…

dubbo微服务

一.启动nacos和redis 1.虚拟机查看是否开启nacos和redis docker ps2.查看是否安装nacos和redis docker ps -a3.启动nacos和redis docker start nacos docker start redis-6379 docker ps二.创建三个idea的maven项目 1.第一个项目dubboapidemo 2.1.1向pom.xml里添加依赖 …

x-cmd pkg | qrencode - 命令行生成二维码,小白也能轻松上手!

目录 简介首次用户功能特点竞品和相关项目进一步阅读 简介 qrencode 是一个用于生成二维码的命令行工具。它可以将文本、URL、电话号码等信息转换为二维码图像。生成的二维码图像可以保存为图片文件&#xff0c;方便在电子文档、网页、移动应用等各种场景中使用。 它支持的二维…

深入理解 Solidity 中的支付与转账:安全高效的资金管理攻略

在 Solidity 中&#xff0c;支付和转账是非常常见的操作&#xff0c;尤其是在涉及资金的合约中&#xff0c;比如拍卖、众筹、托管等。Solidity 提供了几种不同的方式来处理 Ether 转账&#xff0c;包括 transfer、send 和 call&#xff0c;每种方式的安全性、灵活性和复杂度各有…

SKD4(note上)

微软提供了图形的界面API&#xff0c;叫GDI 如果你想画某个窗口&#xff0c;你必须拿到此窗口的HDC #include <windows.h> #include<tchar.h> #include <stdio.h> #include <strsafe.h> #include <string>/*鼠标消息 * 键盘消息 * Onkeydown * …

STM32 软件触发ADC采集

0.91寸OLED屏幕大小的音频频谱&#xff0c;炫酷&#xff01; STM32另一个很少人知道的的功能——时钟监测 晶振与软件的关系&#xff08;深度理解&#xff09; STM32单片机一种另类的IO初始化方法 ADC是一个十分重要的功能&#xff0c;几乎任何一款单片机都会包含这个功能&a…

阿里云 SAE Web:百毫秒高弹性的实时事件中心的架构和挑战

作者&#xff1a;胡志广(独鳌) 背景 Serverless 应用引擎 SAE 事件中心主要面向早期的 SAE 控制台只有针对于应用维度的事件&#xff0c;这个事件是 K8s 原生的事件&#xff0c;其实绝大多数的用户并不会关心&#xff0c;同时也可能看不懂。而事件中心&#xff0c;是希望能够…

JS进阶 3——深入面向对象、原型

JS 进阶3——深入面向对象、原型 1.编程思想 面向过程&#xff1a;分析出解决问题的过程&#xff0c;然后用函数将这些步骤一步步封装起来面向对象&#xff1a;将事物分为一个个对象&#xff0c;然后对象之间分工合作 2.构造函数&#xff1a;封装性、面向对象 构造函数方法存…

linux学习--第七天(多路复用IO)

多路复用IO -阻塞IO与非阻塞IO -IO模型 IO的本质时基于操作系统接口来控制底层的硬件之间数据传输&#xff0c;并且在操作系统中实现了多种不同的IO方式&#xff08;模型&#xff09;比较常见的有下列三种&#xff1a; 1.阻塞型IO模型 2.非阻塞型IO模型 3.多路复用IO模型 -阻…

开源项目 - 交通工具检测 yolo v3 物体检测 单车检测 车辆检测 飞机检测 火车检测 船只检测

开源项目 - 交通工具检测 yolo v3 物体检测 单车检测 车辆检测 飞机检测 火车检测 船只检测 开源项目地址&#xff1a;https://gitcode.net/EricLee/yolo_v3 示例&#xff1a;

【C++】多态(下)

个人主页~ 多态&#xff08;上&#xff09;~ 多态 四、多态的原理1、虚表的存储位置2、多态的原理3、动态绑定和静态绑定 五、单继承和多继承关系的虚函数表1、单继承中的虚函数表2、多继承中的虚函数表 六、多态中的一些小tips 四、多态的原理 1、虚表的存储位置 class A {…

开放式耳机哪个品牌好?分享几款不错的开放式蓝牙耳机

相信很多人戴入耳式耳机时间一久&#xff0c;就不是很舒服。经常会有闷热、不透气的感觉&#xff0c;甚至有的朋友会因为佩戴入耳式耳机滋生细菌&#xff0c;导致最后炎症的发生。总之&#xff0c;入耳式耳机真的不适合长时间佩戴&#xff0c;而且佩戴的场景也有很多限制。 那…

一文了解构建工具——Maven与Gradle的区别

目录 一、Maven和Gradle是什么&#xff1f; 构建工具介绍 Maven介绍 Gradle介绍 二、使用时的区别&#xff1a; 1、新建项目 Maven&#xff1a; Gradle&#xff1a; 2、配置项目 Maven&#xff1a; Gradle&#xff1a; 3、构建项目——生成项目的jar包 Gradle&…

Linux 信号详解

目录 一.前置知识 1.前台进程和后台进程 a.概念理解 b.相关指令 2.信号的前置知识 a.Linux 系统下信号的概念 b.进程对信号的处理方式 3.信号的底层机制 二.详解信号 1.信号的产生 a.键盘组合键 b.kill 指令和系统调用接口 ① kill 指令 ② kill() 系统调用接口 ③ raise() 系统…

TCP四次挥手过程详解

TCP四次挥手全过程 有几点需要澄清&#xff1a; 1.首先&#xff0c;tcp四次挥手只有主动和被动方之分&#xff0c;没有客户端和服务端的概念 2.其次&#xff0c;发送报文段是tcp协议栈的行为&#xff0c;用户态调用close会陷入到内核态 3.再者&#xff0c;图中的情况前提是双…

leetcode-链表篇3

leetcode-61 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入&#xff1a;head [0,1,2], k 4 输出&#x…

栏目二:Echart绘制动态折线图+柱状图

栏目二&#xff1a;Echart绘制动态折线图柱状图 配置了一个ECharts图表&#xff0c;该图表集成了数据区域缩放、双Y轴显示及多种图表类型&#xff08;折线图、柱状图、象形柱图&#xff09;。图表通过X轴数据展示&#xff0c;支持平滑折线展示比率数据并自动添加百分比标识&…

从原理到代码:如何通过 FGSM 生成对抗样本并进行攻击

从原理到代码&#xff1a;如何通过 FGSM 生成对抗样本并进行攻击 简介 在机器学习领域&#xff0c;深度神经网络的强大表现令人印象深刻&#xff0c;尤其是在图像分类等任务上。然而&#xff0c;随着对深度学习的深入研究&#xff0c;研究人员发现了神经网络的一个脆弱性&…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 10月3日,星期四

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年10月3日 星期四 农历九月初一 1、 应急管理部&#xff1a;10月华北东南部等部分地区洪涝和风雹灾害风险较高&#xff0c;可能有1-2个台风登陆或明显影响我国。 2、 中国海警舰艇首次进入北冰洋&#xff0c;与俄海警展开联…