Springboot的自动注入

news2024/10/5 19:09:16

一、开篇

 在平时的开发过程中用的最多的莫属springboot了,都知道springboot中有自动注入的功能,在面试过程中也会问到自动注入,你知道自动注入是怎么回事吗,springboot是如何做到自动注入的,自动注入背后的原理是什么,今天来分析下springboot的自动注入,希望这篇文章可以解除大家心中的疑惑。

二、详述

2.1、什么是自动注入

天天将自动注入,你真正明白自动注入是怎么回事吗?举个例子来说,我们要在springboot中使用mybatis,之前的做法是什么?

1、引入依赖;

2、在配置文件中配置配置类;

3、写mybatis的配置文件或注解;

在springboot中这个步骤就减少了,减少的是第二步,不用再写一堆配置类了,步骤简化为:

1、引入依赖;

2、写mybatis的配置文件或注解;

也就是说无需再搞配置类了,就比如之前的”SqlSessionFactoryBean“,现在不用配置了,springboot为我们做了这些工作,现在看springboot引入mybatis需要加入的依赖,

<!--mybatis的依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>

        </dependency>

        <!--mysql的驱动程序-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>

我们加入mybatis和数据库的驱动依赖,因为mybatis要使用数据库连接,所以这里少不了mysql的数据库驱动。重点看mybatis的这个依赖和之前的是不一样的,这个是”mybatis-spring-boot-starter“,再看这个依赖中都有哪些jar

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
    </dependency>
  </dependencies>

除了常见的mybatis及mybatis-spring还有一个mybatis-spring-boot-autoconfigure,这个就是今天的主角。

2.2、springboot读取spring.facotries文件

2.2.1、SpringApplication构造方法

springboot的启动很简单,就是下面这样一行代码

SpringApplication.run(BootServer.class);

 要跟着这样一行代码走下去,追踪到了这样一句

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

可以看的会new一个SpringApplication的实例,然后再调用其run方法,先看下new方法做了什么,最终调用的是下面的构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
         //重要-设置初始化器
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
         //重要-设置监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

我在上面 做了注释,重点看注释部分的代码

2.2.2、setInitializers()方法

该方法从方法名上看是要设置初始化器,其中getSpringFactoriesInstances(ApplicationContextInitializer.class)是重点。其方法定义如下

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
        //SpringFactoriesLoader.loadFactoryNames是重点
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

看SpringFactoriesLoader.loadFactoryNames方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
         //loadSpringFactories(classLoader)方法是重点
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

把断点放在loadSpringFactroies方法内

从上面的debug结果可以看到使用AppClassLoader读取FACTORIES_RESOURCE_LOCATION“处的资源,AppClassLoader大家都很熟悉,就说应用类加载器,常量FACTORIES_RESOURCE_LOCATION“指的是

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

文件内容如下

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

可以看到是一些列的键值对,我们看下loadSpringFactories方法最后的返回值

这个返回值是,项目中所有jar下META-INF/spring.factories文件中的键值对组成的map。回到loadFactoryNames方法处 

 该方法需要的是key为”org.springframework.context.ApplicationContextInitializer“的value,该value的值有这样8个

 对应的配置文件内容

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

 这里只有5个, 前三个应该是系统配置

这样我们把setInitializers方法就分析完了,其主要就是从jar包中的META-INF/spring.factories文件中获取org.springframework.context.ApplicationContextInitializer对应的值。下面看setListeners方法

2.2.3、setListeners()方法

该方法和setInitializers方法是类似的

重点是其参数不一样,该方法的参数是ApplicationListener.class,也就是要找出org.springframework.context.ApplicationListener在spring.factories中的配置

 文件内容如下:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

和断点配置一样, 前三个可能是系统配置

写到这里其实和自动注入没有关系,如果说有关系的话是,这里认识了一个关键的类”SpringFactoriesLoader“,该类的作用就是读取jar包中META-INF/spring.facotries文件的内容。在后边的自动注入中还会出现该类的影子。继续向前。

2.3、自动注入的原理

2.3.1、@SpringBootApplication注解

在启动springboot程序的时候在程序的入口都会有写上@SpringBootApplication的注解 

