SpringBoot学习笔记之二-配置相关

SpringBoot的配置文件相关学习

以下两个都是全局配置文件:

application.properties

application.yml或application.yaml

修改SpringBoot自动配置的默认值;底层会给一些自动配置,比如想要改端口的话,就得修改配置文件;

YAML YAML Ain't Markup Language概念:一种是又不是的标记化语言。很奇怪哎

标记语言:

以前的配置文件一半都是xxx.xml文件,YAML以数据为中心

yml修改端口号的方法见.yml文件,如下图:

properties修改端口号的方法,如下图:

YAML基本语法

基本语法:

(1)key : value (key空格,然后冒号:再写一个空格,再写值)

(2)以空格的缩进来控制层级关系,左缩进空格同宽的元素 都是同层级的配置

(3)属性和值都是大小写敏感的。

以下是demo:

server:

port : 8081

path : /hello

值的写法

(1)字面值:普通的值(数字,字符串,布尔值) 这些类型的表示方法:

key: value 字面值直接来写,字符串类型的值不用加单引号或双引号。

双引号“”和单引号’‘含义不同:带了双引号的字符串,本字符串不会被转义。但是单引号包装字符串的话特殊字符就依然是普通字符。比如一个name:“张三\n李四”,如果用双引号,则结果会会换行,而使用单引号会输输出:张三\n李四

(2)对象、Map;

当值类型是对象的时候,key : value 在下一行写属性和值的关系,但是要留意缩进:

比如:这个例子,friends是个对象,friends跟:之间会有一个空格,先严格按照以下配置拷贝去写

firends :

lastName: zhangsan

age: 20

另外一种定义对象 和属性值的行内写法格式:

friends2 : { lastName : zhangsan, age : 20}

(3)数组(List,Set)

第一种写法,用-值表示数组中的一个元素,或者[]普通定义法,以下是两种写法:

pets :

- cat

- dog

- pig

pats : [cat, dot, pig]

配置文件值的注入

如下是测试的yml配置文件,为了对象绑定值:

person :

lastName : 张三

age : 18

boss : false

birth : 2017/12/12

maps : { key1 : value 1, key2 : 2 }

list :

- 李四

- 王五

dog :

name : 小狗

age : 2

如下是待绑定的对象Person定义:

package com.example.userinitialzr.demo.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

import java.util.Date;

import java.util.List;

import java.util.Map;

/**将.yml配置的每一个属性值 映射到java对象属性中

@ConfigurationProperties 这个注解 把配置文件中的每个属性跟对象属性关联,prefix参数值确定 要映射配置文件中哪个配置中的值映射过来

只有这个组件是荣国旗中的组件才能提供容器中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> list;

private Dog dog;

@Override

public String toString() {

return "Person{" +

"lastName='" + lastName + '\'' +

", age=" + age +

", boss=" + boss +

", birth=" + birth +

", maps=" + maps +

", list=" + list +

", 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> getMaps() {

return maps;

}

public void setMaps(Map<String, Object> maps) {

this.maps = maps;

}

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;

}

}

里面的子对象Dog的定义:

package com.example.userinitialzr.demo.bean;

public class Dog {

private String name;

@Override

public String toString() {

return "Dog{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

private Integer age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

}

最后可以自默认的测试目录的测试类去验证,项目启动时打印待绑定的对象的值,如下图:

如图是测试效果,其中IDEA的类和方法名的前面有个绿色的小箭头,右键提示可以直接运行该类或该方法,很方便,如下图:

YAML注释的关键字#,如下:

#person :

# lastName : 张三

以.properties方式配置person的值:

server.port=8081

# 配置person的值

person.lastName=张三

person.age=12

person.birth=2018/12/24

person.list=a,b,c

person.boss=false

person.maps.k1=v1

person.maps.k2=v2

person.dog.name=小狗

但需要注意的是,这个配置方式需要指定下文件的编码格式,指定文件运行时转为ASCII码才可以正常读取中文,如下图:

注意一下属性绑定的写法,以下是一些松散语法:

(1)标准写法 person.firstName

(2)大写用-替代,即 person.first-name

(3)大写用_替代,即 person.first_name

(4)当时系统属性的时候推荐这样写:PERSON_FIRST_NAME

具体针对数据校验的demo:当从配置文件读取时,如下demo演示了,读取properties的配置,使用@Email注解来验证数据是否是邮件类型的时候,直接获取person属性的值是会报错的,如下图:

通过@Value为属性赋值的三种方式:

@Component

@ConfigurationProperties(prefix = "person")

@Validated//表示要校验字段了

public class Person {

/*

bean对象的取值映射方法:第一种方法${}

*/

@Email //通过这个注解来校验 数值

@Value("${person.last-name}") //只能用这种写法,用person.lastName写法会报错,但是在.properties各种写法都支持

