Spring-IOC容器深度剖析详解

🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟
🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵
🍉本篇简介:🍉 本篇由表及里分析Spring-IOC容器始末,如有出入还望指正。

关注公众号【可为编程】回复【面试】领取年度最新面试题!!!

上一篇我们讲到了IOC容器,其实就是我们常说的Spring容器IOC容器是具有依赖注入功能(也就是DI)的容器,负责对象的实例化、对象的初始化,对象和对象之间依赖关系配置、对象的销毁、对使用者提供对象的查找等操作,可以说IOC容器控制了整个对象的生命周期。我们需要使用的对象都由IOC容器进行创建并管理,不需要我们再去手动通过new的方式去创建对象,而是由IOC容器直接帮我们组装好,当我们需要使用的时候直接从IOC容器中直接获取就可以了,这是上一篇我们讲过的内容,这里再回顾一下。

上一篇 Spring中的核心概念

那么spring ioc容器是如何知道需要管理哪些对象呢?

上文说到我们需要给IOC容器提供一个配置清单,这个配置清单支持xml格式java注解的方式,在配置文件中列出需要让IOC容器管理的对象,以及指定好让IOC容器如何构建某些对象,比如A对象依赖B对象、C对象等对象与对象之间的依赖关系。当spring容器启动的时候,就会去加载这个配置文件,然后将这些对象给组装好以供外部访问者使用。


IOC容器初始化细节

当然,到了springboot的时候有了自动注入的功能,在启动时并不第一时间去直接加载所有Bean的配置文件,而是采用了懒加载的方式。这意味着在应用程序启动后,只有当实际需要使用到配置信息时,才会去加载对应的配置文件。通过加载META-INF/spring.factories文件来实现自动配置。这是一个特殊的配置文件,它包含了各种自动配置类的全限定类名。Spring Boot在启动时会扫描这个文件,并根据pom文件中的配置信息创建和配置应用程序所需的组件。其实本质上还是读取配置文件,只是加载的方式变了。后续到springboot章节咱细聊。

那么说具体一点,IOC容器到底是怎样实现的呢?用了哪种结构?怎么存储?从哪看?          

IOC容器其实就是一个Map,本身就是一个CurrentHashMap,这里就涉及到三级缓存,后面我们会详细拿出一篇文章进行讲解,这个Map里面存放的是各种对象,包括在Xml里面配置的Bean节点和我们采用注解的方式进行标注的类,在项目启动的时候会读取配置文件里面的Bean节点,根据全限定类名使用反射机制创建对象放到Map里。扫描到带注解的类也是通过反射机制创建对象并存放到Map中,比如一些注解、xml中的bean节点内的ref属性,项目启动的时候会读取xml节点ref属性,并根据ID进行注入,同时对于注解形式,根据类型或者id进行注入,id也就是对象名称且默认按照驼峰首字母小写的形式进行注入。

定义类

整个IOC容器的启动入口在ClassPathXmlApplicationContext的构造方法中的refresh()方法里,其实在每一个容器接口类里都会存在refresh方法,比如AnnotationConfigApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext你都会发现refresh()方法的身影。该方法调用的是父类AbstractApplicationContext的refresh()方法。BeanDefinitionParserDelegate是 BeanDefinition解析委托类,就是专门解析由xml 转成Document的类,Document里面是以 beans 为根节点的Spring配置文件的全部内容。也就是下面这一区域。

关注公众号【可为编程】回复【面试】领取年度最新面试题!!!

图片

BeanDefinition在Spring框架中用于定义Bean的配置元信息。它包含了Bean的类名、设置父bean名称、是否为primary、Bean行为配置信息(如作用域、自动绑定模式、生命周期回调等)、依赖设置、构造参数、属性设置等内容。BeanDefinition可以被看作是对Bean属性和配置信息的抽象,这些信息被存储在Spring容器中,以便在需要的时候创建和管理Bean。通过这样的设计,Spring将Bean的创建和管理逻辑与具体的实现解耦,使得用户可以更加灵活地配置Bean。

然后将Document类进行解析,载入到BeanDefinition,这个过程非常复杂,在这里就不细细展开。其实BeanDefinition说白了就是引入了一个第三方来统一Bean的格式,因为创建Bean我们可能有多种方式,配置文件、注解、配置类等等,他都会转换成同一种格式文件,到这步只是一个定义类,只是将BeanDefinition信息发布到IOC容器中,此时依旧没有对应的Bean实例创建,没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给Bean,那么它还不能完全使用。

初始化

对于初始化和依赖注入,Spring Bean还有一个配置选项lazy-init属性,其含义就是是否初始化SpringBean,在没有任何配置的情况下,它的默认值为default,实际值为false,也就是IOC默认全自动初始化Ben,如果将其设置为tue,那么只有当我们使用SpringIoC容器的getBean方法获取它时,它才会进行Bean的初始化,完成依赖注入。

