SpringBoot的自动装配原理——其实真得就是你想的那么简单~

📑本篇内容:SpringBoot二刷自动装配原理——其实真得就是你想的那么简单~

📘 文章专栏:SpringBoot微服务 其原理与项目开发

🎬最近更新:2022年1月22日 SpringBoot二刷自动装配原理——其实真得就是你想的那么简单~

🙊个人简介:一只二本院校在读的大三程序猿,本着注重基础,打卡算法,分享技术作为个人的经验总结性的博文博主,虽然可能有时会犯懒,但是还是会坚持下去的,如果你很喜欢博文的话,建议看下面一行~(疯狂暗示QwQ)

🌇点赞 👍 收藏 ⭐留言 📝 一键三连 关爱程序猿,从你我做起

🚀SpringBoot自动装配原理理解以及深入了解🚀

💖写在前面💖

二刷SpringBoot的第一天,还是老样子,把当初一开始恶心了我一天自动装配原理吃的死死的,哼,一日不见,如隔三秋,自动装配原理小儿~纳命来!!!小付今天给大家整理的是关于SpringBoot自动装配原理的相对应的知识理解,以及深入,如何快速理解应对这个面试的始祖题,这一篇绝对够了哦!相信你自己,这一文绝对通俗易懂,等你再遇时,一定信手拈来,妙笔生花

🎁1、SpringBoot自动装配带来的便利

大家都学过SSM框架,给我们的最直观感受就是配置文件太过于繁琐复杂,如果稍有不慎配置出错,你的项目就无法跑起来了,这明显是个很头痛的问题,但是学过了SpringBoot之后,你就会明白一个道理,约定大于配置,只需要对SpringBoot进行简单的配置你的项目就会像SSM框架整合出来的项目一样,随心所欲的跑起来了,并且还能在此基础上配置更多你想配置的依赖功能,那SpringBoot是如何做到只需简单几步就可以构建出以前那样庞大且配置复杂的项目呢?这就不得不提到自动装配原理了,这就是他带来的好处以及便利~

注意事项

  • 本文对SpringBoot自动装配原理的理解是基于SpringBoot-2.6.3最新版本的理解,可能部分源码与某些兄弟姐妹们不太一样,所以如有不大对的地方,希望您能给我做出指出我会积极改正的!感谢阅读哦~

🎠2、SpringBoot的自动装配原理

当你学了如何利用SpringBoot快速搭建一个小的项目并将其作为项目经历写在简历上时(后续博主会快速搭建一个给大家上手的~),面试的HR大大有可能会对你问一些简单的有关于SpringBoot的相关面试题,那么SpringBoot自动装配原理那就可谓是SpringBoot面试题中的始祖了,那我们现在就来看看如何掌握这个让人头痛不已的自动装配原理吧!

本文会从以下两个大方面分析SpringBoot的自动装配原理

  • 第一部分解读pom.xml文件SpringBoot可能会将那些依赖自动装配到IOC容器当中
  • 第二部分就是在SpringBoot程序的主启动类上的注解进行剖析,去了解SpringBoot是如何将写好的配置文件进行自动装配的。

🍨1、SpringBoot程序中的pom.xml文件的解读

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.6.3</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.alascanfu</groupId>
   <artifactId>hello-springboot</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>hello-springboot</name>
   <description>Demo project for Spring Boot</description>
   <properties>
      <java.version>1.8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>

步骤1:拿到源码之后

我们可以先看看源码的组成,并且分析源码与在Spring的Maven项目中的pom.xml有什么异同

注意:在IDEA中我们可以通过Ctrl+鼠标左键,点击对应的类,可进入其源码进行解读。

我们现在来截断分析:

build标签

<build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

这个和我们之前在Spring中配置的build完全不一样(之前我们是在build中配置Maven打包资源的拦截,那些需要打包,那些不打包),且这个的标签为这明显是个插件,见名知意就知道这是Maven项目负责打包SpringBoot程序的一个小插件,走走走,看下一段~


parent标签

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.6.3</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>

这是一个引入父Maven工程的仓库地址,我们Ctrl+左键点击 spring-boot-starter-parent 看看这个仓库配置文件帮我们配置了些啥?

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.6.3</version>
  </parent>

映入眼帘的还是一个父工程依赖,接着进去瞅瞅

<properties>
    <activemq.version>5.16.3</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.93</appengine-sdk.version>
    <artemis.version>2.19.0</artemis.version>
    <aspectj.version>1.9.7</aspectj.version>
    ...
