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.xdnf.cn/news/1553123.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

10/02赛后总结

T1学习除法 题目传送门&#xff1a;学习除法http://bbcoj.cn/contest/1028/problem/1 说白了&#xff0c;就是检验是不是质数罢了&#xff0c;是质数输出0&#xff0c;不然输出1&#xff1b; 但是质数判断写错了 100分只有60分&#xff0c;damn #include<bits/stdc.h>…

【Linux】进程间关系与守护进程

超出能力之外的事&#xff0c; 如果永远不去做&#xff0c; 那你就永远无法进步。 --- 乌龟大师 《功夫熊猫》--- 进程间关系与守护进程 1 进程组2 会话3 控制终端4 作业控制5 守护进程 1 进程组 之前我们提到了进程的概念&#xff0c; 其实每一个进程除了有一个进程 ID(P…

Django5 使用pyinstaller打包成 exe服务

首先&#xff1a;确保当前的django项目可以完美运行&#xff0c;再进行后续操作 python manage.py runserver第一步 安装 pyinstaller pip install pyinstaller第二步 创建spec 文件 pyinstaller --name manage --onefile manage.pypyinstaller&#xff1a;这是调用 PyInsta…

数据异质性与数据异构性的本质和举例说明

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在现代数据科学与信息技术领域&#xff0c;“数据异质性” 与 “数据异构性” 是两个常见的概念。对于初学者而言&#xff0c;明确这两个概念的本质及其间的差异至关重要。本文旨在以简明易懂的方式&am…

Python笔记 - 利用装饰器设计注解体系

认识注解 注解&#xff08;Annotation&#xff09;是一种用于为代码添加元数据的机制。这些元数据可以在运行时被访问&#xff0c;用于为代码元素&#xff08;如类、方法、字段等&#xff09;提供额外的信息或指示。 由于Python中装饰器只能装饰类和方法&#xff0c;因此也只…

Mac 网络连接正常,微信可以使用,但浏览器打不开网页?

解决&#xff1a; Step1&#xff0c;选择&#x1f34e;图标&#xff0c;选择系统设置&#xff08;或系统偏好设置&#xff09;打开&#xff1b; Step2&#xff0c;选择网络&#xff0c;Wi-Fi Step3&#xff0c;选择详细信息&#xff1b; Step4: 选择代理&#xff0c;关闭右…

3.点位管理改造-列表查询——帝可得管理系统

目录 前言一、与页面原型差距1.现在&#xff1a;2.目标&#xff1a;3. 存在问题&#xff1a;所在区域和合作商ID展示的都是ID&#xff0c;而不是名称&#xff1b;同时合作商ID应改为合作商 二、修改1.重新设计SQL语句2.修改mapper层&#xff0c;使用Mybatis中的嵌套查询3.修改s…

C. Tree Pruning【Codeforces Round 975 (Div. 1)】

