写点什么

学过了 SpringBoot 配置详解,再来深入认识一下 SpringBoot 的自动配置原理

发布于: 2021 年 04 月 10 日
学过了SpringBoot配置详解,再来深入认识一下SpringBoot的自动配置原理

基本概念

  • SpringBoot 的优点:

  • 可以创建独立的 Spring 应用

  • SpringBoot 嵌入 Tomcat,Jetty Unsertow, 不需要部署 war 文件

  • 根据需要通过 maven 获取 starter

  • Spring 进行自动配置

  • 提供生产就绪型功能,包括指标,健康检查和外部配置

SpringBoot 父项目

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-dependencies</artifactId>    <version>1.5.9.RELEASE</version>    <relativePath>../../spring-boot-dependencies</relativePath>  </parent>
复制代码


  • 管理 SpringBoot 应用里面所有的依赖版本,这样以后导入依赖默认不需要写版本号,可以统一管理开发版本(没有在 dependencies 里面管理的依赖才需要声明版本号)

spring-boot-starter

<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>         </dependency>
复制代码


  • spring-boot-starter: springboot 场景启动器

  • spring-boot-starter-web: 导入 web 模块正常运行所依赖的组件

  • SpringBoot 将所有的功能场景都抽取出来,做成各个 starter 启动器,只需要在项目的 pom.xml 中引入这些 starter 依赖,相关场景的所有依赖都会被导入进来。

@SpringBootApplication

  • @SpringBootApplication:标注在 SpringBoot 的主配置类,SpringBoot 就会运行这个类的 main 方法来启动 SpringBoot 运用。


@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration   //SpringBoot配置类,类似于配置文件。配置类也是容器中的组件。标注在类上标明是一个SpringBoot配置类@EnableAutoConfiguration  //开启SpringBoot自动配置功能@ComponentScan(    excludeFilters = {@Filter(    type = FilterType.CUSTOM,    classes = {TypeExcludeFilter.class}), @Filter(    type = FilterType.CUSTOM,    classes = {AutoConfigurationExcludeFilter.class})})public @interface SpringBootApplication {    @AliasFor(        annotation = EnableAutoConfiguration.class,        attribute = "exclude"    )    Class<?>[] exclude() default {};
@AliasFor( annotation = EnableAutoConfiguration.class, attribute = "excludeName" ) String[] excludeName() default {};
@AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {};
@AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {};}
复制代码


  • @SpringBootConfiguration: SpringBoot 配置类,类似于配置文件。配置类也是容器中的组件。标注在类上标明是一个 SpringBoot 配置类


@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration  // 表明该类是一个Spring的配置类public @interface SpringBootConfiguration {  @AliasFor(    annotation = Configuration.class  )  boolean proxyBeanMethods() default true;}
复制代码


@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Component  // 表明该类是Spring的一个组件public @interface Configuration {  @AliasFor(    annotation = Component.class  )  String value default "";    boolean proxyBeanMethods() default true; }
复制代码


  • @EnableAutoConfiguration: 开启 SpringBoot 自动配置功能



package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};}
复制代码


  • @AutoConfigurationPackage: 自动配置包,通过 @Import({Registrar.class}) 完成,通过查看 Registrar 源码发现将主配置类即 @SpringBootApplication 标注的类的所在包及所有子包里面的所有组件扫描到 Spring 容器中

  • 其中,@Import 是 Spring 的底层注解,给容器导入一个组件,导入的组件由 Registrar.class 来指定。

  • AutoConfigurationImportSelector:导入组件的选择器,将所有需要导入的组件以全类名的方式返回,这样组件就会被添加到容器中

  • 会给容器中导入自动配置类:就是给容器中导入场景所有的组件并配置好.这样就不用手动编写配置并注入功能组件

  • SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader):

  • 从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值

  • 过程总结:

  • SpringBoot 在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值

  • 将这些值作为自动配置类导入到容器中,自动配置类就会生效,进行配置工作。

  • J2EE 的整体整合解决方案和自动配置都在 spring-boot-autoconfigure-2.0.1.RELEASE.jar 中




