Spring(学习笔记)

news2024/10/3 12:22:48

<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.coloradmin.cn/o/2186196.html

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

相关文章

四.网络层(上)

目录 4.1网络层功能概述 4.2 SDN基本概念 4.3 路由算法与路由协议 4.3.1什么是路由协议&#xff1f; 4.3.2什么是路由算法&#xff1f; 4.3.3路由算法分类 (1)静态路由算法 (2)动态路由算法 ①全局性 OSPF协议与链路状态算法 ②分散性 RIP协议与距离向量算法 4.3.…

netty之Netty使用Protobuf传输数据

前言 在netty数据传输过程中可以有很多选择&#xff0c;比如&#xff1b;字符串、json、xml、java对象&#xff0c;但为了保证传输的数据具备&#xff1b;良好的通用性、方便的操作性和传输的高性能&#xff0c;我们可以选择protobuf作为我们的数据传输格式。目前protobuf可以支…

(作业)第三期书生·浦语大模型实战营(十一卷王场)–书生基础岛第1关---书生大模型全链路开源体系

观看本关卡视频和官网https://internlm.intern-ai.org.cn/后&#xff0c;写一篇关于书生大模型全链路开源开放体系的笔记发布到知乎、CSDN等任一社交媒体&#xff0c;将作业链接提交到以下问卷&#xff0c;助教老师批改后将获得 100 算力点奖励&#xff01;&#xff01;&#x…

V3D——从单一图像生成 3D 物体

导言 论文地址&#xff1a;https://arxiv.org/abs/2403.06738 源码地址&#xff1a;https://github.com/heheyas/V3D.git 人工智能的最新进展使得自动生成 3D 内容的技术成为可能。虽然这一领域取得了重大进展&#xff0c;但目前的方法仍面临一些挑战。有些方法速度较慢&…

深刻理解Redis集群(中):Redis主从数据同步模式

背景 目前实现Redis高可用的模式主要有三种&#xff1a;主从模式、哨兵模式、集群模式。今天我们先来聊一下主从模式。 Redis 提供的主从模式&#xff0c;是通过复制的方式&#xff0c;将主服务器上的Redis的数据同步复制一份到从 Redis 服务器&#xff0c;这种做法很常见&…

函数式接口在Java中的应用与实践

1. 引言 函数式接口是Java 8引入的一个概念&#xff0c;它是指只有一个抽象方法的接口。函数式接口可以被用作lambda表达式的目标类型。在函数式接口中&#xff0c;除了抽象方法外&#xff0c;还可以有默认方法和静态方法。 函数式接口的引入是为了支持函数式编程&#xff0c…

SpringBoot 源码解读与自动装配原理结合Actuator讲解

Spring Boot 作为简化 Spring 应用开发的重要框架&#xff0c;能够通过“约定大于配置”的方式&#xff0c;使开发者无需大量的 XML 或配置类即可完成复杂的配置过程。这背后的核心机制之一就是 自动装配 (Auto-Configuration)&#xff0c;其依赖 Spring 的 依赖注入 (DI) 和 注…

AI通用大模型编程需要的能力

这几天研究通过通义千问AI大模型编程&#xff0c;有三点感受&#xff0c;分享给大家。如果将来有新的感受&#xff0c;会继续分享。 1、清晰的提示词指令&#xff0c;让输出的成功率更高 2、了解点代码知识&#xff0c;虽不会写&#xff0c;但能看的懂 3、定位代码问题的能力…

数据库软题5-SQL语言

一、DDL数据定义语言 题 1-创建视图 建立视图属于DDL的知识 建立视图要用到CREATE AS CREATE View Computer-BOOK ASSELECT 图书编号、图书名称、作者、出版社、出版日期FROM 图书WHERE 图书类型计算机 WITH CHEEK OPTION&#xff1b;二、DQL数据查询语言 题1-交 查询平均…

SAP 和 Carahsoft 的调查范围扩大到与近 100 家机构

美国司法部正在扩大对德国软件公司SAP和经销商Carahsoft的价格操纵调查&#xff0c;涉及近100个政府机构。这项调查最初集中在两家公司是否在2014年以来向美国国防部和其他政府部门收取过高费用&#xff0c;涉及金额超过20亿美元。最新的法院文件显示&#xff0c;调查范围已扩展…

HTTPS协议详解:从原理到流程,全面解析安全传输的奥秘

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

精准农业中遥感技术应用(六)- 作物长势分析和展示

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、领域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&…

Linux之实战命令23:lsattr应用实例(五十七)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

解锁中英互译新工具,4款翻译助手带你畅游语言世界。

在现在的全球化的大背景下&#xff0c;中英互译工具显得十分的重要&#xff0c;我们无论是跨文化学习、工作还是生活旅行&#xff0c;都离不开有效的中英互译。今天我们就来说说几款高效的中英互译工具&#xff0c;希望它们能够在生活中给大家带来帮助。 1、中英在线翻译大师 …

Vortex GPGPU的github流程跑通与功能模块波形探索(二)

文章目录 前言一、环境配置和debugging.md文档1.1 调试 Vortex GPU1.1.1测试 RTL 或模拟器 GPU 驱动的更改1.1.2 SimX 调试1.1.3 RTL 调试1.1.4 FPGA 调试1.1.5 分析 Vortex 跟踪日志 二、跑出波形文件和日志文件总结 前言 昨天另辟蹊径地去探索了子模块的波形仿真&#xff0c…

读数据湖仓05数据需要的层次

1. 业务价值 1.1. 技术和商业在这个世界上是相互交织的 1.1.1. 基础数据在商业和技术应用中是不可或缺的 1.2. 技术的存在是为了推动商业的目标和进步&#xff0c;并由企业出资支持 1.2.1. 当技术推动商业发展时&#xff0c;商业会蓬勃发展&#xff0c;技术也会随之繁荣 1.…

Megabit兆比特10月比特币激增做好准备-最新加密货币新闻

Kaiko Research最近的分析表明&#xff0c;交易员正在积极为潜在的强劲表现做好准备特币(BTC)比今年十月。目前&#xff0c;BTC的交易价格为60800美元&#xff0c;在测试了60000美元的支撑位后&#xff0c;最近上涨了800美元。Megabit兆比特自成立以来,Megabit凭借用户友好的界…

初识Linux以及Linux的基本命令

千呼万唤始出来&#xff0c;Linux系列的文章从今天起开始不定期更新&#xff0c;闲话少叙&#xff0c;我们直接进入正题 目录 初识Linux 前置知识点 什么是路径&#xff1f; 什么是目录&#xff1f; 什么是文件&#xff1f; Linux的基本命令 Linux中的复制粘贴 创建文件…

数据仓库的建设——从数据到知识的桥梁

数据仓库的建设——从数据到知识的桥梁 前言数据仓库的建设 前言 企业每天都在产生海量的数据&#xff0c;这些数据就像无数散落的珍珠&#xff0c;看似杂乱无章&#xff0c;但每一颗都蕴含着潜在的价值。而数据仓库&#xff0c;就是那根将珍珠串起来的线&#xff0c;它能够把…

【AIGC】2020-NIPS-去噪扩散概率模型

2020-NIPS-Denoising Diffusion Probabilistic Models 去噪扩散概率模型摘要1. 引言2. 背景3. 扩散模型和去噪自动编码器3.1 正向过程和 L T L_{T} LT​3.2 逆过程与 L 1 : T − 1 L_{1:T-1} L1:T−1​3.3 数据缩放、逆过程解码器和 L 0 L_{0} L0​3.4 简化的训练目标 4. 实…