配置文件
配置文件
SpringBoot使用一个全局的配置文件,配置文件名是固定的
- application.properties
- application.yml
YAML语法
基本语法
k:(空格)v:表示一对键值对(空格必须有)
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一层级的
server:
port: 8081
path: /hello
属性和值是大小写敏感
值的写法
字面量:普通值(数字,字符串,布尔)
k: v:字面直接来写
字符串默认不用加上单引号或者双引号
-
“”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
-
‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
对象、Map(属性和值)(键值对):
k: v:在下一行来写对象的属性和值的关系;注意缩进
对象还是k: v的方式
dog:
name: Tom
age: 12
maps: {k1: v1,k2: v2}
数组(List、Set):
用- 值表示数组中的一个元素:
lists:
- lisi
- zhaoliu
lists: [list,zhaoliu]
Properties语法
使用小细节
logo.location=/image/logo/
mail.host=webmaster@zlex.org
site.name=zlex中文网站
welcome=欢迎您,{0}!
ClassPathResource resource = new ClassPathResource("abc.properties");
Properties properties = new Properties();
properties.load(resource.getInputStream());
properties.forEach((k, v) -> System.out.println(k + "=" + v));
解决中文乱码问题: 使用Reader
读取
ClassPathResource resource = new ClassPathResource("abc.properties");
Properties properties = new Properties();
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8));
properties.load(reader);
properties.forEach((k, v) -> System.out.println(k + "=" + v));
Properties 文件中可议使用:吗?
logo.location:/image/logo/
mail.host:webmaster@zlex.org
site.name:zlex中文网站
welcome:欢迎您,{0}!
=/:中间可以有N个空格吗?
logo.location: /image/logo/
mail.host :webmaster@zlex.org
site.name : zlex中文网站
welcome:欢迎您,{0}!
发现打印的结果一模一样。结论:
中间
有N个空格,都是无所谓的
头、尾有空格呢?
logo.location:/image/logo/
mail.host:webmaster@zlex.org
site.name:zlex中文网站
welcome:欢迎您,{0}!
头部N个空格都没关系,但是尾部的空格是会当作值的一部分的。这个特别注意,很多人在这里踩过坑,因为这个错误还非常的不好找~
值注入
可以导入配置文件处理器,以后编写配置就有提示
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
yaml写法:
person:
lastName: 王锐
age: 18
boss: false
birth: 2017/12/12
maps: {k1: v1,k2: v2}
lists:
- lisi
- zhaoliu
dog:
name: Tom
age: 12
property写法:
person.last-name=王锐
person.age=18
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=Tom
person.dog.age=15
@ConfigurationProperties
ConfigurationPropertiesBindingPostProcessor
会对标注@ConfigurationProperties
注解的Bean进行属性值的配置
@ConfigurationProperties把同类的配置信息自动封装成实体类
-
加在类上,和@Component注解结合使用。代码如下
@Component @ConfigurationProperties(prefix = "person") public class Person { private String lastName; private Integer age; private Boolean boss; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; }
public class Dog { private String name; private Integer age; }
-
通过@Bean的方式进行声明,加在配置类即可,代码如下
@Bean @ConfigurationProperties(prefix = "person") public Person person() { return new Person(); }
@Value
@Value注解的注入非常强大,可以借助配置文件的注入、也可以直接注入
${ properties }
和#{ SpEL }
的语法区别
- ${ property : default_value }
- #{ obj.property? : default_value } 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还可以表示常量
二者组合使用:#{ ‘${}’ }
两者结合使用,可以利用SpEL的特性,写出一些较为复杂的表达式,如:
@Value("#{'${os.name}' + '_' + person.name}")
private String age; // Windows 10_wangrui
-
注入普通字符串
@Value("true") private Boolean boss; @Value("normal") private String normal;
-
注入操作系统属性
@Value("#{systemProperties['os.name']}") private String systemPropertiesName; @Value("${os.name}") private String systemPropertiesName;
-
注入配置文件值
@Value("${person.last-name}") private String lastName;
-
注入表达式结果
@Value("#{11*2}") private Integer age; @Value("#{ T(java.lang.Math).random() * 100.0 }") private double randomNumber; //41.29185128620939
-
注入其它Bean的属性:Person类的name属性
@Bean public Person person() { Person person = new Person(); person.setName("fangshixiang"); return person; } //注入属性 @Value("#{person.name}") private String personName; //如果age22这个key根本就不存在,启动肯定会报错的 @Value("#{person.age22?:20}") private Integer age;
-
直接注入容器内对象
发现这个很强大,可以直接把容器的里的一个对象直接注入进来。可是一般不这么做
@Value("#{person}") private Person person;
-
获取级联属性
@Value("#{person.parent.name}") private String parentName1; @Value("#{person['parent.name']}") private String parentName2;
-
注入文件资源
在resources下放置一个jdbc.properties配置文件。然后可以直接注入
@Value("classpath:jdbc.properties") private Resource resourceFile; // 注入文件资源
-
注入Url资源
@Value("http://www.baidu.com") private Resource testUrl;
@Value获取值和@ConfigurationProperties获取值比较
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
配置文件注入值数据校验
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
@Email
private String lastName;
}
@PropertySource&@ImportResource
@PropertySource
和@ImportResource
很多人都用过,但若真拿它两比较,却有点傻傻分不清楚了
他俩都是向容器中导入Bean/属性信息,但是使用起来还是有很大区别的
在讲解之前,可以记住一个通用的的结论:
@PropertySource
用于导入.properties
的属性配置文件(能导入yaml吗,且继续往下看吧)@ImportResource
用于导入.xml
的Bean信息的配置文件(能导入,properties吗,且继续看~)
@PropertySource
此注解也是非常的强大,用好了,可以很好的实现配置文件的分离关注
,大大提高开发的效率,实现集中化管理
属性介绍:
-
value:数组。指定配置文件的位置。支持
classpath:
和file:
等前缀如果是classpath开头最终使用Resource的子类ClassPathResource
如果是file开头最终使用的类是FileSystemResource
-
ignoreResourceNotFound:默认值false。表示如果没有找到文件就报错,若改为true就不报错。建议保留false
-
encoding:加载进来的编码。一般不用设置,可以设置为UTF-8等等
-
factory:默认的值为DefaultPropertySourceFactory
加载指定的配置文件;
// 加载的就是根路径下的person.properties文件
@PropertySource(value = {"classpath:person.properties"})
@Component
// 这个不能省略,它是用来指定前缀的,只是默认从全局配置文件中取值
@ConfigurationProperties(prefix = "person")
// @Validated
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
通过@PropertySource把配置文件加载进来,然后使用@Value获取
@Configuration
@PropertySource("classpath:jdbc.properties")
public class PropertySourceConfig {
@Value("${db.url}")
private String dbUrl;
}
多环境配置以及表达式使用(spring.profiles.active)
可以这么配置@PropertySource(“classpath:jdbc-${spring.profiles.active}.properties”)
,程序员在开发时不需要关心生产环境数据库的地址、账号等信息,一次构建即可在不同环境中运行
@ImportResource:
导入Spring的配置文件,让配置文件里面的内容生效
SpringBoot里面没有Spring的配置文件,自己编写的配置文件,也不能自动识别,想让Spring的配置文件生效、加载进来。需要将@ImportResource标注在一个配置类上
指示包含要导入的bean定义的一个或多个资源。它的功能比较像@Import
注解,就是向容器内导入Bean。只是@ImportResource
它导入的是一个xml配置文件,然后通过解析xml文件的方式再把解析好的Bean信息导入到Spring容器内
beans.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloService" class="com.example.demo.service.HelloService"></bean>
</beans>
将它在主配置类上引入或者在配置类上引入
@ImportResource(locations = {"classpath:beans.xml"})
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
@ImportResource(locations = {"classpath:beans.xml"})
@Configuration
public class MyConfig {
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot02ConfigApplicationTests {
@Autowired
ApplicationContext ioc;
@Test
public void testHelloService(){
System.out.println("注入一个组件");
boolean b = ioc.containsBean("helloService02");
System.out.println(b);
}
}
5、配置文件占位符
1、随机数
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
2、占位符获取之前配置的值,如果没有可以是用:指定默认值
person.last-name=张三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15