Spring-IOC容器深度剖析详解

news2024/11/26 18:38:18

🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟
🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵
🍉本篇简介:🍉 本篇由表及里分析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/beans
        https://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.coloradmin.cn/o/1173201.html

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

相关文章

LeetCode算法心得——找到冠军(反向推理)

大家好&#xff0c;我是晴天学长&#xff0c;今天的周赛第二题&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .找到冠军 一场比赛中共有 n 支队伍&#xff0c;按从 0 到 n - 1 编号。每支队伍也是 有向无环图&#xff08;DAG&#xff09; 上的…

Technology Strategy Pattern 学习笔记2-Creating the Strategy-World Context

Creating the Strategy-World Context 1 PESTEL 1.1 从6个方案看外部 PoliticalEconomicSocialTechnologicalEnvironmentalLegal 1.2 参考URL https://zhuanlan.zhihu.com/p/192522082https://www.docin.com/p-449396129.htmlhttps://blog.csdn.net/xiaoyw71/article/deta…

发现一款PDF转换成翻页电子书的网站

​随着科技的发展&#xff0c;电子书越来越受到人们的喜爱。而PDF格式的文件也越来越多地被人们使用。那么&#xff0c;如何将PDF文件转换成翻页电子书呢&#xff1f;今天就为大家推荐一款好用的PDF转翻页电子书网站。 一、网站介绍 这款网站是一款非常实用的在线转换工具&…

7.spark sql编程

概述 spark 版本为 3.2.4&#xff0c;注意 RDD 转 DataFrame 的代码出现的问题及解决方案 本文目标如下&#xff1a; RDD ,Datasets,DataFrames 之间的区别入门 SparkSession创建 DataFramesDataFrame 操作编程方式运行 sql 查询创建 DatasetsDataFrames 与 RDDs 互相转换 使用…

Navicat连接mysql 8.0.35 2059错误解决办法

这2天在家重装电脑&#xff0c;顺便把mysql升级8.0&#xff0c;安装完成后&#xff0c;用Navicat连接&#xff0c;报错2059&#xff0c;如下 网上查了一下&#xff0c; 【报错原因】mysql8.0 之前的版本中加密规则是 mysql_native_password&#xff0c;而 mysql8.0 之后的版本…

超越 GLIP! | RegionSpot: 识别一切区域,多模态融合的开放世界物体识别新方法

本文的主题是多模态融合和图文理解&#xff0c;文中提出了一种名为RegionSpot的新颖区域识别架构&#xff0c;旨在解决计算机视觉中的一个关键问题&#xff1a;理解无约束图像中的各个区域或patch的语义。这在开放世界目标检测等领域是一个具有挑战性的任务。 关于这一块&…

Vim快速插入常用代码模板

1 修改home目录下.vimrc 家目录中ls -a找到隐藏文件.vimrc 2 编辑.vimrc 输入i编辑&#xff0c;在尾巴插入代码&#xff0c;按:wq保存并退出。 noremap io i#include <stdio.h><Esc>o<Esc> noremap im iint main(int argc, char *argv[])<Esc> map …

使用自定义函数拟合辨识HPPC工况下的电池数据(适用于一阶RC、二阶RC等电池模型)

该程序可以离线辨识HPPC工况下的电池数据&#xff0c;只需要批量导入不同SOC所对应的脉冲电流电压数据&#xff0c;就可以瞬间获得SOC为[100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0%]的所有电池参数,迅速得到参数辨识的结果并具有更高的精度&#xff0c;可以很大程度上降低参…

第7章-使用统计方法进行变量有效性测试-7.1-假设检验

目录 女士品茶 假设检验 样本与总体 原假设与备择假设 检验法、拒绝域与检验统计量 显著性水平 决策方法——临界值法和p值&#xff08;p-value&#xff09;法 假设检验步骤 参考文献 假设检验&#xff0c;我们从女士品茶这个故事开始说起。希望这篇文章能给您带来极大…

三、操作系统

