跳到主要内容

SpringBoot

00 应用入口

@SpringBootApplication是一个组合注解,包括

  • @Configuration

  • @EnableAutoConfiguration

    根据类路径中的jar包依赖为当前项目进行自动配置,如添加了spring-boot-starter-web依赖,会自动添加Tomcat和spring mvc的依赖,那么spring boot会对tomcat和spring mvc进行自动配置

  • @ComponentScan

    自动扫描@SpringBootApplication所在类的同级包以及下级包里的Bean,所以入口类基本建议配置在groupID和arctifactID组合的包名下

01 配置文件

全局配置文件application.properties或者application.yml,在src/main/resources目录或者类路径的/config下

02 Spring boot启动

通过getSpringFactoriesInstances方法,通过SPI机制,读取了一堆配置

spring.factories文件中定义的ApplicationContextInitializer/ApplicationListener/Bootstrapper/BootstrapRegistryInitializer类的一些实现

Springboot默认提供了两个spring.factories文件,分别位于spring-boot.jar和spring-boot-autoconfigure.jar的META-INF/下

即在创建SpringApplication示例的时候,会加载一些初始化和启动的参数与类

spring启动过程分为多个阶段,每完成一步产生一个事件,并调用对应事件的监听器,是一种标准的观察者模式,在启动过程中有很好的扩展性

img

03 配置文件加载顺序

配置文件加载优先级

优先级高的会覆盖低的, 也可以理解他们的加载时机是顺序相反的!

下面优先级从1-6,加载顺序是从6到1

  1. 启动时指定某些参数 > java -jar app.jar --server.port=9999

  2. 启动时指定配置文件(有坑:互补失效!)

    java -jar app.jar -spring.config.location=/mydir/application.properties

  3. jar同目录的config/application.properties

    app.jar 同级别目录下的 ./config/application.properties

  4. jar同目录的application.properties

    app.jar同级别目录下的 application.properties

  5. jar内部 classpath目录下的 ./config/application.properties

    app.jar/BOOT-INF/classes/config/application.properties

  6. jar内部classpath目录下的 application.properties

    app.jar/BOOT-INF/classes/application.properties

多环境加载

基础的application和application-xxx都会被加载,xxx优先级高,会覆盖无xxx的同名属性。

一般在application.proerties里通过 spring.profiles.active=dev来确定加载哪个环境的配置文件,也可以看上面,通过命令行--spring.profiles.active=xxx来指定

同一目录下,properties和yml共存

properties优先级高,yml内容会被覆盖

先加载yml,再加载properties,后加载的覆盖前面同名的属性。

04 springcloud Eureka注解

@EnableDiscoveryClient和@EnableEurekaClient 跟springcloud版本有关

Spring Cloud版本:Angle -> Brixton -> Camden -> Dalston -> Edgware -> Finchley

前者是通用的,不论使用哪种注册中心,都可以注册

后者专门用于eureka作为注册中心的情况,建议使用前者

在Spring Cloud的Dalston及其之前的版本中:  1、从2014年的Spring Cloud 1.0.0.RC1版本开始,官方就推荐使用EnableDiscoveryClient来取代EnableEurekaClient。  2、EnableEurekaClient源码中使用了注解EnableDiscoveryClient,因此如果要使用eureka的注册发现服务,两者功能是一样的。  3、EnableDiscoveryClient注解在spring.factories配置中通过配置项EurekaDiscoveryClientConfiguration来开启服务注册发现功能;

在Dalston之后的版本中(不含Dalston):  1、在spring.factories配置中,配置类EurekaDiscoveryClientConfiguration被配置到springboot的自动配置注解中,与EnableDiscoveryClient注解没有关系了,也就是说只要开启了springboot的自动配置,服务注册发现功能就会启用。  2、 EnableEurekaClient源码中没有使用注解EnableDiscoveryClient,此时EnableEurekaClient已经没用了。

05 spring boot通过命令行设置属性

在命令行运行时,连续的两个减号–就是对application.properties中的属性值进行赋值的标识。所以,java -jar xxx.jar --server.port=8888命令,等价于我们在application.properties中添加属性server.port=8888,该设置在样例工程中可见,读者可通过删除该值或使用命令行来设置该值来验证。