在 SpringBoot 项目中的 resources 文件夹:


  • static:保存所有静态资源,例如 js,css,images

  • templates:保存所有的模板页面。在 SpringBoot 中默认 jar 包使用的是嵌入式 tomcat,默认不支持 jsp 页面。可以使用模板引擎:freemarker,thymeleaf

  • application.properties:SpringBoot 应用的配置文件,可以修改一些默认设置

SpringBoot 配置

配置文件

SpringBoot 使用一个全局配置文件,配置文件名是固定的:


  • application.properties

  • application.yml

  • 配置文件作用:修改 SpringBoot 自动配置的默认值。

yml

  • yml:(YAML Ain't Markup Language)以数据为中心,比 json、xml 等更适合做配置文件。

YAML 基本语法
  • key: value(表示一对键值对。value 前面必须要有空格。)

  • 空格缩进来表示层级关系,只要是左对齐的一列数据,都是同一个层级的 1.缩进时不允许使用 Tab 键,只允许使用空格 2.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可

  • 属性和值是大小写敏感

  • 值的写法:

  • 字面量:普通的值(数字,字符串,布尔值)

  • key:value :字面量直接书写。字符串默认不加引号。

  • 单引号-转义字符会作为普通字符串输出

  • 双引号-转义字符会转变成格式

  • 对象、Map(属性和值,键值对):

  • 对象还是 key: value 的方式


 friends:           lastName: Chova           firstName: Vea
复制代码


行内写法:


friends: {lastName: Chova,firstName: Vea}
复制代码


  • 数组(List、Set)

  • - 值表示数组中的元素


pets:  - dog  - cat  - pig
复制代码


行内写法:


pets: [dog,cat,pig]
复制代码

@Value 获取值和 @ConfigurationProperties 获取值比较


  • 如果我们只是在某个业务逻辑中需要获取一下配置文件的某项值,就使用 @Value

  • 如果 JavaBean 需要和配置文件进行映射,就使用 @ConfigurationProperties

配置文件注入值数值校验(JSR303)

  • 必须要用 @ConfigurationProperties

  • JSR303 数据校验:@Validate-@Email

@PropertySource 和 @ImportResource

  • @PropertySource:加载指定的配置文件


 @PropertySource(value = {"classpath:person.properties"})
复制代码


  • @ImportResource:导入 Spring 的配置文件,让配置文件的内容生效


@ImportResource(locations={"classpath:beans.xml"})
复制代码


  • SpringBoot 推荐给容器中添加组件的方式: 推荐使用全注解的方式

  • 1.配置类 --- Spring 配置文件

  • 2.使用 @Bean 在配置类中为容器中添加组件

配置文件占位符

  • RandomValuePropertySource:配置文件中可以使用随机数


1.${random.value}2.${random.int}3.${random.int(10)}4.${random.int[1024,65536]}
复制代码


  • 属性配置占位符:1.可以在配置文件中引用前面配置过的属性(优先级前面配置过的这里都能用)2.${app.name:默认值}来指定找不到属性值时的默认值

Profile

  • 多 Profile 文件

  • 1.在写配置文件的时候,文件名可以是:application-{profile}.properties/yml

  • 2.默认使用 application.properties 的配置

  • yml 支持多文档块方式

  • 用"- - -"划分文档块


---
复制代码


激活指定 Profile1.在主配置文件 application.properties 中指定激活:


spring.profiles.active=dev
复制代码


2.命令行激活:(Program arguments)


--spring.profiles.active=dev
复制代码


3.虚拟机参数激活:(VM options)


-Dspring.profiles.active=dev
复制代码

配置文件加载位置

  • SpringBoot 启动会扫描以下位置的 application.properties 或者 application.yml 文件作为 SpringBoot 的默认配置文件


  1. file:./config/

  2. file:./

  3. classpath:/config

  4. classpath:/


  • 以上按照优先级从高到低,所有文件都会被加载,互补配置。高优先级内容会覆盖低优先级内容。

  • 可以通过配置 - -spring.config.location 来改变默认配置位置:项目打包好以后,使用命令行参数的形式,启动项目的时候来指定配置文件的新位置,指定的配置文件和默认加载的配置文件会共同起作用,互补配置。

外部配置的加载顺序

  • SpringBoot 支持多种外部配置方式,优先级如下:

  • 1.命令行参数(- -,多个命令用空格分开)

  • 2.来自 java:comp/env 的 JNDI 属性

  • 3.Java 系统属性(System.getProperties())

  • 4.操作系统环境变量

  • 5.RandomValuePropertySource 配置的 random.*属性值由 jar 包外部向 jar 包内进行寻找:优先加载带 profile 的

  • 6.jar 包外部的 application-{profile}.properties/yml(带 spring.profile)配置文件

  • 7.jar 包内部的 application-{profile}.properties/yml(带 spring.profile)配置文件然后加载不带 Profile 的

  • 8.jar 包外部的 application-{profile}.properties/yml(不带 spring.profile)配置文件

  • 9.jar 包内部的 application-{profile}.properties/yml(不带 spring.profile)配置文件

  • 10.@Configuration 注解类上的 @PropertySource

  • 11.通过 SpringApplication.setDefaultProperties 指定的默认属性

自动配置原理

  • SpringBoot 启动时加载主配置类,开启了自动配置功能 @EnableAutoConfiguration

  • @EnableAutoConfiguration 作用:利用 EnableAutoConfigurationImportSelector 给容器导入组件。具体实现可以查看 selectImports() 方法:


List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置SpringFactoriesLoader.loadFactoryNames();扫描所有jar包类路径下:META-INF/spring.factories。把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class(类名)类的值,然后把它们添加在容器中
复制代码


将类路径下 META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到了容器中。


  • 每一个自动配置类进行自动配置功能。


 @Configuration  // 表示这是一个配置类,类似配置文件,可以给容器中添加组件@EnableConfigurationProperties({HttpProperties.class})  // 启用指定类的ConfigurationProperties(从配置文件中获取指定的值和bean的属性进行绑定)功能@ConditionalOnWebApplication(  // Spring底层@conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效(判断当前应用是否为web应用)    type = Type.SERVLET)@ConditionalOnClass({CharacterEncodingFilter.class})  // 判断当前项目有没有这个类,CharacterEncodingFilter:SpringMVC中进行乱码解决的过滤器@ConditionalOnProperty(  // 判断文件中是否存在某个配置    prefix = "spring.http.encoding",    value = {"enabled"},    matchIfMissing = true  // 即使配置文件中不配置spring.http.encoding.enable=true,也是默认生效的)public class HttpEncodingAutoConfiguration {
复制代码


根据当前不同的条件判断,决定配置类是否生效,就会通过 @Bean 为容器中添加各种组件,这些组件的值需要从 properties 中获取,properties 中的每一个属性和配置文件绑定。<font color=red>注意点:</font>


  • @Conditional 派生注解(来源于 Spring 底层注解 @Conditional)

  • 1.作用:必须是 @Conditional 指定的条件成立,才给容器中添加组件,配置里面的所有内容才生效。

  • 2.自动配置类必须在一定的条件下才会生效:在配置文件中


  debug=true
复制代码


可以在控制台打印自动配置报告,可以查看哪些自动配置生效,哪些自动配置不生效。

总结

  • SpringBoot 启动会加载大量的自动配置类。

  • 判断 SpringBoot 默认写好的自动配置类有没有需要的功能。

  • 判断自动配置类是否配置需要的组件,没有的就需要自己配置。

  • 给容器中自动配置类添加组件时,会从 properties 中获取属性,在配置文件中指定这些属性的值。

  • xxAutoConfiguration:自动配置类,给容器中添加组件===xxProperties:封装配置文件中的相关属性,和配置文件绑定

发布于: 2021 年 04 月 10 日阅读数: 34
用户头像

一位攻城狮的自我修养 2021.04.06 加入

分享技术干货,面试题和攻城狮故事。 你的关注支持是我持续进步的最大动力! https://github.com/ChovaVea

评论

发布
暂无评论
学过了SpringBoot配置详解,再来深入认识一下SpringBoot的自动配置原理