@SpringBootApplication
public class BootServer {
    public static void main(String[] args) {
        try {
            SpringApplication.run(BootServer.class);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 在该注解上还有@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解,今天重点看@EnableAutoConfiguration注解。

2.3.2、@EnableAutoConfiguration注解

 该注解便是自动注入的核心注解

  重点是该注解上的下面这句话

@Import(AutoConfigurationImportSelector.class)

看下AutoConfigurationImportSelector类,该类中有这样一个方法,和自动注入是相关的

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

很属性的SpringFactoriesLoader类又出现了,还是很熟悉的loadFactoryNames方法,这次的方法参数是getSpringFactoriesLoaderFactoryClass()方法

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

 所以SpringFactoriesLoader.loadFactoryNames是要从META-INF/spring.factories中获取key为”org.springframework.boot.autoconfigure.EnableAutoConfiguration“的value,这里可以看到有很多,从中还可以找到我自定义的和myatis的

 也就是说要把这些配置类加到spring的容器中。现在有个问题这些配置都会生效吗?

2.3.3、这些配置类都会生效吗?

上面说到自动配置会加载很多的配置类,但是这些类都会生效吗?答案是不会的,只会在特定情况下生效,以MybatisAutoConfiguration为例

可以看的该类上有很多注解:

  @ConditionalOnClass,当类路径中存在某个类标识该注解的类才会生效,也就是只有存在SqlSessionFactory、SqlSessionFactoryBean才会解析MybatisAutoConfiguration类。换句话说,要有mybatis、mybatis-spring的jar包。

  @ConditionaleOnSigleCanidate,需要一个单例bean

  @EnableConfigurationProperties  读取配置文件,也就是application.properites

  @AutoConfigureAfter  自动配置在某个类之后

现在我们知道了一个XXAutoConfiguration类是否会生效还要看其上面的注解是怎么定义的。


三、总结

  本文主要分析了springboot的自动注入原理,

  1、注解@SpringBootApplication中含有三个注解,其中@EnabelAutoConfiguration和自动配置有关;

  2、@EnableAutoConfiguration会读取所有jar下META-INF/spring.factories文件的内容,获取”org.springframework.boot.autoconfigure.EnableAutoConfiguration“的配置,把这些配置注入到容器;

  3、@EnableAutoConfiguration注入的类是否生效,需要看其上面的注解,主要配合@ConditionaleXXX注解使用;

本文参考: https://www.cnblogs.com/teach/p/16411991.html

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

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

相关文章

基于 RocketMQ Connect 构建数据流转处理平台

作者&#xff1a;周波&#xff0c;阿里云智能高级开发工程师&#xff0c; Apache RocketMQ Committer 01 从问题中来的 RocketMQ Connect 在电商系统、金融系统及物流系统&#xff0c;我们经常可以看到 RocketMQ 的身影。原因不难理解&#xff0c;随着数字化转型范围的扩大及…

Linux学习笔记——多线程

文章目录补充知识Linux线程概念线程的优点线程的缺点线程异常线程用途多进程的应用场景Linux进程VS线程重新理解进程进程和线程线程共享的进程资源和环境为什么线程切换的成本更低进程和线程的关系Linux线程控制POSIX线程库创建线程线程ID及进程地址空间布局线程终止线程等待线…

pycharm常用的插件

pycharm等IDE 无法编辑bat文件 Chinese Language Pack 汉化插件 vim CodeGlance 右侧地图 将类似于Sublime中的代码小地图嵌入到编辑器窗格中。使用自定义颜色进行语法高亮&#xff0c;同时使用明暗主题。 Rainbow Brackets&#xff08;必备推荐&#xff09;收费了 这个插件…

【数据库原理 • 六】数据库备份与恢复

前言 数据库技术是计算机科学技术中发展最快&#xff0c;应用最广的技术之一&#xff0c;它是专门研究如何科学的组织和存储数据&#xff0c;如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进&#xff0c;最常用的技术。 当前…

Baumer工业相机堡盟工业相机中预处理相机的特性优势以及行业应用

Baumer工业相机堡盟工业相机如何通过BGAPISDK里显示彩色相机和黑白相机的图像&#xff08;C#&#xff09;Baumer工业相机Baumer工业相机的预处理相机的技术背景Baumer工业相机中预处理相机的特性Baumer工业相机中图像压缩相机的特性Baumer工业相机中3D激光相机的特性Baumer工业…

ESP32设备驱动-MAX30102脉搏血氧饱和度和心率监测传感器驱动

MAX30102脉搏血氧饱和度和心率监测传感器驱动 文章目录 MAX30102脉搏血氧饱和度和心率监测传感器驱动1、MAX30102介绍2、硬件准备3、软件准备4、驱动实现1、MAX30102介绍 MAX30102是一款集成脉搏血氧饱和度和心率监测生物传感器模块。 它包括内部 LED、光电探测器、光学元件和…

网页滚动体验,IScroll滚动插件,你安装了类似的滚动页面插件吗

IScroll是一款基于JavaScript的插件&#xff0c;用于在网页中实现平滑滚动效果。 这个插件可以帮助用户创建回到页面顶部和底部的按钮、生成页面导航快照&#xff0c;以及设置滚动时间等功能&#xff0c;从而提升网页的用户体验。 IScroll的特点在于&#xff0c;它能够平滑地…

【15】数据操作reshape、张量的运算

1. N维数组 ① 机器学习用的最多的是N维数组&#xff0c;N维数组是机器学习和神经网络的主要数据结构。 2. 创建数组 ① 创建数组需要&#xff1a;形状、数据类型、元素值。 3. 访问元素 ① 可以根据切片&#xff0c;或者间隔步长访问元素。 ② [::3,::2]是每隔3行、2列访问…

考研数据结构--线性表

线性表 文章目录线性表概述线性表的特点线性表的基本操作线性表的顺序表示概述优缺点操作顺序表的定义顺序表的初始化顺序表的插入顺序表的删除顺序表的查找顺序表的输出顺序表的判空顺序表的销毁main方法测试线性表的链式表示概述优缺点单链表操作单链表的定义单链表的初始化单…

自动化面试题3

1、PLC输入输出如何接线&#xff0c;源性漏型如何看&#xff1f; 所谓“源型输入”&#xff0c;是指电流从模块的公共端流入&#xff0c;从模块的输入通道流出的接线方式。源型输入的公共端作为电源正极&#xff08;共阳极&#xff09;。源型输入可以等效为在输入模块外部连接…

C# | 上位机开发新手指南(九)加密算法——RSA

上位机开发新手指南&#xff08;九&#xff09;加密算法——RSA 文章目录上位机开发新手指南&#xff08;九&#xff09;加密算法——RSA前言RSA的特性非对称性安全性可逆性签名速度较慢密钥管理RSA算法的参数公钥公钥指数e模数n私钥私钥指数d模数n质数p和qdp和dqqInv质数填充方…

性能测试,python 内存分析工具 -memray

Memray是一个由彭博社开发的、开源内存剖析器&#xff1b;开源一个多月&#xff0c;已经收获了超8.4k的star&#xff0c;是名副其实的明星项目。今天我们就给大家来推荐这款python内存分析神器。 Memray可以跟踪python代码、本机扩展模块和python解释器本身中内存分配&#xf…

yolov5-v7.0实例分割快速体验

简介 &#x1f680;yolov5-v7.0版本正式发布&#xff0c;本次更新的v7.0则是全面的大版本升级&#xff0c;最主要的功能就是全面集成支持了实例分割&#xff0c;yolov5已经集成检测、分类、分割任务。 前面几篇文章已经介绍过关于Yolov5的一些方面 yolov5目标检测:https://bl…

[计算机图形学]几何:曲线和曲面(前瞻预习/复习回顾)

一、曲线 1.Bzier Curves—贝塞尔曲线 贝塞尔曲线也是一种显式的几何表示方法。贝塞尔曲线定义了一系列的控制点&#xff0c;致使确定满足这些控制点关系的唯一一条曲线&#xff1a;如上图定义的贝塞尔曲线满足 起始点为p0&#xff0c;结束点为p3&#xff0c;起始点的切线方向…

ABB机械臂和RobotStudio编程简介

ABB机械臂和RobotStudio编程简介机械臂ABB机械臂ABB示教器RobotStudio与编程简介RobotStudio简介与安装RobotStudio使用RAPID程序指令机械臂 一种能够进行编程并在自动控制下执行某些操作和 移动作业任务的机械装置。 —— 美国国家标准局(NSB) 一种用于移动各种材料、零部件、…

基于目标级联法的微网群多主体分布式优化调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

家政服务小程序实战开发教程017-我的页面未注册功能开发

顾客在家政服务小程序可以在线提交预约信息&#xff0c;预约成功后可以查看订单的进度。我们本篇就来实现一下我的页面的功能。 1 新建页面 进入编辑器&#xff0c;在组件页面区域点击号&#xff0c;创建我的页面 2 未登录页面开发 我的页面的逻辑是如果用户未注册&#…

【MyBatisPlus】一文带你快速上手MyBatisPlus

文章目录MyBatisPlus学习笔记前言1、MyBatisPlus概述2、快速体验3、CRUD接口3.1 Mapper层CRUD接口3.1.1 Insert3.1.2 Delete3.1.3 Update3.1.4 Select3.2 Service层CRUD接口3.2.1 Save3.2.2 Remove3.2.3 Update3.2.4 Get3.3 自定义SQL接口4、常用注解和配置4.1 TableId4.2 Tabl…

【C++】模板进阶(非类型模板参数、类模板的特化和模板的分离编译)

之前我们讲解过模板初阶&#xff0c;没有阅读过的童鞋可以先去阅读之前的博文----->模板初阶 本章我们将针对模板进行进一步的讲解。 目录 &#xff08;一&#xff09;非类型模板参数 &#xff08;二&#xff09;模板的特化 &#xff08;1&#xff09;概念 &#xff0…

我的面试八股(Java集合篇)

Java集合 两个抽象接口派生&#xff1a;一个是Collection接口,存放单一元素&#xff1b;一个是Map接口存放键值对。 Vector为什么是线程安全 简单&#xff0c;因为官方在可能涉及到线程不安全的操作都进行了synchronized操作&#xff0c;就自身源码就给你加了把锁。 Vector…