</properties>

你会神奇的发现,这里通过properties配置了很多依赖的版本号,就比如这里的 activemq 消息队列 aspecj AOP相关的,等等都在这里…那差不多你就明白了在 spring-boot-dependencies中配置了很多很多 的依赖,并且已经将其依赖版本 配置好了,如果我们需要导入较低或者较高版本的依赖只需要根据在这里的版本修改版本号就好了~

既然知道这里面的相关配置,那我们接着退回去看看,上一个 spring-boot-starter-parent帮我们配置了其他的什么东西没?

<build>
    <resources>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
          <include>**/application*.yml</include>
          <include>**/application*.yaml</include>
          <include>**/application*.properties</include>
        </includes>
      </resource>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <excludes>
          <exclude>**/application*.yml</exclude>
          <exclude>**/application*.yaml</exclude>
          <exclude>**/application*.properties</exclude>
        </excludes>
      </resource>
    </resources>

眼不眼熟???这就是和Spring 的Maven工程中里pom.xml文件配置的几乎一毛一样,配置打包输出的资源,对yml,yaml,properties等文件都不进行拦截

那这里就差不多了,但是细心的小伙伴们又会发现在 spring-boot-dependencies 中有很多配置文件会爆红,那接下来咱们就在退回到我们SpringBoot程序中的pom.xml文件

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

我们配置了的这个,那么你心中答案就有了,跑到spring-boot-dependencies中看看对应的依赖是否爆红?如果没有爆红就能应征你内心的想法了。答案是肯定的!你看如图所示:

在这里插入图片描述

人家都爆红了就他一个没爆红,这说明什么? 说明他已经被自动装配到咱们的IOC容器当中,能被正常启动使用了。这就是对SpringBoot程序中pom.xml文件的理解与掌握,快速知其原因,知其理,第一部分就差不多是这个样子了,当然我们还要知道为啥导入这个就会被自动装配到简单运行呢?光导入依赖可不能让SpringBoot认认真真跑起来呢,这只是先理解自动装配这个概念

🍨2、@SpringBootApplication理解自动装配原理

打开我们SpringBoot项目的主启动类:你会发现这个主启动类代码不过三行

@SpringBootApplication
public class HelloSpringbootApplication {
	public static void main(String[] args) {
		SpringApplication.run(HelloSpringbootApplication.class, args);
	}
}

如果把@SpringBootApplication这行删除了就无法正常启动了,那咱们就来了解一下@SpringBootApplication这个注解具体的功能是干啥的吧~

在这里插入图片描述

@SpringBootApplication注解分析大概从上述Xmind图来进行分析,因为小付的Xmind暂未破解所以有水印稍微担待一点~

🥚1、@SpringBootApplication

开始对@SpringBootApplication进行剖析

Ctrl+左键点击@SpringBootApplication咱们进去看看这玩意写了些啥~

映入眼帘的除了四个原生注解之外,就是这四个注解了。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootApplication概述

  • 这是一个组合注解 分别由 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个重要注解所组成。
  • 目前来说咱还不知道他能干嘛,不过可以肯定的是它是SpringBoot程序的主程序启动注解

接下来接着看来总结这个@SpringBootApplication注解有啥用喽~

🥚2、@SpringBootConfiguration
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

映入眼帘除却四个原生注解就是这俩孩子了!

**第一个注解,眼熟么?眼熟么?**这不就是纯纯的Spring注解一个作为配置组件注入到IOC容器的注解么。

@Configuration进去就是一个@Component组件。所以就不再细讲了。

第二个注解@Indexed,应该很多人不认识,毕竟这是Spring5的新产物,其实小付对它也不是很了解,但是经过查阅,得知:它是一个用于给模式注解添加索引,来以此提升启动性能的组件。

那么到此为止@SpringBootConfiguration就了解了塞就是使得@SpringBootApplication 作为一个Spring的配置自动注入到IOC容器当中喽。

🥚3、@ComponentScan

先分析这个简单一点的注解:你只需要这个注解是用于扫描当前SpringBoot程序启动类同级所处的包下所有的注解程序。至于为什么是同级目录所处的包下,这也在源码中有所体现,这里就不带大家看了。

✨4、@EnableAutoConfiguration(重点!!!)✨