通过命令行来修改属性值固然提供了不错的便利性,但是通过命令行就能更改应用运行的参数,那岂不是很不安全?是的,所以Spring Boot也贴心的提供了屏蔽命令行访问属性的设置,只需要这句设置就能屏蔽:SpringApplication.setAddCommandLineProperties(false)。

参考 :使用 spring.profiles.active来区分配置

06 spring boot导入外部资源

如果有下列的情况, 就需要用到@ImportResource来帮忙了:

  1. 有一个遗留的xml文件, 比如名叫 spring-beans.xml 里面有很多的配置bean, 都需要注册到spring容器中, 让容器来管理这些bean以备不时之需;
  2. 传统springmvc的项目, 原来的xml配置文件不想删除, 但是又想用springboot来改造它; 就可以使用 @ImportResource来 导入外部资源

简言之: 就是还想用xml, 还想用 springboot; xml就由此注解来注册进去!

启动类上加注解: @ImportResource("classpath:/spring/spring-*.xml")

07 spring boot自动装配

spring boot在启动时,会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到spring容器中(就是按照配置的类型信息,new对象丢到spring容器中),并执行类中定义的各种操作。

文件内容一般是:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...
spring boot的自动配置代码

org.springframework.boot:spring-boot-autoconfigure:{version}下的META-INF/spring.factories

里边key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性下,有非常多要自动配置的类,这些类很多都是springboot官方提供的starter里的配置类。

这么多配置类,都要实例化吗?

自动配置类里有一些Conditionalxxx注解,用来判断是否实例化这些类。

  • @ConditionalOnClass(xxx.class)

    只有路径中有xxx类时,才会自动装配此配置类,即你得引入xxx所在的starter,springboot才会自动装配此配置类

  • @ConditionalOnMissingBean(type="xxx.yyy")

    如果自己定义了yyy类,那springboot就会放弃帮你自动装配。即如果yyy类确实(没有自己定义)那么springboot就自动帮你装配。

  • @EnableConfigurationProperties(xxx.class)

    xxx类中的属性,就是在application.properties里可以支持配置的属性。

写一个自己的springboot starter
  • 创建一个自己的自动装配类,包括用于自定义的配置文件类

  • 在META/spring.factories文件中键入需要自动装配的类

第一步:创建自动配置项目

创建一个名为hello-autoconfigure的maven项目 ,项目结构如下,其中library下的类实际情况下应该是在另外一个库里,我们要使用这个库来完成功能,这个库要在后面的starter里与这个项目一起引入的。出于演示目的,直接放在autoconfigure项目中了。

├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── top
│ │ │ └── ss007
│ │ │ └── hellospringbootautoconfigure
│ │ │ ├── config
│ │ │ │ ├── HelloAutoConfiguration.java
│ │ │ │ └── HelloProperties.java
│ │ │ └── library
│ │ │ └── ShuSheng007.java
│ │ └── resources
│ │ └── META-INF
│ │ ├── additional-spring-configuration-metadata.json
│ │ └── spring.factories
│ └── test
│ └── java
│ └── top
│ └── ss007
│ └── hellospringbootautoconfigure

在pom.xml中引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.4</version>
</dependency>
<!-- 在META-INF中生成属性配置的元数据给IDE,用于编辑属性文件时的代码提示,可选-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<version>2.5.4</version>
</dependency>
<!-- 在META-INF中生成忽略配置元数据,加快自动配置的初始化速度,可选-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
<version>2.5.4</version>
</dependency>

构建属性配置类(可选),主要是为了让我们的starter具备自定义能力,可以通过application.properties文件对其进行配置。

@ConfigurationProperties(prefix = "ss007.hello")
public class HelloProperties {
/**
* 讲话者姓名
*/
private String name = "ShuSheng007";
private String content = "Hello Spring Starter";

//省略getter和setter
}

创建自动配置类

@Configuration
@ConditionalOnProperty(value = "ss007.hello.enabled",havingValue = "true")
@ConditionalOnClass(ShuSheng007.class)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
private final HelloProperties helloProperties;
public HelloAutoConfiguration(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}

@Bean
@ConditionalOnMissingBean
public ShuSheng007 shuSheng007(){
return new ShuSheng007(helloProperties.getName(),helloProperties.getContent());
}
}

将自动配置类加入META-INF/spring.factories中

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
top.ss007.hellospringbootautoconfigure.config.HelloAutoConfiguration
第二步:创建starter项目