&#xff08;一&#xff09;概述 操作系统是管理整个系统的软、硬件资源的系统&#xff0c;既是人和硬件之间的一种接口&#xff0c;也是应用软件与硬件之间的接口。 &#xff08;二&#xff09;进程管理 1.进程的状态 进程的状态是操作系统对进程进行管理的时候设置的几种状…

CLion2022安装

1. CLion下载 地址&#xff1a;https://www.jetbrains.com.cn/clion/download/other.html 下载你需要的版本&#xff0c;这里以2022.2.4为例 之后获取到对应的安装包 2. 安装 1、双击运行安装包&#xff0c;next 2、选择安装路径&#xff0c;建议非系统盘&#xff0c;nex…

Jetpack:029-Jetpack中的网格布局

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了Jetpack中Card相关的内容&#xff0c;本章回中主要介 网格布局。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01; 1. 概念介绍 我们在本章回中…

第九章《搞懂算法:决策树是怎么回事》笔记

决策树算法是机器学习中很经典的一个算法&#xff0c;它既可以作为分类算法&#xff0c;也可以作为回归算法。 9.1 典型的决策树是什么样的 决策树算法是依据“分而治之”的思想&#xff0c;每次根据某属性的值对样本进行分类&#xff0c;然后传递给下个属性继续进行分类判断…

【CMU15445】Fall 2019, Project 3: Query Execution 实验记录

目录 实验准备实验测试Task 1: CREATING A CATALOG TABLE SQL 执行是由数据库解析器转化为一个由多个 executor 组成的 Query Plan 来完成的&#xff0c;本实验选择了火山模型来完成 query execution&#xff0c;这一次的 project 就是实现各种 exeutor&#xff0c;从而可以通过…

2014年亚太杯APMCM数学建模大赛C题公共基础课教师专业化培养方式研究求解全过程文档及程序

2014年亚太杯APMCM数学建模大赛 C题 公共基础课教师专业化培养方式研究 原题再现 近年来&#xff0c;世界基础工业、信息产业、服务业的跨越式发展引发了大量人才需求&#xff0c;导致了职业教育的飞速发展&#xff0c;除原有专科层次高等职业教育院校外&#xff0c;大量普通…

行业安卓主板-基于RK3568/3288/3588的电子班牌/人脸识别门禁/室内对讲门禁方案解决方案(二)

电子班牌 智能电子班牌可在主页实时显示班级全面的基本信息&#xff0c;包括天气、班名、课程表、值日表等&#xff0c;并发布学校通知、班级通知。学生可刷卡自动登陆系统进行课堂反馈&#xff0c;教师和家长可及时了解教学反馈&#xff0c;打通学校、教师、学生之间的互动通…

逆向学习记录(2)windows常用基本操作及用环境变量配置上多个python版本

1、如何打开cmd 第一种方法&#xff1a;按下winr&#xff0c;运行cmd 第二种方法&#xff1a;进入一个目录&#xff0c;点击路径处&#xff08;显示蓝色背景&#xff09;&#xff0c;然后直接键盘输入cmd&#xff0c;回车&#xff0c;运行cmd并直接进入此目录。 2、命令dir&am…

OpenFeign 的超时重试机制以及底层实现原理

目录 1. 什么是 OpenFeign&#xff1f; 2. OpenFeign 的功能升级 3. OpenFeign 内置的超时重试机制 3.1 配置超时重试 3.2 覆盖 Retryer 对象 4. 自定义超时重试机制 4.1 为什么需要自定义超时重试机制 4.2 如何自定义超时重试机制 5. OpenFeign 超时重试的底层原理 5…

04-附注 三维空间中的线性变换

附注 三维空间中的线性变换 三维空间线性变换 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 三维空间线性变换 图1 绕y轴旋转90 绕y轴旋转90后&#xff0c;各基向量所在的坐标如图1所示。用旋转后的各基向量作为矩阵的列&#xff0c;就得到变换矩阵。变换矩阵…