既然都知道上述两个注解并没实际将具体的依赖注入启动,那肯定使得SpringBoot实现自动装配最主要的东西肯定非他莫属了呀~

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@EnableAutoConfiguration:

  • 毫无疑问这也是一个组合注解:它是由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})所组成。
  • 它有什么用呢?目前为止我们通过这段源码只能看得出这个注解将AutoConfigurationImportSelector.class叫做自动装配导入选择器的类注入到IOC容器了,这个类有啥用?咱们等会儿讲。
  • 那么@AutoConfigurationPackage又是干嘛的呢?不知道往里进就是了
🎗1、@AutoConfigurationPackage
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}
  • 由源码咱们得知:该注解就干了一件事情,就是将Registrar.class这个类注入到IOC容器当中了,那么我们必须要明白这个 叫做注册的类干了些啥!!!
  • 神奇的是你点击进去之后,这个注册类Registrar.class实际是AutoConfigurationPackages.java的静态内部类。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
		//导入@ComponentScan的包中数据进行自动注册
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }
		//确定数据导入正确性
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }
  • 这个注册类用于读取从@ComponentScan读取的包中元数据AnnotationMetadata metadata进行自动注册。

总结就是@AutoConfigurationPackage这个注解是用于读取@ComponentScan中通过注解注册的Java程序的元数据用来进行自动注册的

🎗2、AutoConfigurationImportSelector.class
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

这是这个类中的第一个方法,用于选择导入的数据,根据元数据annotationMetadata进行导入~

  • 如果不支持元数据导入,就直接返回空的导入。
  • 反之调用类中的getAutoConfigurationEntry获取自动装配实体的这个方法

这个方法中有这么一个又调用了List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)方法
在这里插入图片描述

上述调用List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);方法主要目的是为了获取所有候选配置


getCandidateConfigurations(annotationMetadata, attributes)

进入源码看其到底干了个啥?

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

loadFactoryNames这个方法返回的是一个Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)

在这里插入图片描述

可以看到这里有一个枚举类进行读取/META-INF/spring.factories的资源路径,那到底这个路径存了些啥东西呢?

🎉3、/META-INF/spring.factories🎉

1、这个就是SpringBoot帮我们自动配置的文件,其目录位置在spring-boot-autoconfigure-2.6.3.jar下的META-INF中的spring.factories
2、你可以在其中找到所有SpringBoot自动帮我们配置好的文件自动配置类

在这里插入图片描述

✨5、xxxxxAutoConfiguration✨

重点:至于每个自动装配类是否全都被自动配置进行装配到IOC容器呢?怎么可能嘛,这里又和上面的pom.xml文件进行了联系,你这下肯定就懂了塞~,那SpringBoot又是如何区分哪些装配了,哪些没有装配呢?

这就又要来看这些自动装配类是如何进行书写的啦~

这里咱们用咱们最常用的DataSourceAutoConfiguration来举例子

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(
    type = {"io.r2dbc.spi.ConnectionFactory"}
)
@AutoConfigureBefore({SqlInitializationAutoConfiguration.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, InitializationSpecificCredentialsDataSourceInitializationConfiguration.class, SharedCredentialsDataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
}

你可以看到咱们很多不认识的注解,但都基本上可以见名知意

比如:

  • ConditionalOnClass:这代表的就是在IOC容器中必须要存在这两个class才能被自动装配
  • ConditionalOnMissingBean:代表的就是必须在IOC中没有这样的组件类型才可以装配
  • AutoConfigureBefore:代表的是必须在 SqlInitializationAutoConfiguration自动装配之前完成装配
  • EnableConfigurationProperties:支持配置的Properties配置文件的Java类型文件,在yaml中或者properties进行配置的属性。

这里不理解你就可以进DataSourceProperties.class去看看你就知道这玩意是干啥的了

这样整个SpringBoot的自动装配原理就彻底了解了,但是实际启动的run方法,如何启动,就要另说了哦~

🧨3、总结SpringBoot自动装配原理🧨

SpringBoot程序在启动的时候会通过@EnableAutoConfiguration注解找到 spring-boot-autoconfigure.jar下的META-INF/spring.factories文件中的所有自动配置类,并将其加载,这些被找到的自动配置类是以xxxAutoConfiguration形式来命名的。简单理解这个就是JavaConfig自动配置的文件,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性,如spring.datasource.url=xxxxxxx这种就是对自动装配原理的最好体现。

💖写在后面💖

每天一点小知识,每天一道小算法

日积月累,无怨无悔

前途之路,定是光明璀璨!

加油! 致敬前进道路上的每位程序猿~

在这里插入图片描述

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alascanfu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值