进阶

原理初探:自动配置

pom.xml

  • spring-boot-depencties的依赖都在pom.xml的父工程中
  • 我们在引入一些springboot依赖的时候不需要引入版本就是因为在父工程中的依赖都配置好了版本

所以我们通过查看源码可以得出:pom.xml的父工程的父工程管理着所有的依赖和版本,所以我们在设置启动器的时候才不用规定版本

启动器

那么说到了启动器,启动器又是什么呢?

启动器说白了就是Spring Boot的启动场景

在上面的这张图中,我们可以看到,pom.xml的依赖中其实就是一个又一个的启动器

其中spring-boot-starter是总启动器,也就是说只有靠它才能启动spring boot项目

下面还有启动器,图中只标注除了test启动器

我们的web环境其实也是依赖于一个启动器,就是spring-boot-stater-web,有了这个启动器,它会帮我们自动导入web环境所有依赖

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

有了这些启动器,启动场景就会启动,而启动器其实都是依赖于刚才说的pom.xml的所有依赖

启动器几乎包含了我们能想象到的所有的场景,所以我们要什么环境直接加上一个启动器就好了

启动器的所有依赖自然在官网上有:所有的依赖版本

https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/reference/html/appendix-dependency-versions.html#appendix-dependency-versions

所有的启动器:

https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/reference/html/using-spring-boot.html#using-boot-starter


主程序

主程序看上去很简单,只有一个注解和启动方法

注解

通过查看源码,我们可以得出一个流程图

最后得到的文件:

那么Spring Boot在启动的时候,从类路径下:META-INF/spring.factories获取指定的值

将这些自动配置的类导入容器,自动配置就会生效并且帮我进行自动配置

以前我们需要配置的东西,现在spring boot帮我们做了

整合JAVAEE和解决方案都在这个spring-boot-autoconfiguration-xxx.jar这个包下

它会把所有需要导入的组件以类名的方式返回,这些组件就会被添加到容器

容器中也存在着非常多的xxxAutoConfiguration的文件,就是这些文件给这个容器中导入了这个场景所需要的所有组件,并自动配置



启动方法

Run方法

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器 , 设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

Spring Boot配置文件

我们看到Spring Boot的配置文件resource/application.properties到底能配置什么东西

讲东西要讲原理,官方的配置太多了,要了解原理一通百通

  1. 可以删掉application.properties,因为官方不建议使用properties
  2. 建立application.yaml,因为官方支持yaml并且建议使用yaml并且yaml功能易学强大,用的人多

注意,applicatioin这个名称是固定的,在配置中可以看到:

我们要么使用application.prperties

要么使用application.yaml

yaml

yaml是什么

yaml不是一种标记语言的,但是它同时也是一种标记语言,以数据为中心的

可以做配置


yaml的语法与properties做对比

  • yaml的语法是 key: value(注意空格)
  • properties的语法是key=value

注意,yaml对空格的严格十分高

给实体类赋值

除此之外,yaml一个更强大的地方为还可以注入到配置类中!

首先来两个pojo

  • Dog
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
    private String name;
    private Integer age;
}
  • Person
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "person") //这个就是那个赋值的注解,直接关联yaml中的person
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

下面来给Person赋值:

  • application.yaml,将会被@ConfigurationProperties注解关联到
person:
  name: bean
  age: 3
  happy: false
  birth: 2020/2/19
  maps: {k1: v1,k2: v2}
  list:
    - code
    - music
    - girl
  dog:
    name: wang
    age: 3
  • 测试
@SpringBootTest
class SpringbootprojectApplicationTests {

    @Autowired
    private Person person;

    @Test
    void contextLoads() {

        System.out.println(person);

    }

}
  • 结果
Person{name='bean', age=3, happy=false, birth=Wed Feb 19 00:00:00 CST 2020, maps={k1=v1, k2=v2}, lists=[code, music, girl], dog=Dog{name='wang', age=3}}

虽然在上面已经看到了,但是还是要再说一遍:

通过application.yaml可以配置各种配置,其中就包括给pojo赋值

通过注解@ConfigurationProperties(prefix = "")来指定配置,将其关联起来

如图所示,关联起来了

但是我们还看见了一个提示框,忽略也可以,但是不忽略的话就去它的链接,去那里找到一句话加上就可以了

https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor

它让放,你就放,放完就没了


松散绑定

随便在application.yml中的任意一个值时,都可以被识别到,只需要使用@Value注解即可



配置文件

配置文件的路径

在官网上我们可以找到,配制文件可以在这么几个地方:

  • file:./config
  • file:./
  • classpath:/config/
  • classpath:/

上面的file就是我们总的项目路径

springbootproject

  • application.yaml
  • config
    • application.yaml
  • src
    • resource/java
      • application.yaml
      • config
        • application.yaml

优先级

通过测试,我们看到优先级是:

  1. file:./config
  2. file:./
  3. classpath:/config/
  4. classpath:/

也就是说,默认的给我们排序的优先级是最低的…

多套环境自由切换

多文件切换的方式

我们可以使用优先级覆盖的方式,但是这种方法太low了,我们要自助切换

