📑本篇内容: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
这种就是对自动装配原理的最好体现。
💖写在后面💖
每天一点小知识,每天一道小算法
日积月累,无怨无悔
前途之路,定是光明璀璨!
加油! 致敬前进道路上的每位程序猿~