浅谈SpringBoot自动配置

要点:

  • 约定大于配置

  • starter、spring.factories

  • @EnableAutoConfiguration

 

前述:

MAC中的配置:
浅谈SpringBoot自动配置_第1张图片
Boot中的配置:

这是springboot相较springmvc,带给我们最直观的使用感受

曾经在mvc框架下,我们要构建项目,需要以xml形式写一堆配置文件,书写易错,配置繁杂,管理麻烦

终于!springboot的出现,将我们从这配置的苦海中带到天堂。

"腰不疼了,头发掉的也少了"

 

详述:

一、"约定大于配置"

百度的意思是这样的:
“约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。”

这个是springboot一切的基础,我们构建一个项目基本配置大都相同,而springboot则已经为我们提供了默认配置。
我们则基于这些默认的配置进行项目构建,如有不同的配置,则可另行在application.ym中进行配置即可

springboot中的约定:

  • Maven的目录结构。默认有resources文件夹,存放资源配置文件。src-main-resources,src-main-java。默认的编译生成的类都在targe文件夹下面
  • spring boot默认的配置文件必须是,也只能是application.命名的yml文件或者properties文件,且唯一
  • application.yml中默认属性。数据库连接信息必须是以spring: datasource: 为前缀;多环境配置。该属性可以根据运行环境自动读取不同的配置文件;端口号、请求路径等

总而言之:基于此约定的springboot,为我们简化了spring配置!爽!

 

二、1.Starter

“spring-boot-starter-web”,是我们使用springboot构建项目时,在pom中最常引用的Starter
spring-boot-starter-*起步依赖是SpringBoot核心之处,它提供了Spring和相关技术提供"一条龙"依赖与服务,让开发者不必关心Spring相关配置,简化了传统的xml等配置操作,当然了开发者也可通过application.ym/properties文件自定义配置

这不是与上文前后呼应?所以,可以说 starter-* 是springboot为我们提供"一条龙"服务,最直观的体现!爽!

那什么是Starter?
简单说:就是一个场景/环境所需的一组jar依赖!
避免了我们每个项目构建一个环境/服务时,重复引入依赖并配置,繁琐且低效。那starter就把一个环境/服务所需的依赖组合起来为一个starter,需要时直接引starter就好了
其实和Docker理念比较类似,都是在做一个"包装操作"

以spring-boot-starter-web为例:
浅谈SpringBoot自动配置_第2张图片
点进去康一下:
浅谈SpringBoot自动配置_第3张图片
我们看到只有一个pom文件,在其中,有两部分:

  1. 引入了我们构建web项目所必须的依赖
  2. 引入自动配置的jar,pring-boot-starter会引入spring-boot-autoconfigure的依赖。
    虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfiguration。因为Spring Boot坚信“约定大于配置”这一理念,所以我们使用ConfigurationProperties来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter的ConfigurationProperties还使得所有的配置属性被聚集到一个文件中(一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。

浅谈SpringBoot自动配置_第4张图片

2.spring.factories

自动配置类的jar的是有一个META-INF/spring.factories文件内容如下:

\是为了换行也可以使用,可以看到配置的结构形式是Key=>Value形式,多个Value时使用,隔开。

应用在启动时就会加载spring-boot-autoconfigure的jar包下面的META-INF/spring.factories文件中定义的autoconfiguration类。将configuration类中定义的bean加入spring到容器中。就相当于加载之前我们自己配置组件的xml文件。而现在SpringBoot自己定义了一个默认的值,然后直接加载进入了Spring容器。

这样我们引入的starter就可以直接被springboot启动时初始化配置,并被Spring容器管理,为我们提供“服务”~

那么....问题来了,我们的springboot如何进行"自动配置"的呢?
 

三、@EnableAutoConfiguration自动配置

(1.)@SpringBootApplication
浅谈SpringBoot自动配置_第5张图片

(2.)点进去瞅瞅

浅谈SpringBoot自动配置_第6张图片

  1. @SpringBootConfiguration:注解中引入了@Configuration,表明SpringBootApplication是一个配置类
  2. @EnableAutoConfiguration:开启自动配置功能。
  3. @ComponentScan:自动扫描

(3.)进入@EnableAutoConfiguration

浅谈SpringBoot自动配置_第7张图片

  1. @AutoConfigurationPackage:当SpringBoot应用启动时默认会将启动类所在的package作为自动配置的package
  2. @Import(AutoConfigurationImportSelector.class):利用@Import加载自动配置类,进行自动配置

(4.)进入AutoConfigurationImportSelector.class

浅谈SpringBoot自动配置_第8张图片

浅谈SpringBoot自动配置_第9张图片

浅谈SpringBoot自动配置_第10张图片

浅谈SpringBoot自动配置_第11张图片

浅谈SpringBoot自动配置_第12张图片

我们可以从下面方式进入相应源码中

getAutoConfigurationEntry -> getCandidateConfigurations ->SpringFactoriesLoader.loadFactoryNames() ->loadSpringFactories()

我们会看到相应的获取自动配只的源码:

// 使用类加载器找META-INF/spring.factories资源
Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                // 遍历找到的资源
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 使用属性文件加载资源
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

通过这种方式,会获取到META-INF/spring.factories文件,并通过其全限定名,之后可通过反射机制将该配置类加入到Spring容器中进行管理。

工厂加载机制:这里采用的是工厂加载机制,由SpringFactoriesLoader完成,SpringFactoriesLoaderl使用loadFactoryNames方法,从META-INF/spring.factories文件路径下加载指定接口的实现类

 

但是!AutoConfigurationImportSelector如何被加载?何时被加载?

实现了DeferredImportSelector接口,该又继承了ImportSelector

DeferredImportSelector是在所有@Configuration处理完成后才被处理的

当我们启动springboot启动类后,会加载实现了DeferredImportSelector接口的process()方法,在process()中会调用getAutoConfigurationEntry(),然后就是我们上面的调用链路了~

总述:

到这里,springboot的自动配置就梳理完成了~

总的来说:就是利用Spring的工厂加载机制,加载我们约定好的META-INF/spring.factories文件中的接口的实现类

"约定大于配置~"

如有不对或疑惑点欢迎留言交流~

 

参考文档:
https://baijiahao.baidu.com/s?id=1637222519335395430&wfr=spider&for=pc
https://blog.csdn.net/jdfk423/article/details/82940924
https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/
https://www.jianshu.com/p/29f3a2f65282
https://blog.csdn.net/WiteWater/article/details/90056165
https://www.jianshu.com/p/23d4e853b15b
https://blog.csdn.net/weixin_41532316/article/details/102509707
https://www.cnblogs.com/huojg-21442/articles/12335457.html
https://www.jianshu.com/p/aa99a303bc37
https://www.jianshu.com/p/23d4e853b15b
 

你可能感兴趣的:(JAVA,springboot自动配置)