首先我们来认清几个文件名字:

  • application.yaml:默认环境
  • application-dev.yaml:开发环境
  • application-test.yaml:测试环境

我们可以在默认环境中使用spring.profiles.active=xxx来切换环境

其中值只写application-xxx后面的xxx即可,比如

spring:
  profiles:
    active: dev


一个文件的方式

yaml还有一个重大突破,就是可以把多套环境放在一个文件中,而不需要必须像properties一样放在不同的文件中

使用---来进行环境的分割

  • application.yaml
## 环境切换,这里是默认配置
## 使用spring.profiles.active来激活某个版本
## 不激活版本就是默认配置
server:
  port: 8080
spring:
  profiles:
    active: dev

---
## 通过---来进行配置的分割
## 首先设置配置
## 然后通过spring.profiles来给此配置命名
server:
  port: 8081
spring:
  profiles: dev

---
## 通过---来进行配置的分割
## 首先设置配置
## 然后通过spring.profiles来给此配置命名
server:
  port: 8082
spring:
  profiles: test

JSR303校验

spring boot中可以使用校验,来验证这个数据是什么类型,比如:email,phone等等

spring boot他有一个注解叫做@Validated数据校验,举个例子:


下面这里有很多注解:

尤其注意这个符合指定的正则表达式,这个可以代替任何的其他的注解



自动配置原理再讲解

看过配置之后,我们就想明白:到底还能配置些什么?

原理

首先我们在上次原理初探的时候已经看过了一些源码,最后的结论是一个文件:

spring-boot-autoconfigure-2.2.4.RELEASE.jar\META-INF\spring.factories

我们知道这是一个核心的自动装配的文件,所有的东西都在这里配好了

在:## Auto Configure一栏中,我们可以看到许多:xxxAutoConfiguration,很明显,这些东西都是自动装配的组件

我们能够配置的东西和这些自动装配的组件有很强大的联系,我们随便点开来看,就发现这里面有非常明显的套路性

它一定会存在一个xxxAutoConfiguration,这个xxxAutoConfiguration一定会装配一个xxxpropeties,这个xxxAutoConfiguration文件一定会和配置绑定,所以我们才能够使用


比如,随便在spring.factories点开一个xxxAutoConfiguration,这次选中的是:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

发现几个注解,其中有两个重要的:

@Configuration(proxyBeanMethods = false) //声明这是一个配置类
@ConditionalOnClass(RedisOperations.class)//当满足这里面的条件时才会启动这个类
@EnableConfigurationProperties(RedisProperties.class) //当里面的一个注解生效,它才会生效

那么根据提示,我们进入到RedisProperties.class,发现一个注解:

@ConfigurationProperties(prefix = "spring.redis")

这个注解我们刚刚学过,就是在学习yaml的时候使用yaml直接向配置中绑定并注入数据

那么我们明白了,我们就可以使用spring.redis来对RedisProperties.class进行配置了,我们来看看这个类中有什么属性

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

	private int database = 0;
	private String url;
	private String host = "localhost";
	private String password;
	private int port = 6379;
	private boolean ssl;
	private Duration timeout;
	private String clientName;
	private Sentinel sentinel;
	private Cluster cluster;
	private final Jedis jedis = new Jedis();
	private final Lettuce lettuce = new Lettuce();
}

public static class Cluster {
    private List<String> nodes;
    private Integer maxRedirects;
}

public static class Sentinel {
    private String master;
    private List<String> nodes;
}

public static class Jedis {
    private Pool pool;
}

public static class Lettuce {
    private Duration shutdownTimeout = Duration.ofMillis(100);
    private Pool pool;
}

所以事情就很明显了,我们在application.yaml中随便输入一个属性来设置,比如<font style="background-color:transparent;">Sentinel.master</font>,因为绑定的是<font style="background-color:transparent;">spring.redis</font>,所以:

spring:
  redis:
    sentinel:
      master:

所以结论是:

对于任何的配置,一定会存在一个xxxAutoConfiguration,这个xxxAutoConfiguration一定会装配一个xxxpropeties,这个xxxAutoConfiguration文件一定会和配置绑定,所以我们才能够使用


自动装配的精髓

  1. SpringBoot启动会加载大量的自动配置类
  2. 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;

xxxxAutoConfigurartion:自动配置类;给容器中添加组件

xxxxProperties:封装配置文件中相关属性;


细节问题

@Conditional派生注解(Spring注解版原生的@Conditional作用)

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

我们怎么知道哪些自动配置类生效;我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#开启springboot的调试类
debug=true

Positive matches:(自动配置类启用的:正匹配)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

Unconditional classes: (没有条件的类)


自己写一个Starter

1、首先是一个SpringBoot项目随便你怎么写

2、在Resources下面新建WEB-INF文件夹,在里面新建一个 _spring.factories_文件

3、写一个Configuration,里面写一个我们扫描的位置

@Configuration
@ComponentScan("com.howling")
public class MyConfiguration {}

4、在spring.factories里面写内容,写入多个key-value形式的

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.howling.starter.MyConfiguration

在这里面,key是固定的,value是你自己写的路径

5、在其他项目中引入

<dependency>
    <groupId>com.howling</groupId>
    <artifactId>howling-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。