首页>>后端>>SpringBoot->为什么SpringBoot什么都不需要配置就可以运行起来?

为什么SpringBoot什么都不需要配置就可以运行起来?

时间:2023-11-30 本站 点击:0

写在前面

SpringBoot毋庸置疑是目前流行度最高的框架。而这原因,即是大家最清楚的0配置化,不需要配置便可以启动一个tomcat服务。

我相信,只要你用过Spring Boot,就会对这样一个现象非常的好奇:

引入一个组件依赖,加个配置,这个组件就生效了。

为了故事的发展,我们举个例子来说,比如我们常用的Redis, 在Spring Boot中就是这样使用的

引入依赖

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

配置yml文件

spring:redis:host:127.0.0.1port:6379password:123456

这就配置OK了,可以直接引用了,神奇嘛

@AutowiredprivateRedisTemplateredisTemplate;

就这样,两步搞定,中间没有做任何事情。这里面我们应该提出几个问题。

第一,pom文件redis是在哪里,怎么连版本号都没有,reids版本是什么

第二,RedisTemplate 是怎么交给Spring注入管理的,怎么就能用了呢。

不知道你们刚接触Boot的时候,有没有听说过约定大于配置。我们要明白一个道理,事情总要有人做,自己不做,肯定是别人帮着做了,我们带着这些疑问去深入看源码。

SPI

先不着急,讲流程源码之前,我们先来学习下SPI,这个非常重要,Boot里面大都是靠这个机制在做事情。上一篇只是简单提了下,今天发现绕不开了。

SPI ,全称为 Service Provider Interface(服务提供者接口),是一种服务发现机制。它通过在classpath路径下的META-INF/services文件夹查找文件,自动加载文件中所定义的类。

为了故事的继续发展 我们建个工程

我们用医生给我们做核酸的场景:

spi-doctor 为服务提供方,可以理解为我们的框架

spi-person 为使用方,因为我的服务提供接口叫PlayHeSuan(做核酸),所以所有实现都是人~

我们看下代码

在spi-doctor模块中定义接口PlayHeSuan

/***服务提供者,做核酸*@Date2022/4/295:50下午*@Authorshushi*/publicinterfacePlayHeSuan{//做核酸voidplayHeSuan();}

在spi-person模块中定义两个实现类ZhangSan,Lisi

/***李四做核酸*@Date2022/4/295:57下午*@Authorshushi*/publicclassLisiimplementsPlayHeSuan{@OverridepublicvoidplayHeSuan(){System.out.println("李四做核酸~~~~");}}
/***张三做核酸*@Date2022/4/295:56下午*@Authorshushi*/publicclassZhangSanimplementsPlayHeSuan{@OverridepublicvoidplayHeSuan(){System.out.println("张三做核酸~~~~");}}

编写配置文件

新建文件夹META-INF/services

在文件夹下新建文件cn.shushi.spi.doctor.PlayHeSuan

这里要特别说明 建配置文件一定要直接写 META-INF/services,不能直接写META-INF.services,否则会无效

文件里面写实现类的全路径

测试

publicclassSpiTest{publicstaticvoidmain(String[]args){ServiceLoader<PlayHeSuan>load=ServiceLoader.load(PlayHeSuan.class);load.forEach(PlayHeSuan::playHeSuan);}}

SPI就讲到这里,我们回过头来结合实例理解下。SPI的目的就是借助SPI理解自动装配

借助SPI理解自动装配

回顾一下我们做了什么,我们在resources下创建了一个文件,里面放了些实现类,然后通过ServiceLoader这个类加载器就把它们加载出来了。

假设有人已经把编写配置之类的前置步骤完成了,那么我们是不是只需要使用下面的这部分代码,就能将PlayHeSuan有关的所有实现类调度出来。

//使用Java的ServiceLoader进行加载ServiceLoader<PlayHeSuan>load=ServiceLoader.load(PlayHeSuan.class);load.forEach(PlayHeSuan::playHeSuan);

再进一步讲,如果再有人把上面这部分代码也给写了,然后把这些实现类全部注入到Spring容器里,那会发生什么?

相信到这里大家看到这里心里都已经有个谱了,我们接下来进入SpringBoot开始深入理解

SpringBoot的配置文件

在回想下SPI机制,他们都是通过在组件下放入一个配置文件完成的,那么Spring Boot是不是也这样的呢?。

是的 没错,我们可以通过pom文件的组名称来maven看到在这个下面,我们可以看到spring.factories,翻译过来是spring工厂,是不是有那个感觉了,这个就是实例化的呗

很明显的看到最下面有个自动配置的注释,key还是个EnableAutoConfiguration,开启自动配置!找到了!

我们接着找我们最开始说的redis

进入RedisAutoConfiguration类,看看里面是些什么代码

哈哈 破案了,终于破案了,这里不就是加载进去了嘛,配置文件我们也找到了:spring.factories,也实锤了就是通过这个配置文件进行的自动配置。

接下来,我们来尝试还原一下案情经过:

第一 通过某种方式读取spring.factories文件,紧接着把里面所有的自动配置类加载到Spring容器中

第二 然后就可以通过Spring的机制将配置类的@Bean注入到容器中了。

那接下来,我们就看是通过什么样的方式读取spring.factories,并且注入的,看到这里是不是感觉刚要进去的感觉,坚持看,相信都能看明白

如何注入

其实读取配置,就是注入,我们一起来看下看看Spring中有哪些注入方式

类似于@Component,@Bean这些,阿鉴就不说了,大家肯定见过一种这样的注解:EnableXxxxx

比如:EnableAsync开启异步,大家好不好奇这样的注解是怎么生效的?一起来看下

大家有没有发现,框架中到处是Import注解,这里先不展开细讲,到时候讲Spring的时候,再细说,总之一句话,import就是将该类交给Spring管理,实例化该bean。

那回过头来进去 AsyncConfigurationSelector,接着进去ProxyAsyncConfiguration

真相了,EnableAsync就是通过这里(AsyncAnnotationBeanPostProcessor)生效的

好的 我们接着研究最后一个注解,也是SpringBoot的启动核心注解

SpringBootApplication注解

我们在使用SpringBoot项目时,用到的唯一的注解就是@SpringBootApplication,所以我们唯一能下手的也只有它了,打开它看看吧。

进入AutoConfigurationImportSelector核心逻辑selectImports

@OverridepublicString[]selectImports(AnnotationMetadataannotationMetadata){if(!isEnabled(annotationMetadata)){returnNO_IMPORTS;}AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AnnotationAttributesattributes=getAttributes(annotationMetadata);//获取候选的配置类,这个其实就是开始去拿配置类去了List<String>configurations=getCandidateConfigurations(annotationMetadata,attributes);//移除重复的配置configurations=removeDuplicates(configurations);//获取到要排除的配置Set<String>exclusions=getExclusions(annotationMetadata,attributes);checkExcludedClasses(configurations,exclusions);//移除所有要排除的配置configurations.removeAll(exclusions);//过滤掉不具备注入条件的配置类,通过Conditional注解configurations=filter(configurations,autoConfigurationMetadata);//通知自动配置相关的监听器fireAutoConfigurationImportEvents(configurations,exclusions);//返回所有自动配置类returnStringUtils.toStringArray(configurations);}

如何从配置文件读取的(getCandidateConfigurations)

protectedList<String>getCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){//是不是有刚才那个SPI感觉了List<String>configurations=SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());//通过这个也能看出来是去读取META-INF/spring.factorieAssert.notEmpty(configurations,"NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou"+"areusingacustompackaging,makesurethatfileiscorrect.");returnconfigurations;}

getSpringFactoriesLoaderFactoryClass

spring:redis:host:127.0.0.1port:6379password:1234560

结合上一步,就是加载配置文件,并且读取key为EnableAutoConfiguration的配置

接下来再看loadFactoryNames

spring:redis:host:127.0.0.1port:6379password:1234561

ok, 后面的过滤逻辑就不在这里说了,毕竟本节的重点是自动装配机制,小伙伴明白了原理就ok啦

好的 今天的自动装配原理就讲到这里,我们下期见

总结回顾

本篇介绍了关于SpringBoot的自动装配原理,我们先通过SPI机制进行了小小的热身,然后再根据SPI的机制进行推导Spring的自动装配原理,中间还带大家回顾了一下@Import注解的使用,最后成功破案~

最后给张图做下总结


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