一.配置文件
SpringBoot使用一个全局的配置文件,配置文件名是固定的:
- application.properties
- application.yml
配置文件的作用:修改SpringBoot自动配置的默认值(SpringBoot在底层都给我们配置好了)
例子:通过使用application.properties,修改SpringBoot默认的Tomcat端口,改为8081
修改配置前:Tomcat的启动端口为8080
修改配置后:Tomcat的启动端口为8081
application.properties的内容为:
server.port=8081
二.YAML
YAML简介
- YAML(YAML Ain't Markup Language)
- YAML is a Markup Language:是一个标记语言
- YAML isn't a Markup Language:不是一个标记语言
标记语言:
- 以前的配置文件,大多数都使用的是xxxx.xml文件
- YAML以数据为中心,比json、xml等更适合做配置文件
YAML语法
1.基本语法
- k:(空格)v:表示一对键值对(空格一定要有)
- 以空格的缩进来控制层级关系,只要是左对齐的一列数据,都是一个层级的
- 例如,上面的例子用YAML来写
server: port: 8081
- 属性和值都是大小写敏感的
2.值的写法
- 字面量:普通的值(数字、字符串、布尔)
- k: v
- 字符串默认不用加上单引号或者双引号
- "":双引号,不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思
- name: "zhangSan \n liSi",输出zhangSan换行liSi
- '':单引号,会转义字符串里面的特殊字符,特殊字符最终只是一个普通的字符串数据
- name: 'zhangSan \n liSi',输出zhangSan \n liSi
3.配置文件注入
可以先导入配置文件处理器,这样编写配置时就有提示
1 <!-- 导入配置文件处理器,配置文件进行绑定就会有提示 --> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-configuration-processor</artifactId> 5 <optional>true</optional> 6 </dependency>
JavaBean
1 package com.hmz.bean; 2 3 public class Dog { 4 5 private String name; 6 private Integer age; 7 8 public String getName() { 9 return name; 10 } 11 12 public void setName(String name) { 13 this.name = name; 14 } 15 16 public Integer getAge() { 17 return age; 18 } 19 20 public void setAge(Integer age) { 21 this.age = age; 22 } 23 24 @Override 25 public String toString() { 26 return "Dog{" + 27 "name='" + name + '\'' + 28 ", age=" + age + 29 '}'; 30 } 31 32 }
1 package com.hmz.bean; 2 3 import org.springframework.boot.context.properties.ConfigurationProperties; 4 import org.springframework.stereotype.Component; 5 6 import java.util.Date; 7 import java.util.List; 8 import java.util.Map; 9 10 /** 11 * 将配置文件中配置的每一个属性的值,映射到这个组件中 12 * @ConfigurationProperties 13 * 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定 14 * prefix = "person":配置文件中哪个下面的所有属性进行一一映射 15 * 16 * 17 * 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能 18 */ 19 @Component 20 @ConfigurationProperties(prefix = "person") 21 public class Person { 22 23 private String lastName; 24 private Integer age; 25 private Boolean boss; 26 private Date birth; 27 private Map<String,Object> map; 28 private List<Object> list; 29 private Dog dog; 30 31 public String getLastName() { 32 return lastName; 33 } 34 35 public void setLastName(String lastName) { 36 this.lastName = lastName; 37 } 38 39 public Integer getAge() { 40 return age; 41 } 42 43 public void setAge(Integer age) { 44 this.age = age; 45 } 46 47 public Boolean getBoss() { 48 return boss; 49 } 50 51 public void setBoss(Boolean boss) { 52 this.boss = boss; 53 } 54 55 public Date getBirth() { 56 return birth; 57 } 58 59 public void setBirth(Date birth) { 60 this.birth = birth; 61 } 62 63 public Map<String, Object> getMap() { 64 return map; 65 } 66 67 public void setMap(Map<String, Object> map) { 68 this.map = map; 69 } 70 71 public List<Object> getList() { 72 return list; 73 } 74 75 public void setList(List<Object> list) { 76 this.list = list; 77 } 78 79 public Dog getDog() { 80 return dog; 81 } 82 83 public void setDog(Dog dog) { 84 this.dog = dog; 85 } 86 87 @Override 88 public String toString() { 89 return "Person{" + 90 "lastName='" + lastName + '\'' + 91 ", age=" + age + 92 ", boss=" + boss + 93 ", birth=" + birth + 94 ", map=" + map + 95 ", list=" + list + 96 ", dog=" + dog + 97 '}'; 98 } 99 100 }
配置文件
person: lastName: hmz age: 23 boss: false birth: 1997/3/4 map: {k1: v1,k2: v2} list: - liSi - zhangSan dog: name: dog age: 2
运行结果
三.配置文件相关的注释
@Value获取值和@ConfigurationProperties获取值比较
@ConfigurationProperties | @Value | |
功能 | 批量注入配置文件中的属性 | 一个一个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 支持 | 不支持 |
JSR303 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
- 配置文件无论是yml还是properties,它们都能获取到值
- 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用Value
- 如果说,我们专门编写了一个JavaBean来和配置文件进行映射,使用ConfigurationProperties
- 上面例子的Person.java可以改成:
1 package com.hmz.bean; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.boot.context.properties.ConfigurationProperties; 5 import org.springframework.stereotype.Component; 6 7 import java.util.Date; 8 import java.util.List; 9 import java.util.Map; 10 11 /** 12 * 将配置文件中配置的每一个属性的值,映射到这个组件中 13 * 14 * @ConfigurationProperties 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定 15 * prefix = "person":配置文件中哪个下面的所有属性进行一一映射 16 * <p> 17 * <p> 18 * 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能 19 */ 20 @Component 21 @ConfigurationProperties(prefix = "person") 22 public class Person { 23 24 @Value("hmz") 25 private String lastName; 26 @Value("#{22+1}") 27 private Integer age; 28 @Value("false") 29 private Boolean boss; 30 private Date birth; 31 private Map<String, Object> map; 32 private List<Object> list; 33 private Dog dog; 34 35 public String getLastName() { 36 return lastName; 37 } 38 39 public void setLastName(String lastName) { 40 this.lastName = lastName; 41 } 42 43 public Integer getAge() { 44 return age; 45 } 46 47 public void setAge(Integer age) { 48 this.age = age; 49 } 50 51 public Boolean getBoss() { 52 return boss; 53 } 54 55 public void setBoss(Boolean boss) { 56 this.boss = boss; 57 } 58 59 public Date getBirth() { 60 return birth; 61 } 62 63 public void setBirth(Date birth) { 64 this.birth = birth; 65 } 66 67 public Map<String, Object> getMap() { 68 return map; 69 } 70 71 public void setMap(Map<String, Object> map) { 72 this.map = map; 73 } 74 75 public List<Object> getList() { 76 return list; 77 } 78 79 public void setList(List<Object> list) { 80 this.list = list; 81 } 82 83 public Dog getDog() { 84 return dog; 85 } 86 87 public void setDog(Dog dog) { 88 this.dog = dog; 89 } 90 91 @Override 92 public String toString() { 93 return "Person{" + 94 "lastName='" + lastName + '\'' + 95 ", age=" + age + 96 ", boss=" + boss + 97 ", birth=" + birth + 98 ", map=" + map + 99 ", list=" + list + 100 ", dog=" + dog + 101 '}'; 102 } 103 104 }
- 运行结果
@PropertySource、@ImportSource、@Bean
1.@PropertySource:加载指定的配置文件
package com.hmz.bean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; import java.util.Date; import java.util.List; import java.util.Map; /** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * * @ConfigurationProperties 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定 * prefix = "person":配置文件中哪个下面的所有属性进行一一映射 * <p> * <p> * 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能 */ @Component @ConfigurationProperties(prefix = "person") @PropertySource("classpath:person.properties") public class Person { private String lastName; private Integer age; private Boolean boss; private Date birth; private Map<String, Object> map; private List<Object> list; private Dog dog; public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Boolean getBoss() { return boss; } public void setBoss(Boolean boss) { this.boss = boss; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public Map<String, Object> getMap() { return map; } public void setMap(Map<String, Object> map) { this.map = map; } public List<Object> getList() { return list; } public void setList(List<Object> list) { this.list = list; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } @Override public String toString() { return "Person{" + "lastName='" + lastName + '\'' + ", age=" + age + ", boss=" + boss + ", birth=" + birth + ", map=" + map + ", list=" + list + ", dog=" + dog + '}'; } }
person.lastName=hmz person.age=23 person.boss=false person.birth=1997/3/4 person.map.k1=v1 person.map.k2=v2 person.dog.name=dog person.dog.age=2
运行结果:
2.@ImportSource:导入Spring的配置文件,让配置文件里面的内容生效
- SpringBoot里面没有Spring的配置文件,自己编写的配置文件,也不能自动识别
- 想让Spring配置文件生效,就必须加载进来
- @ImportSource标注在一个配置类上
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <bean id="helloService" class="com.hmz.service.HelloService"/> 7 8 </beans>
1 package com.hmz.service; 2 3 public class HelloService { 4 }
1 package com.hmz.config; 2 3 import org.springframework.context.annotation.Configuration; 4 import org.springframework.context.annotation.ImportResource; 5 6 /** 7 * @Configuration 8 * 指明当前类是一个配置类 9 * 用来代替之前的Spring配置文件 10 */ 11 @Configuration 12 @ImportResource(locations = "classpath:bean.xml") 13 public class MyAppConfig { 14 }
运行结果:
3.@Bean
- SpringBoot推荐使用全注解的方式来给容器添加组件
修改上面的MyAppConfig.java
1 package com.hmz.config; 2 3 import com.hmz.service.HelloService; 4 import org.springframework.context.annotation.Bean; 5 import org.springframework.context.annotation.Configuration; 6 7 /** 8 * @Configuration 9 * 指明当前类是一个配置类 10 * 用来代替之前的Spring配置文件 11 */ 12 @Configuration 13 public class MyAppConfig { 14 15 //将方法返回值添加到容器中,容器中默认的id就是方法名 16 @Bean 17 public HelloService helloService(){ 18 System.out.println("配置类给容器添加组件......"); 19 return new HelloService(); 20 } 21 22 }
配置文件占位符
修改上面的person.properties
person.lastName=hmz${random.uuid} person.age=${random.int} person.boss=false person.birth=1997/3/4 person.map.k1=v1 person.map.k2=v2 person.dog.name=${person.lastName}_dog person.dog.age=2
运行结果:
四.Profile
多Profile文件
- 在主配置文件编写的时候,文件名可以是application-(profile).properties/yml
- SpringBoot默认使用application.properties
yml支持多文档块方式
编写application.yml
server: port: 8081 spring: profiles: active: prod --- server: port: 8082 spring: profiles: dev --- server: port: 8083 spring: profiles: prod
激活指定的profile
- 在配置文件中指定,比如:spring.profiles.active=dev
- 命令行:java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
- 虚拟机参数:-Dspring.profiles.active=dev
五.配置文件加载的位置
SpringBoot启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件
- -file:/config/
- file:/
- classpath:/config/
- classpath:/
优先级由高到低,高优先级的配置会覆盖低优先级的配置
SpringBoot会从这四个位置全部加载主配置文件(互补配置)
也可以通过spring.config.location来改变默认的配置文件的位置
六.外部配置加载顺序
- 命令行参数 所有的配置都可以在命令行上进行指定 java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc 多个配置用空格分开; --配置项=值
- 来自java:comp/env的JNDI属性
- Java系统属性(System.getProperties())
- 操作系统环境变量
- RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找
优先加载带profile
- jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
- jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
再来加载不带profile
- jar包外部的application.properties或application.yml(不带spring.profile)配置文件
- jar包内部的application.properties或application.yml(不带spring.profile)配置文件
- @Configuration注解类上的@PropertySource
- 通过SpringApplication.setDefaultProperties指定的默认属性
七.自动配置原理
- SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
- @EnableAutoConfiguration的作用:
-
- 利用AutoConfigurationImportSelector给容器中导入一些组件
- 可以查看selectImports()方法的内容
- List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
- List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
扫描所有jar包类路径下META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把它们添加到容器中
-
- 每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中,用它们来自动配置
以HttpEncodingAutoConfiguration为例:
1 @Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件 2 @EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的 3 ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把 4 HttpEncodingProperties加入到ioc容器中 5 @ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果 6 满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效 7 @ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有这个类 8 CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器; 9 @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = 10 true) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的 11 //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的; 12 public class HttpEncodingAutoConfiguration { 13 //他已经和SpringBoot的配置文件映射了 14 private final HttpEncodingProperties properties; 15 71 16 72 17 73 18 74 19 75 20 76 21 77 22 78 23 79 24 80 25 81 26 82 27 83 28 84 29 85 30 86 31 87 32 88 33 89 34 90 35 91 36 92 37 93 38 94 39 95 40 96 41 97 42 98 43 1 44 2 45 3 46 4 47 5 48 6 49 7 50 8 51 9 52 10 53 11 54 12 55 13 56 根据当前不同的条件判断,决定这个配置类是否生效? 57 一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取 58 的,这些类里面的每一个属性又是和配置文件绑定的; 59 5)、所有在配置文件中能配置的属性都是在xxxxProperties类中封装者‘;配置文件能配置什么就可以参照某个功 60 能对应的这个属性类 61 精髓: 62 1)、SpringBoot启动会加载大量的自动配置类 63 2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类; 64 3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了) 65 4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这 66 些属性的值; 67 xxxxAutoConfigurartion:自动配置类; 68 给容器中添加组件 69 //只有一个有参构造器的情况下,参数的值就会从容器中拿 70 public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { 71 this.properties = properties; 72 } 73 @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取 74 @ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件? 75 public CharacterEncodingFilter characterEncodingFilter() { 76 CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); 77 filter.setEncoding(this.properties.getCharset().name()); 78 filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); 79 filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); 80 return filter; 81 }
- 根据当前不同的条件判断,决定这个配置类是否生效
- 一旦这个配置类生效,这个配置类就会给容器添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
- 所有在配置文件中能配置的属性都是在xxxProperties类中的封装者,配置文件能配置什么,就可以参照某个功能对应的这个属性类
1 @ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值和bean的属 2 性进行绑定 3 public class HttpEncodingProperties { 4 public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");
- SpringBoot启动会加载大量的自动配置类
- 注意所需要的功能有没有SpringBoot默认写好的自动配置类
- 注意这个自动配置类配置了那些组件
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,就可以在配置文件中指定这些属性的值
@Conditional派生注解(Spring注解版原生的@Conditional作用)
- 作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |