一、开篇

  在平时的开发过程中用的最多的莫属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,

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

2.2、springboot读取spring.facotries文件(可跳过该节)

  前边说到今天的主角是”mybatis-spring-boot-autoconfigure“,其实还有很多这样的依赖,大多数第三方自己实现的都会有这样一个依赖比如,前边自己实现的starter中就有这样一个”customer-spring-boot-autoconfigurer“,还有很多都是springboot自己实现的,所以无需这样的依赖。

  要想知道springboot是如何进行自动注入的,唯一的方式是debug,现在开始debug之旅吧。

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“指的是,

/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

  jar下的”META-INF/spring.factories“文件,也就是说要读取项目中jar包中的”META-INF/spring.factories“文件的内容,我在spring-boot-2.3.3.RELEASE.jar中找到这样一个文件,仅截个图,详细内容可以自己查看,

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

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

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

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

2.2.3、setListeners()方法

  该方法和setInitializers方法是类似的,

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

  本人核实过这些的确是从spring.factories文件中读取的,和其内容是一致的。

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

2.3、自动注入的原理

2.3.1、@SpringBootApplication注解  

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

package com.my.template;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
* @date 2022/6/3 21:32
*/
@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()方法,

/**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
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注解使用;

欢迎转发、关注。

推荐阅读

我的第一个springboot starter

springboot引入mybatis遇到的坑

springboot多环境下如何进行动态配置

做了这些年开发,今天第一次梳理了这三种常用的变量

深入理解springboot的自动注入的更多相关文章

  1. 正确理解springboot的常用注入方式

    springboot的属性注入 以注入dataSource为例1.springboot默认读取的文件是放在resources目录下的名为application.properties或applicati ...

  2. 深入理解SpringBoot之自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码 ...

  3. springboot 整合dubbo 消费模块引入springboot 之后自动注入jdbc 模块导致启动报错问题

    方案一: 排除方法 pom文件直接将数据起步模块内排除数据源自动注入 jdbc jar <!--mybatis-plus 集成 --><!--mybitis--><dep ...

  4. 数据库连接不上的原因以及springBoot的ioc无法自动注入

    无法自动注入解决了,数据池的连接还有问题: 错误原因1: :数据库用的是Mysql8版本,以前的配置mysql驱动包却是5.1.37版本.只需修改驱动包为8.0.11版本即可. <!-- mys ...

  5. 关于springboot项目中自动注入,但是用的时候值为空的BUG

    最近想做一些web项目来填充下业余时间,首先想到了使用springboot框架,毕竟方便 快捷 首先:去这里 http://start.spring.io/ 直接构建了一个springboot初始化的 ...

  6. SpringBoot系列: 理解 Spring 的依赖注入(一)

    ==============================Spring 的依赖注入==============================对于 Spring 程序, Spring 框架为我们提供 ...

  7. 【SpringBoot】SpringBoot热部署和配置文件自动注入实战

    ========================3.SpringBoot热部署devtool和配置文件自动注入实战 ============================ 1.SpringBoot2 ...

  8. SpringBoot自动注入分析

    我们经常会被问到这么一个问题:SpringBoot相对于spring有哪些优势呢?其中有一条答案就是SpringBoot自动注入.那么自动注入的原理是什么呢?我们进行如下分析. 1:首先我们分析项目的 ...

  9. 原创001 | 搭上SpringBoot自动注入源码分析专车

    前言 如果这是你第二次看到师长的文章,说明你在觊觎我的美色!O(∩_∩)O哈哈~ 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 本系列为SpringBoot深度源码专车系列,第一篇发车 ...

随机推荐

  1. float,short类型赋值运算问题

    float f = 3.4; 有错吗? 有错,因为浮点类型默认是double类型,double类型赋值给float类型是大类型赋值给小类型需要进行强转,可在3.4前加(float)进行强转,或者在声明 ...

  2. Java的虚拟线程(协程)特性开启预览阶段,多线程开发的难度将大大降低

    高并发.多线程一直是Java编程中的难点,也是面试题中的要点.Java开发者也一直在尝试使用多线程来解决应用服务器的并发问题.但是多线程并不容易,为此一个新的技术出现了,这就是虚拟线程. 传统多线程的 ...

  3. 【图文教学】如何轻松下载tiktok上的视频

    我是真诚的想和大家分享这个办法的!这个叫光影存图,图标就是这个绿色的箭头,我是苹果手机,安卓手机也可以用,就是它分免费版和会员版的,没有强制要你充会员,看一下广告就可以获取下载次数,我就是这么干的,只 ...

  4. JavaWeb学习day8-Response排错

    用字符串接收送前端返回的数据并输出 1 req.setCharacterEncoding("UTF-8"); 2 resp.setCharacterEncoding("U ...

  5. OpenHarmony 3.1 Beta 版本关键特性解析——ArkUI canvas组件

    (以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 江英杰 华为技术有限公司 canvas 是 ArkUI 开发框架里的画布组件,常用于自定义绘制图形.因为其轻量.灵活. ...

  6. 【直播回顾】OpenHarmony知识赋能第四期第四课——音频驱动开发

    3月31日晚上19点,知识赋能第四期直播的第四节,也是本期最后一节直播课​<OpenHarmony标准系统HDF框架之音频驱动开发>​,在OpenHarmony开发者成长计划社群内成功举办 ...

  7. 可怕!CPU暗藏了这些未公开的指令!

    大家好,我是轩辕. 我们知道,我们平时编程写的高级语言,是经过编译器编译以后,变成了CPU可以执行的机器指令: 而CPU能支持的指令,都在它的指令集里面了. 很久以来,我都在思考一个问题: CPU有没 ...

  8. 『现学现忘』Git基础 — 6、Git的操作流程

    目录 1.Git的基本操作流程 2.工作区.暂存区.版本库的区别 (1)工作区 (2)版本库 (3)暂存区 (4)通过新增文件理解三个区的关系 (5)说明 1.Git的基本操作流程 初始化一个本地版本 ...

  9. android软件简约记账app开发day02-收入支出明细页面绘制

    android软件简约记账app开发day02-收入支出明细页面绘制 效果图 列表界面绘制 新建layout文件-item_mainlv.xml大体使用绝对布局,嵌套相对布局,嵌套文本内容实现 < ...

  10. 从零开始搭建GoLang语言开发环境

    更多干货文章,更多最新文章,欢迎到作者主博客 菜鸟厚非 一.安装 GoLang 1.1 下载 首先访问 https://go.dev/dl/ 下载 GoLang,下载完成后双击安装即可.如下: 1.2 ...