private String lastName;

/**

* 第二种取值方法 #{},大括号内传入一个表达式

*/

@Value("#{12*3}")

private Integer age;

/**

* 第三种取值方式,直接赋值

*/

@Value("true")

private Boolean boss;

注意:Java bean的对象配置值的方式比较(使用.properties方式和@Value的方式):

 

@ConfigurationProperties(就是写.properties文件或.yaml文件的方式)

@Value(直接在对象的属性前面加注解)

功能

批量注入配置文件中的属性

一个个指定属性的值

松散绑定(松散语法)

支持

不支持

SpEL(Spring写法,如#{}表达式)

不支持

支持

GSR303数据校验

支持

不支持

复杂类型封装

支持

不支持

但是相同点:

(1)两种方式(配置文件和 @Value的方式 )都可以获取到值

总结,两种配置方式该什么时候使用呢?

(1)如果只是在某个业务逻辑中获取一下配置文件中的某个值,可以使用@Value

(2)如果专门写了一个java bean(一个.java)专门来处理数据映射,那就应该使用.properties去配置最完备的数据

配置文件的一些关键字:

@PropertySource和@ImportResource

@PropertySource含义:加载指定的配置文件,因为@ConfigurationProperties的注解默认读取了全局的配置文件,如下图:

该关键字用法:

@Component

@PropertySource(value = {"classpath:application.properties"}) //加载指定的配置文件,使得该bean从该配置文件去绑定属性

@ConfigurationProperties(prefix = "person") //如果前面没有@PropertySource注解的话,读取的是全局的配置文件

//@Validated//表示要校验字段了

public class Person {

/*

bean对象的取值映射方法:第一种方法${}

*/

// @Email //通过这个注解来校验 数值

// @Value("${person.last-name}") //只能用这种写法,用person.lastName写法会报错,但是在.properties各种写法都支持

private String lastName;

@ImportProperties导入配置文件,让配置文件的内容生效

Spring Boot里没用的配置文件,默认是不生效的。把这个注解标注在配置类上,让配置文件生效

@ImportResource(locations = {"classpath:beans.xml"})

Sping Boot推荐给容器添加组件的方式:其中beans.xml是我自己写的配置文件,跟默认.properties同级文件夹(DemoApplication是我的主程序类)

package com.example.userinitialzr.demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.ImportResource;

@ImportResource(locations = {"classpath:beans.xml"})//导入自己写的配置文件

@SpringBootApplication

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

其中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组件 ,配置bean的格式:id:bean的对象名,class:该类所在的包路径-->

<bean id="helloService2" class="com.example.userinitialzr.demo.service.HelloService"></bean>

</beans>

测试配置是否成功加载的测试类:

package com.example.userinitialzr.demo;

import com.example.userinitialzr.demo.bean.Person;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.context.ApplicationContext;

import org.springframework.test.context.junit4.SpringRunner;

/**

* 这是SpringBoot的单元测试类

*

* 可以在测试期间方便的,进行自动注入容器的功能

*/

@RunWith(SpringRunner.class) //使用Spring的驱动器去跑。什么是驱动器不知道

@SpringBootTest //标识这是一个单元测试类

public class DemoApplicationTests {

@Autowired

Person person;

@Autowired

ApplicationContext ioc;

@Test

public void contextLoads() {

// System.out.println("yanruTODO person="+person+"@"); //打印看看绑定完的person对象的值

}

//判断容器中有没有HelloService

@Test //添加了@Test这个注解,当运行该类的时候就会跑这个测试函数

public void testHelloService()

{

boolean b = ioc.containsBean("helloService2");

System.out.println("是否存在helloService?"+b);

}

}

Spring Boot推荐的配置文件方式:直接使用配置类来配置值

package com.example.userinitialzr.demo.config;

import com.example.userinitialzr.demo.service.HelloService;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

//指明当前类是一个配置类。

@Configuration

public class MyAppConfig {

//将方法的返回值添加到容器中,容器中的这个组件的默认id,就是函数的名称

@Bean

public HelloService helloServiceYanru()

{

System.out.println("给容器中添加组件");

return new HelloService();

}

}

配置文件中的占位符

随机数,如下${random.uuid}

占位符获取之前配置的值,如果没有,使用:冒号指定默认值,如下图:

Profile

功能:用来实现Spring Boot灵活选择生产环境还是测试环境

配主配置文件编写的时候,文件名可以是application-(profile).properties/.yml

默认调取的配置文件时application.properties文件

比如我创建了两个配置文件,区别在于使用的端口不同,如下图:

在主配置文件中使用如下代码去指定使用哪个配置文件:

(1)第一种方式:server.profiles.active=dev(在我本地一配置这个 启动就报错了)

server.port=8081

##指定使用哪一个 配置文件

#server.profiles.active=dev

(2)第二种方式,命令行方式:

在项目启动项属性设置 指定系统参数:--spring.profiles.active=dev,如下图:

(3)jar包运行命令行的方式:

java -jar XXX.jar --spring.profiles.active=dev

(4)虚拟机参数:

-Dspring.profiles.active=dev

如下图:

Spring Boot配置文件加载设置

配置文件的存放位置:

(1)file:.config/

(2)file:./

(3)classpath:/config/

(4)classpath:/

以上按照优先级从高到低的顺序,所有的配置文件都会被加载,高优先级的配置文件覆盖低优先级的

使用spring.config.location改变配置文件的位置

项目打包以后,使用命令行参数的形式,“热更新”配置来实现对服务器的部分修改:

#指定配置文件的位置

spring.config.location=/application.properties

此外,除了项目内的资源,Spring Boot还可以加载外部的配置文件,可以详细的查看官方说明。

此外,当多个配置文件同时存在被加载的时候,形成互补配置的状态,冲突的部分会由高优先级的覆盖低优先级的配置文件;

当同时存在带profile的和不带profile的配置文件,会优先加载带Profile的配置。

命令行启动项目时指定参数:

java -jar D:\MyProjects\SpringBootStudyFromProginn2\demoForConfigLoad\target\demo-0.0.1-SNAPSHOT.jar --spring.context.path=/abc -- server.port=8087

自动配置的原理

配置文件能配置的属性,参见Spring Boot官网。

自动配置原理

(1)Spring Boot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration

(2)@EnableAutoConfiguration的作用:

利用EnableAutoConfigurationImportSelector给容器中导入一些组件

可以查看selectImports()方法的内容

获取候选的配置方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

return configurations;

}

一些重要的函数说明:

List<String> configurations = SpringFactoriesLoader.loadFactoryNames()

将类路径下META-INF/spring.factories里面配置的所有的EnableAutoConfiguration的值加入到了容器中。

每一个XXXAutoConfiguration类都是容器中的一个组件,都加入到容器中,通过他们来实现自动配置

(3)每一个自动配置类,进行自动配置功能;

(4)以HttpEncodingAutoConfiguration为例解释自动配置的原理:

@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件

@EnableConfigurationProperties(HttpEncodingProperties.class);//启动指定类的

//ConfigurationProperties功能:

@ConditionalOnWebApplication //作用是根据不同的条件,如果满足指定的条件,整个配置类才会生效,这个注解是判断当前应用是否Web应用,是的话,配置生效

@ConditionalOnWClass(CharacterEncodingFilter.class); //判断当前的项目有没有这个类,CharacterEncodingFilter是乱码的过滤器

@ConditionalOnProperty(prefix="string.http.encoding", value="enabled" matchIfMissing=true) //判断配置文件是否存在某个配置,string.http.encoding.enabled这个属性值,matchIfMissing=true表示,即使没有.enabled这个属性,这个配置也生效

public class HttpEncodingAutoConfiguration{

private final HttpEncodingProperties properties;

//只有一个有参构造器的情况下,参数的值就会从容器中拿

public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {

this.properties = properties;

}

@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取

@ConditionalOnMissingBean({CharacterEncodingFilter.class})

public CharacterEncodingFilter characterEncodingFilter() {

CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();

filter.setEncoding(this.properties.getCharset().name());

filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));

filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));