C. Tree Pruning (永远不知道为什么TLE直到把初始化的memset换成for循环 题意很简单&#xff0c;就是找到一个深度&#xff0c;使得删除最少的节点且所有的叶子节点都为这个深度。 从小到大遍历可能的深度i&#xff0c;容易知道所有 深度大于i的节点 和所有 子树最大深度小于i…

操作符详解与表达式求值

目录 操作符分类 1.算数操作符 2.移位操作符&#xff08;只适用于整数范围&#xff09; &#xff08;1&#xff09;引入 &#xff08;2&#xff09;左移操作符<< &#xff08;2&#xff09;右移操作符>> 3.位操作符 4.赋值操作符 复合赋值符 5.单目操作符 5…

深度优先搜索(DFS)与有向图中的唯一结点

深度优先搜索(DFS)与有向图中的唯一结点 前提与定义分析与方法伪代码与 C 代码实现解释结果在图论中,深度优先搜索(DFS)是一种用于遍历或搜索图的算法。DFS 从给定的起始结点出发,沿着图的深度方向尽可能深地搜索,直到无法继续为止,然后回溯并从未访问过的邻接结点继续…

Unraid的cache使用btrfs或zfs?

Unraid的cache使用btrfs或zfs&#xff1f; 背景&#xff1a;由于在unraid中添加了多个docker和虚拟机&#xff0c;因此会一直访问硬盘。然而&#xff0c;单个硬盘实在难以让人放心。在阵列盘中&#xff0c;可以通过添加校验盘进行数据保护&#xff0c;在cache中无法使用xfs格式…

YOLOv11改进 | Neck篇 | YOLOv11引入Slim-Neck(轻量)

1. Slim-Neck介绍 摘要&#xff1a;目标检测是计算机视觉中重要的下游任务。 对于车载边缘计算平台来说&#xff0c;巨大的模型很难达到实时检测的要求。 而且&#xff0c;由大量深度可分离卷积层构建的轻量级模型无法达到足够的精度。 我们引入了一种新的轻量级卷积技术 GSCon…

【顺序查找】

目录 一. 顺序查找的概念二. 查找的性能计算 \quad 一. 顺序查找的概念 \quad \quad 二. 查找的性能计算 \quad

使用ROCm的GPU感知MPI

GPU-aware MPI with ROCm — ROCm Blogs (amd.com) 注意: 此博客之前是 AMD Lab Notes博客系列的一部分。 MPI&#xff08;消息传递接口&#xff09;是高性能计算中进程间通信的事实标准。MPI进程在其本地数据上进行计算&#xff0c;同时进行大量的相互通信。这使得MPI程序可以…

【折半查找】

目录 一. 折半查找的概念二. 折半查找的过程三. 折半查找的代码实现四. 折半查找的性能分析 \quad 一. 折半查找的概念 \quad 必须有序 \quad 二. 折半查找的过程 \quad \quad 三. 折半查找的代码实现 \quad 背下来 \quad 四. 折半查找的性能分析 \quad 记住 比较的是层数 …

sed引入变量中的坑

sed引入变量问题 1、sed引入变量2、sed引入变量问题 1、sed引入变量 sed指令引入变量&#xff0c;直接使用双引号即可 例如&#xff0c;下面的示例&#xff1a; ab; echo "abc" | sed "s/b/$a/g"2、sed引入变量问题 但是&#xff0c;如果变量值中带有/等…

自闭症寄宿学校:释放孩子内心的美

在自闭症儿童的成长旅程中&#xff0c;寻找一个既能提供专业康复服务&#xff0c;又能让孩子感受到爱与关怀的教育环境&#xff0c;是许多家庭梦寐以求的目标。在广州&#xff0c;星贝育园自闭症儿童寄宿制学校正是这样一所充满爱与希望的学校&#xff0c;它不仅为自闭症儿童提…

CMU 10423 Generative AI:lec13/13.5(text-to-image models:三大类方法、评估标准、图像编辑原理)

1 文章目录 1 lec13和lec13.5概述2 Text-to-Image Generation 概念、主要方法、挑战、发展历程1. **基本概念**2. **主要技术方法**2.1. **生成对抗网络&#xff08;GAN&#xff09;**2.2. **自回归模型&#xff08;Autoregressive Models&#xff09;**2.3. **扩散模型&#x…

9.28学习笔记

1.ping 网址 2.ssh nscc/l20 3.crtl,打开vscode的setting 4.win 10修改ssh配置文件及其密钥权限为600 - 晴云孤魂 - 博客园 整体来看&#xff1a; 使用transformer作为其主干网络&#xff0c;代替了原先的UNet 在latent space进行训练&#xff0c;通过transformer处理潜…

Java项目实战II基于Java+Spring Boot+MySQL的智能物流管理系统(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 随着电商行业的蓬勃发展&#xff0c;物流行业迎来了前所未有的机遇与挑战。面对日益增长的订单量和复…