DI依赖注入

当我们用到这个对象的时候,再通过DI注入。DI注入采用三种方式:

  1. 构造函数注入:这是在对象创建时通过构造函数参数进行的依赖注入。当Spring容器创建一个新的bean实例时,会调用相应的构造函数,将所需的依赖作为参数传递给该构造函数。

  2. Setter方法注入:在这种方式下,依赖注入发生在bean的初始化阶段。首先,Spring容器会调用bean的无参数构造函数创建一个bean实例,然后通过反射调用bean的setter方法,将依赖注入到bean中。

  3. 注解注入:这是通过在字段上使用@Autowired注解进行的依赖注入。这种方式的注入也发生在bean的初始化阶段,与setter方法注入类似。但需要注意的是,字段注入通常不推荐使用,因为它违反了封装的原则,而且可能会导致不可预见的副作用。

需要注意的是,以上的时机都是相对于Spring容器的生命周期来说的。在Spring容器启动并初始化bean时,依赖注入就会发生。具体的时机取决于选择的依赖注入方式(构造函数、setter、字段)以及配置。构造函数注入能够保证所有的依赖在对象创建后就立即可用,并且它们是final的,不会被修改。而setter方法注入则提供了更大的灵活性,可以在运行时动态更改依赖,但它也带来了更大的复杂性。到这终于把IOC容器的流程大致说了一遍。

关注公众号【可为编程】回复【面试】领取年度最新面试题!!!


Bean的概念

为啥叫Bean不叫其他呢,大家可以自行百度哈。由spring容器管理的对象统称为Bean对象。Bean就是普通的java对象,他和我们自己new的对象其实是一样的,只是这些对象是由spring去创建和管理的,我们需要在配置文件中告诉spring容器需要创建哪些bean对象,这就是我们之前说配置文件和注解两种方式,如果采用配置文件,需要先在配置文件中定义好需要创建的bean对象,这些配置统称为bean的元数据配置信息,spring容器通过读取这些bean配置元数据信息来构建和组装我们需要的对象。如果是采用注解的形式,那么spring会扫描指定包下所有带有相关注解的类,并通过AnnotationConfigApplicationContext上下文获取到指定的Bean对象。这里要注意,当我们通过注解的形式进行Bean定义的时候,如果没有指定BeanId,系统会默认通过类名驼峰的形式定义BeanId。这就是Bean的两种加载方式。

@Service
public class KeWeiService {public KeWeiService() {System.out.println("基于注解形式创建正在创建KeWeiService---   " + this);}
}
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(KeWeiService.class);
KeWeiService kw = (KeWeiService) annotationConfigApplicationContext.getBean("keWeiService");
System.out.println(kw);

基于XML的SpringIOC容器初始化步骤

1、引入spring相关的maven配置

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.6.RELEASE</version></dependency>
</dependencies>

2、创建bean配置文件,比如test.xml配置文件

3、在test.xml文件中定义好需要spring容器管理的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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<!--关注公众号【可为编程】回复【面试】领取年度最新面试题!!!-->
<!--构造方法中增加妻子对象-->    <bean class="org.kewei.pojo.Person" id="person"><constructor-arg type="org.kewei.pojo.Wife" ref="wife"/></bean><bean class="org.kewei.pojo.Wife" id="wife" autowire-candidate="true"><property name="age" value="18"/><property name="name" value="可为"/></bean>
</beans>

4、创建spring容器,并给容器指定需要装载的bean配置文件,当spring容器启动之后,会加载这些配置文件,然后创建好配置文件中定义好的bean对象,将这些对象放在容器中以供使用。