return filter;

}

(5)所有在配置文件中的配置的属性都是在XXXProperties类中封装者,配置文件能配什么就可以参照某个功能对应的这个属性类

一旦这个配置类生效,这个配置类就会向容器中添加各种各样的组件;

Spring Boot的精髓:

(1)Sprinig Boot启动会加载大类的自动配置类;

(2)看我们需要的功能有没有Spring Boot默认写好的自动配置类

(3)我们再来看这个自动配置中到底配置了哪些组件:只要我们要用的组件有,就不再需要配置了

(4)给容器中自动配置类添加组件的时候,会从propertie类中获取某些组件,我们就可以在配置文件中指定这些属性的值;

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

xxxProperties中封装配置类所需要的属性

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

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,否则该组件不生效

举例:

@ConditionalOnJava:Java的版本是否符合要求

@ConditionOnMissingBean:判断容器中是否没有某个组件,没有的话,该条件成立

@ConditionalOnProperty:配置中是否配置了某个属性

等等

Q:我们怎么知道哪些自动配置类生效了?

在.properties属性配置中指定:

debug=true

项目启动时,控制台会打印自动配置类的debug信息,就方便知道到底哪些自动配置类生效了

log中的关键字:

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

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

 

 

 

猜你喜欢

转载自blog.csdn.net/Stephanie_1/article/details/86566531