Spring Boot使用两个全局的配置文件,配置文件名是固定的:
· application.properties
· application.yml
配置文件的作用:修改Spring Boot自动配置的默认值,Spring Boot在底层都为我们自动配置好了。
以properties作为扩展名的配置文件在Spring框架开发中很常见,这里说明一下YAML(YAML Ain’t Markup Language)文件。YAML是“YAML不是一种标记语言”的外语缩写,这是为了强调这种语言以数据为中心,不是以置标语言为重点,而用返璞词重新命名。它是一种直观的、能够被电脑识别的数据序列化格式,是一个可读性高、容易阅读、容易和脚本语言交互、用来表达资料序列的编程语言。
以前的配置文件大多都使用的是XML格式的文件,而YAML以数据为中心,比JSON、XML格式的文件等更适合做配置文件。
以下是YAML配置的例子:
server: port: 8081
XML配置例子:
<server> <port>8081</port> </server>
下一小节将重点讲解YAML的语法。
k:(空格)v:表示一对“键值对”(空格必须有),以空格的缩进来控制层级关系,只要是左对齐的一列数据,都是同一个层级的。
基本语法规则:
· 区分字母大小写。
· 使用缩进表示层级关系。
· 缩进时不允许使用制表符(Tab),只允许使用空格。
· 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
以下是一个写法示例:
server: port: 8081 path: /hello
注意
属性和值也是区分字母大小写的。
作为配置文件中键值对中的“值”的写法,有以下几种情况需要注意:
· 字符串默认不用加上单引号或者双引号。
· 如果使用了双引号,会转义字符串里面的特殊字符。如name: "zhangsan \n lisi":表示输出zhangsan后换行再输出lisi。
· 如果使用单引号,不会转义特殊字符,特殊字符最终只是一个普通的字符串数据。如name:'zhangsan \n lisi':表示输出zhangsan \n lisi。
一个对象或Map结构都可以由一组键值对(Key-Value Pair)所组成,可以转为字典结构,而一个字典结构的值采用的是简单的“键:值”的形式(这个冒号后面必须是一个空格)。比如表达一个对象的方式如下:
friends: lastName: zhangsan age: 20
在一行内的写法(简称行内写法)如下:
friends: {lastName: zhangsan, age: 18}
列表中的所有元素(或成员)都处于相同的缩进层级,并使用一个"- "作为开头(一个横杠和一个空格):
pets: - cat - dog - pig
也可以采用行内写法:
pets: [cat, dog, pig]
本节示例对应的项目名为spring-boot-02-config,该项目在本章示例源码文件夹中。
通过使用@ConfigurationProperties注解,可以将YAML配置文件中相关配置的值与JavaBean进行绑定,这部分配置代码如下:
示例代码1-4 application.properties(文件的部分代码或者文件的核心代码)
JavaBean部分代码如下:
示例代码1-5 Person.java(文件的部分代码或者文件的核心代码)
另外,Spring Boot默认使用yml配置文件,使用xml或properties作为配置文件时,Spring Boot需要将spring-boot-configuration-processor添加到类路径以生成配置元数据,其余用法和yml一致。要将spring-boot-configuration-processor依赖添加到pom.xml文件中,只需要在dependencies节点下添加如下代码:
示例代码1-6 pom.xml(文件的部分代码或者文件的核心代码)
<!--导入配置文件处理器,配置文件进行绑定就会有提示--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
下面是关于在配置文件中注入值需要注意的地方。
properties配置文件可能会出现乱码,参照图1-3所示的方式进行调整。
图1-3
其实,在properties文件中,中文也会显示为UTF-8编码格式,这种情况下可以在file→setting→editor→file encodings下勾选transparent native-to-ascii conversion选项即可。
配置文件无论是yml还是properties,这两种方式都可以获取到值。如果只是在某个业务逻辑中需要获取一下配置文件中的某项值,那么使用@Value即可;如果要专门编写了一个Java Bean映射到配置文件,那么就直接使用@ConfigurationProperties。二者比较如表1-1所示。
表1-1 @Value和@ConfigurationProperties对比
@Validated是Spring Validator校验机制,用于类型、方法和方法参数,但不能用于成员属性(field)。下面是用于类型Person上进行数据校验的示例代码,它在不同属性上可以加上不同的校验类型,如@Email、@NotBlank等:
示例代码1-7 Person.java(文件的部分代码或者文件的核心代码)
@Component @ConfigurationProperties(prefix = "person") @Validated public class Person { //lastName必须是邮箱格式 @Email private String lastName; private Integer age; }
其中,@Validated注解表示需要进行数据校验,@Email则表示属性值必须是邮件格式。
@PropertySource:加载指定的配置文件,可以在Spring Boot默认的两个全局配置文件之外自定义属性配置文件,具体用法参见以下示例代码:
示例代码1-8 Person.java(文件的部分代码或者文件的核心代码)
@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效,Spring Boot中没有Spring的配置文件,我们自己编写的配置文件也不能被自动识别,要想让Spring的配置文件生效,使用该注解将其加载进来即可。
比如,编写Spring的配置文件beans.xml,并配置了一个JavaBean,代码如下所示:
示例代码1-9 beans.xml
然后将@ImportResource标注在一个配置类上,写法为@ImportResource(locations={"classpath:beans.xml"}),具体参见启动类代码:
示例代码1-10 SpringBoot02ConfigApplication.java(文件的部分代码或者文件的核心代码)
@ImportResource注解必须使用在有@Configuration注解的类上(@SpringBootApplication继承自@Configuration)。虽然该方法可行,但是在Spring Boot中更推荐使用@Configuration注解的配置类来往容器中添加组件,Spring Boot会自动将带有@Configuration注解的类视为容器配置类,并将类中使用@Bean注解的方法视为Bean的获取方法,@Bean注解的方法也可以写在@SpringBootApplication注解的主程序类中,所以开发过程中建议采用以下方式给容器添加组件:
(1)编写一个配置类,添加@Configuration注解即可。
(2)使用@Bean给容器中添加组件。
配置类代码如下所示:
示例代码1-11 MyAppConfig .java
我们在编写主配置文件的时候,文件名可以是application-{profile}.properties/yml,比如,可以有多个配置文件共存,如applicaiton.properties、application-dev.properties、application-test.properties等。默认使用application.properties配置文件。
如果是yml格式的配置文件,可以用“---”来划分文档块,每个文档块都被看作是一个profile,可以在主文档块中指定生效的profile,代码如下所示:
示例代码1-12 application.yml
(1)在配置文件中指定spring.profiles.active=dev。
(2)在实际生产环境中直接使用命令来启动项目,启动的同时可以指定激活的profile,命令行方式指定如下:
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev ;
可以直接在测试的时候,配置传入命令行参数。
(3)可以通过设置Java虚拟机参数的方式来激活指定的profile:
-Dspring.profiles.active=dev
Spring Boot启动之后会扫描以下位置的application.properties或者application.yml文件,以作为Spring Boot的默认配置文件。扫描位置有以下几个:
· –file:./config/
· –file:./
· –classpath:/config/
· –classpath:/
优先级由高到低,高优先级的配置会覆盖低优先级的配置,Spring Boot会加载这四个位置的全部主配置文件。
另外,还可以通过spring.config.location来改变配置文件的默认位置。项目打包好以后,我们可以使用命令行参数的形式,在启动项目时指定配置文件的新位置,指定的配置文件会和默认加载的配置文件共同起作用,形成互补配置,参考命令如下:
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
Spring Boot也可以从以下位置加载配置,由于高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置。
(1)命令行参数
所有的配置都可以在命令行中指定:
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
多个配置用空格分开。
(2)来自java:comp/env的JNDI属性。
(3)Java系统属性(System.getProperties())。
(4)操作系统环境变量。
(5)RandomValuePropertySource配置的random.*属性值。
(6)jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件。
(7)jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件。
(8)jar包外部的application.properties或application.yml(带spring.profile)配置文件。
(9)jar包内部的application.properties或application.yml(不带spring.profile)配置文件。
(10)@Configuration注解类上的@PropertySource。
(11)通过SpringApplication.setDefaultProperties指定的默认属性。
总的来说,生效的配置文件是jar包外的优先,其次才是jar包内的配置文件,即优先加载带有profile的文件,再加载不带有profile的文件。比如,有个application.properties的配置文件和xxx.jar放置在同一个目录,那么项目启动时一定会先加载application.properties文件,再加载xxx.jar里面的配置文件。
Spring Boot如何能做到简化配置,提供JavaEE的一站式解决方案,很大程度上在于其自动配置,下面分析一下其自动配置的原理。
自动配置原理
Spring Boot启动的时候加载主配置类,开启了自动配置功能。@SpringBootApplication是一个复合注解或派生注解。在@SpringBootApplication中有一个注解@EnableAutoConfiguration,其实简单来说就是开启自动配置,这个注解就是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
这个spring.factories文件也是一组一组的key=value的形式,比如,其中一个key是EnableAutoConfiguration类的全类名,它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,以下是部分类名:
Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConf iguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfigu ration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
这个@EnableAutoConfiguration注解通过@SpringBootApplication间接地标记在Spring Boot的启动类上。在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
每一个这种xxxAutoConfiguration类都是容器中的一个组件,它们具有自动配置功能,加入到容器中进行自动配置。
这里以HttpEncodingAutoConfiguration(HTTP编码自动配置)为例解释自动配置原理,该配置类代码及解释如下:
每一个AutoConfiguration自动配置类都是在某些条件下才会生效,这些条件的限制在Spring Boot中以注解的形式来体现,常见的条件注解有如下几项:
· @ConditionalOnBean:当容器里有指定Bean的条件下。
· @ConditionalOnMissingBean:当容器里不存在指定Bean的条件下。
· @ConditionalOnClass:当类路径下有指定类的条件下。
· @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
· @ConditionalOnProperty:指定的属性是否有指定的值。
根据当前不同的条件判断来决定这个配置类是否生效,如果这个配置类生效,那么该配置类就会在容器中添加各种组件,而这些组件的属性从对应的properties类中获取,这些properties类中的每一个属性又是和配置文件绑定的。所有在配置文件中能配置的属性,都封装在对应的Properties类中,因而配置文件能配置什么属性可以参照某个功能对应的属性类。如HttpEncodingProperties类定义如下:
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的 值和Bean的属性进行绑定 public class HttpEncodingProperties { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
Spring Boot启动时,会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并加载这些自动配置类,这些自动配置类都是以AutoConfiguration结尾来命名的,每一个自动配置类实际上都是一个JavaConfig形式的Spring容器配置类,它们能从以Properties结尾命名的类中取得在全局配置文件中配置的属性,如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。