public class Main {public static void main(String[] args) {ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");Person person = (Person) classPathXmlApplicationContext.getBean("person");System.out.println(person.getWifeName() + person.getWifeAge());
}

5、通过容器提供的方法获取容器中的对象,然后调用Person中的方法获取Wife的年龄和名字。


IOC容器对象分析

Spring内部提供了很多表示Spring容器的接口和对象,比如BeanFactory接口,ApplicationContext接口,我们来看看比较常见的几个容器接口和具体的实现类。

BeanFactory接口

org.springframework.beans.factory.BeanFactory

spring容器中具有代表性的容器就是BeanFactory接口,这个是spring容器的顶层接口,提供了容器最基本的功能,其他实现类都是基于BeanFactory进行的功能扩展。

常用的几个方法
/按bean的id或者别名查找容器中的bean
Object getBean(String name) throws BeansException
//这个是一个泛型方法,按照bean的id或者别名查找指定类型的bean,返回指定类型的bean对象
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
//返回IOC容器中指定类型的bean对象
<T> T getBean(Class<T> requiredType) throws BeansException;
//获取指定类型bean对象的获取器,这个方法比较特别,以后会专门来讲
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
//判断容器中是否有该Bean
boolean containsBean(String name);
//判断某个Bean对象是否为单例Bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException
//判断某个Bean是否是原型Bean
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
ApplicationContext接口
org.springframework.context.ApplicationContext

这个接口继承了BeanFactory接口,所以内部包含了BeanFactory所有的功能,并且在其上进行了扩展,增加了很多企业级功能,比如AOP、国际化、事件支持等等。

BeanFactory与ApplicationContext有什么区别?                                                               

BeanFactory采用的是延迟加载形式来注入Bean,即只有在使用某个Bean的时候才调用getBean()方法,才对Bean进行加载和实例化。意味着Bean在需要时才进行初始化,而不是在应用启动时立即初始化。这样我们就不会发现一些存在的Spring的配置问题。如果Bean中的某一个属性没有进行注入,当BeanFactory加载后,直到第一次使用调用getBean方法的时候才会抛出异常。

要实现延迟加载,你可以通过在Bean的定义中使用lazy-init属性来配置。例如,在XML配置中,可以这样设置:

<bean id="myBean" class="com.example.MyBean" lazy-init="true"/>

通过设置lazy-init="true",告诉Spring在第一次请求Bean时才初始化它,而不是在应用上下文启动时去加载。一开始我以为是设置了延迟时间吗?诚然不是,这里所说的延迟加载是BeanFactory本身没有默认的延迟时间,只是控制Bean的初始化时机,不涉及具体的延迟时间。

ApplicationContext是在容器启动的时候一次性创建了所有的Bean对象。先根据byType去找再通过byName去找,这样在容器启动的时候就能够发现Spring中存在的错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建, 如使用ContextLoader。

BeanFactory与ApplicationContext都支持BeanPostProcessor与BeanFactoryPostProcessor但两者之间的区别是BeanFactory要手动注册,而ApplicationContext则是自动注册。

图片

Spring中的核心概念

不要称之为卷土重来:为什么 Java 仍然会是冠军!

关于高并发你必须知道的几个概念

线程的创建方式对比与线程池相关原理剖析

BigDecimal对象的日常使用汇总

欢迎大家关注公众号【可为编程】,回复【加群】进入微信群,右边为Q群:761374713,成长,进步,编程,技术、掌握更多知识!

在这里插入图片描述

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

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

相关文章

手动关闭PS中的TopazStudio2的登录窗口

2021 adobe photoshop Topaz Studio 2 不是使用防火墙出站规则&#xff0c;是手动关闭的解决方案 点击社区-切换用户&#xff0c;登录窗口会出现X&#xff0c;可以手动关闭

代码随想录算法训练营第11天|20. 有效的括号 1047. 删除字符串中的所有相邻重复项 150. 逆波兰表达式求值

JAVA代码编写 20. 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括…

【backward解决方案与原理】网络模型在梯度更新时出现变量版本号机制错误

【backward解决方案与原理】网络模型在梯度更新时出现变量版本号机制错误 报错详情 错误产生背景 原理 解决方案 RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation 报错详情 模型在backward时&#xff0c;…

react使用react-sortable-hoc实现拖拽

react-sortable-hoc拖拽 安装 npm install react-sortable-hoc --save 代码如下&#xff08;示例&#xff09;&#xff1a; import React, { useImperativeHandle, forwardRef, memo, useState } from react;import { DrawerForm } from ant-design/pro-form;import { messag…

第一次测试项目该做些什么准备

目录 1、初步进入软件行业的表现和遇到的问题 2、快速融入项目组的普通方法 3、测试人员快速融入项目的非常规方法 一、初步进入软件测试行业的表现和遇到的问题 看到项目模块较多、功能较多&#xff0c;就怕就慌&#xff0c;不知道从什么地方下手理解不了业务&#xff0c;…

【Agent模型1】MemGPT: Towards LLMs as Operating Systems

论文标题&#xff1a;MemGPT: Towards LLMs as Operating Systems 论文作者&#xff1a;Charles Packer, Vivian Fang, Shishir G. Patil, Kevin Lin, Sarah Wooders, Joseph E. Gonzalez (UC Berkeley) 论文原文&#xff1a;https://arxiv.org/abs/2310.08560 论文出处&#x…

快速排序(Java)

基本思想 快速排序Quicksort&#xff09;是对冒泡排序的一种改进。 基本思想是分治的思想&#xff1a;通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数据分别进行快速排…

win10设置windows永不更新

以下方法能全部设置都要全部设置。 方法一&#xff1a;Windows设置 要想关闭Win10自动更新&#xff0c;比较简单的一种方法就是进入到Windows设置中&#xff0c;将Windows更新直接关闭。步骤如下&#xff1a; 1、按“Windows I”键&#xff0c;打开Windows设置&#xff0c;再…

Python语言_single_color_共140种--全平台可用

Python语言_single_color_共140种–全平台可用

JVM运行时数据区-堆

目录 一、堆的核心概述 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;堆空间细分 &#xff08;三&#xff09;jvisualvm工具 二、设置堆内存的大小与OOM 三、年轻代与老年代 四、图解对象分配一般过程 五、对象分配特殊过程 六、常用调优工具 七、Mino…

80个10倍提升Excel技能的ChatGPT提示

你是否厌倦了在使用Excel时感觉像个新手&#xff1f;你是否想将你的技能提升到更高的水平&#xff0c;成为真正的Excel大师&#xff1f;嗯&#xff0c;如果你正在使用ChatGPT&#xff0c;那么成为Excel专家简直易如反掌。 你只需要了解一些最有用的Excel提示&#xff0c;就能在…

Kotlin 进阶函数式编程技巧

Kotlin 进阶函数式编程技巧 Kotlin 简介 软件开发环境不断变化&#xff0c;要求开发人员不仅适应&#xff0c;更要进化。Kotlin 以其简洁的语法和强大的功能迅速成为许多人进化过程中的信赖伙伴。虽然 Kotlin 的初始吸引力可能是它的简洁语法和与 Java 的互操作性&#xff0c…

教你烧录Jetson Orin Nano的ubuntu20.04镜像

Jetson Orin Nano烧录镜像 视频讲解 教你烧录Jetson Orin Nano的ubuntu20.04镜像 1. 下载sdk manager https://developer.nvidia.com/sdk-manager sudo dpkg -i xxxx.deb2. 进入recovery 插上typeC后&#xff0c;短接J14的FORCE_RECOVERY和GND&#xff0c;上电 如下图&#…

基于GB28181-2022实现web无插件播放H265视频

目前发布的GB28181-2022增加了对前端设备视频H265编码格式的支持&#xff0c;所以实现国标平台通过浏览器对H265视频流的无插件的解码播放将是未来的趋势。 目前大多的方案都是通过平台端把H265转码为H264&#xff0c;再推送到web前端进行解码播放&#xff0c;这种方式因为需要…

专访HuggingFace CTO:开源崛起、创业故事和AI民主化丨智源独家

导读 HuggingFace CTO Julien Chaumond认为&#xff0c;在大模型时代&#xff0c;AI民主化至关重要。随着大语言模型和复杂人工智能系统的崛起&#xff0c;持续提升AI技术的可及性有助于确保这些技术的获取和控制不集中在少数强大实体手中。技术民主化促进了机会均等&#xff0…

Java锁常见面试题

图片引用自&#xff1a;不可不说的Java“锁”事 - 美团技术团队 1 java内存模型 java内存模型(JMM)是线程间通信的控制机制。JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存中&#xff0c;每个线程都有一个私有的本地内存&#xff0c;本地内存中存储了该…

c语言从入门到实战——函数递归

函数递归 前言1. 递归是什么&#xff1f;2. 递归的限制条件3. 递归举例3.1 举例1&#xff1a;求n的阶乘3.1.1 分析和代码实现3.1.2 画图推演 3.2 举例2&#xff1a;3.2.1 分析和代码实现3.2.2 画图推演 4. 递归与迭代 前言 函数递归是指一个函数直接或间接地调用自身&#xff…

使用javafx,结合讯飞ai,搞了个ai聊天系统

第一步&#xff1a;先在讯飞ai那边获取接入的api 点进去&#xff0c;然后出现这个页面&#xff1a; 没有的话&#xff0c;就点击免费试用&#xff0c;有了的话&#xff0c;就点击服务管理&#xff1a; 用v2.0的和用3的都行&#xff0c;不过我推荐用2.0版本 文档位置&#xff1…

【每日一题】数组中两个数的最大异或值

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希集合 其他语言python3 写在最后 Tag 【哈希集合】【位运算-异或和】【数组】【2023-11-04】 题目来源 421. 数组中两个数的最大异或值 题目解读 找出数组中两个数的最大异或结果。 解题思路 一看数据量达到了 …

Flink日志采集-ELK可视化实现

一、各组件版本 组件版本Flink1.16.1kafka2.0.0Logstash6.5.4Elasticseach6.3.1Kibana6.3.1 针对按照⽇志⽂件⼤⼩滚动⽣成⽂件的⽅式&#xff0c;可能因为某个错误的问题&#xff0c;需要看好多个⽇志⽂件&#xff0c;还有Flink on Yarn模式提交Flink任务&#xff0c;在任务执…