starter项目中只包含一个pom.xml文件,它只是将使用到的liberary与我们上面的项目作为依赖引入到pom文件中。可以理解对自动配置项目包了一层。

hello-starter
└── pom.xml

其中pom.xml内容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=...>
<modelVersion>4.0.0</modelVersion>

<groupId>top.ss007</groupId>
<artifactId>hello-spring-boot-starter</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>hello-spring-boot-starter</name>
<description>hello-spring-boot-starter</description>

<dependencies>
<dependency>
<groupId>top.ss007</groupId>
<artifactId>hello-autoconfigure</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 如果autoconfigure里还有其他用到的第三方依赖,可以在这里引入 -->
</dependencies>
</project>
第三步:使用starter

pom.xml中引入hello-starter

开始为了演示增加的开关属性,在application.properties中加入ss007.hello.enabled=true,很多stater不一定有需要这个,具体看starter的自动配置实现方式

然后就可以开始直接使用我们在自动配置项目hello-autoconfigure中配置的bean了,可以直接注入使用:

@Autowired
private ShuSheng007 shuSheng007;
更进一步
  • 属性文件代码提示

    在hello-configure项目中引入的另外两个jar,可以根据属性配置类和自定义的属性,生成metadata给IDE使用。

    属性配置类中的属性元数据文件生成:

    生成的元数据文件是:target/classes/META-INF/spring-configuation-metadata.json

    自定义的其他属性:

        在resources/META-INF中添加一个additional-spring-configuration-metadata.json文件即可,会自动追加到上面的metadata.json里。

08 @ConfigurationProperties及@EnableConfigurationProperties

假设application.yml如下

my:
test:
aaa: aaa_value
bbb: bbb_value
complex:
loginType: json

@ConfigurationProperties作用于类

如果@ConfigurationProperties所注解的类,可以被springboot扫描并且添加到容器中作为bean(比如使用@Component等注解,或者配置@ComponentScan扫描该类所在包等手段),那么spring容器会自动使该类上的@ConfigurationProperties生效,创建一个该类的实例,然后把对应的配置属性绑定到该实例,再把该实例作为bean添加到spring容器。

@Component // 这里指定了可以被扫描为bean
@ConfigurationProperties(prefix="my.test")
public class myTestProperties {
private String aaa;
private String bbb;
private ComplexProperties complex = new ComplexProperties();
// getter and setter
}

public class ComplexProperties {
private String loginType;
// getter and setter
}

@EnableConfigurationProperties使用

如果类只使用了@ConfigurationProperties注解,但是该类并没有在bean的扫描路径下,或者没有使用@Component等注解,导致无法被扫描为bean,那么就必须在配置类上(使用@Configuration注解的类)使用@EnableConfigurationProperties注解,去指定这个类,这是会让该类的@ConfigurationProperties生效,然后作为bean添加到bean容器中。

@ConfigurationProperties(prefix="my.test")
public class MyTestProperties {
private String aaa;
private String bbb;
private ComplexProperties complex = new ComplexProperties();
// getter and setter
}

public class ComplexProperties {
private String loginType;
// getter and setter
}


@Configuration
//在@Configuration中,使用了这个配置,让MyTestProperties类自动装配属性,
//并作为一个bean添加到bean容器中
@EnableConfigurationProperties(MyTestProperties.class)
public class MyConfig {
// 一些配置,可以是任意的config类
}

@ConfigurationProperties作用于方法

在配置类中,@ConfigurationProperties与@Bean一起用,把配置文件中的属性注入该Bean,并且该Bean也添加到bean容器中。

public class MyTestProperties {
private String aaa;
private String bbb;
private ComplexProperties complex = new ComplexProperties();
// getter and setter
}

public class ComplexProperties {
private String loginType;
// getter and setter
}


@Configuration
public class MyConfig {

@Bean
@ConfigurationProperties(prefix="my.test")
public MyTestProperties myTestProperties() {
return new MyTestProperties();
}
}

总结

@ConfigurationProperties 和 @value 有着相同的功能,但是 @ConfigurationProperties的写法更为方便

@ConfigurationProperties 的 POJO类的命名比较严格,因为它必须和prefix的后缀名要一致, 不然值会绑定不上, 特殊的后缀名是“driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是 “driverClassName”

参考:

@ConfigurationProperties 与 @EnableConfigurationProperties_51CTO博客_@EnableConfigurationProperties

@ConfigurationProperties使用技巧 - 掘金