SpringBoot教程(三)(配置文件解析)_下

2018-07-24  本文已影响0人  文思li

      配置文件解析(下)

                                                                                                 原创者:文思

一、yml(YAML Ain’t Markup Language)基本用法

application.properties或application.yml

配置文件的作用:修改springboot的默认配置。

YAML:以数据为中心,比json,xml等更适合做配置文件

基本语法:

K:(空格)V :表示一对键值对。以空格的缩进来控制层级关系,只要是左对齐的一列数据,都是一个层级的。

值的写法:

字面量:普通的值(数据、字符串、布尔),k: v形式直接来写。如果有特殊字符,不需要转义可用双引号,需要转义可用单引号。

对象、Map:k: v形式直接来写,在下一行写对象的属性和值,注意缩进。

还可以使用行内写法:friends:{name:zhangsan,age: 18}

数组(List、Set):用- 值表示数组中的一个元素

示例:

新建一个yml配置文件:

server:

     port: 8081

person:

     lastName: zhangsan

     age: 18

     boss:  false

     birth:  2018/7/17

 map:  {t1:test1,t2:test2,t3:test3}

 map2:

   t1:  test11

   t2:  test22

   t3:  test33

list:

   -  lisi

   -  wangwu

   -  zhaoliu

 dog:

    name:  小狗

    age:  2

创建对应的对象:

@Component//声明为spring组件,让spring容器管理

@ConfigurationProperties(prefix="person")//默认从全局配置文件中获取值,告诉springboot将此类的属性与配置文件的配置绑定

public classPerson {

    private String lastName;

    private Integer age;

    private Boolean boss;

    private Date birth;

    privateMap map;

    privateMap map2;

    private List list;

    private Dog dog;

    public String toString(){

       return "Person{"+

              "lastName="+ lastName+

              ",age="+ age+

              ",boss="+ boss+

              ",birth="+ birth+

              ",map="+ map+

              ",map2="+ map2+

              ",list="+ list+

              ",dog="+ dog+

              "}";

    }

 .....setter\getter方法略

}

package com.wensi.springbootdemo1.yml;

public classDog {

    private String name;

    private Integer age;

    public String getName() {

       return name;

    }

 .....setter\getter方法略

}

运行测试类:

package com.wensi.springbootdemo1;

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.test.context.junit4.SpringRunner;

import com.wensi.springbootdemo1.yml.Person;

@RunWith(SpringRunner.class)

@SpringBootTest

public classAppTest{

    @Autowired

    Personperson;

    @Test

    public void contextLoads(){

       System.out.println(person);

    }

}

运行结果:

Person{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST 2018,map={t1=test1, t2=test2, t3=test3},map2={t1=test11,t2=test22, t3=test33},list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@796d3c9f}

使用properties格式的配置文件application.properties:

person.lastName=zhangsan

person.age=18

person.boss=false

person.birth=2018/7/17

person.map.t1=test1

person.map.t2=test2

person.map.t3=test3

person.list=lisi,wangwu,zhaoliu

person.dog.name=小狗

person.dog.age=2

运行测试类的结果:

Person{lastName=zhangsan,age=18,boss=false,birth=TueJul 17 00:00:00 CST 2018,map={t3=test3, t2=test2,t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@6f8d7714}

使用@value:

@Component

public class PersonValue {

    @Value("${person.lastName}")

    private String lastName;

    @Value("#{2*11}")

    private Integer age;

    @Value("true")

    privateBooleanboss;

运行结果:

Person{lastName=zhangsan,age=22,boss=true,birth=null,map=null,map2=null,list=null,dog=null}

@value和@ConfigurationProperties区别:

例如:

@Component

@ConfigurationProperties(prefix="person")

@Validated

public class Person {

    @Email

    private String lastName;//lastName必须是邮箱格式

    private Integer age;

    private Boolean boss;

    private Date birth;

运行结果:

Binding to targetPerson{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST2018,map={t3=test3, t2=test2, t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@7689ddef} failed:

    Property:person.lastName

    Value:zhangsan

    Reason: 不是一个合法的电子邮件地址

使用@value时:

@Component

@Validated

public class PersonValue {

    @Email

    @Value("${person.lastName}")

    private String lastName;

运行结果:

Person{lastName=zhangsan,age=22,boss=true,birth=null,map=null,map2=null,list=null,dog=null}。说明@value不支持数据校验

对于复杂数据类型:

@Component

public class PersonValue {

    @Value("${person.lastName}")

    private String lastName;

    @Value("#{2*11}")

    private Integer age;

    @Value("true")

    private Boolean boss;

    private Date birth;

    @Value("${person.map}")

    private Map map;

运行结果:

Caused by: java.lang.IllegalArgumentException:Could not resolve placeholder 'person.map' in value "${person.map}"

说明@value不支持复杂数据类型,如map。

使用建议:

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value。

如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties

@PropertySource和@importResource

根据名称分析@PropertySource可加载非默认的配置文件,即加载指定的配置文件,当我们想将一些特定业务所需要的配置与系统默认配置分开维护时,需要@PropertySource

@Component

@ConfigurationProperties(prefix="person")

@PropertySource(value={"classpath:person.properties"})

public class PersonPropertySource {

    private String lastName;

    privateInteger age;

运行结果:

Person{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST 2018,map={t3=test3, t2=test2,

t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@1b32cd16}

@importResource导入spring的配置文件,让配置文件里面的内容生效

因为springboot里面没有spring的配置文件,自己编写地配置文件也不能识别,所以需要使用@importResource加载进来。示例:

public class TestImportService {

}

bean.xml:

@SpringBootApplication

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

public class App

{

……

}

测试用例:

@Autowired

    ApplicationContext ioc;

    @Test

    public void testImportSource(){

       System.out.println(ioc.containsBean("testImportService"));

    }

运行结果:true

SpringBoot推荐给容器中添加组件的方式:推荐使用全注解的方式

1、配置类@Configuration------>Spring配置文件

2、使用@Bean给容器中添加组件,配置类这里使用@Bean注解

/**

* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件

** 在配置文件中用标签添加组件

*/

@Configuration

public class MyAppConfig {

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

        @Bean

        public HelloService helloService02(){

            System.out.println("配置类@Bean给容器中添加组件了...");

            return new HelloService();

        }

}

配置文件占位符

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.age=15

person.dog.name=${person.hello:hello}_dog 

如果${person.hello: hello}取不到值就默认值hello

多profile文件

应该对开发\测试\生产环境下切换

文件名可以是application-{profile}.properties/yml

默认使用的是application.properties/yml

激活指定profile

在配置文件中指定spring.profile.activie=dev,示例,新建application-dev.properties:

application.properties中增加spring.profile.activie=dev

Yml格式的多profile:yml支持文档块模式

spring:

  profiles:

    active: prod

---

server:

  port: 8083

spring:

  profiles: prod

---

server:

  port: 8084

spring:

  profiles:dev

运行如图:

spring.profiles.active是激活使用指定的配置文件。

另一种可以使用命令行的方式:java -jar ***.jar –spring.profiles.active=dev

还有一种虚拟机参数:-Dspring.profiles.active=dev

配置文件加载位置

Spring

boot启动会扫描以下位置的application.properties或yml作为spring

boot的默认配置文件

-file:./config/

-file:./

-classpath:/config/

-classpath:/

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

外部配置加载顺序

SpringBoot也可以从外部以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置

1.命令行参数

所有的配置都可以在命令行上进行指定

java -jarspring-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.*属性值由jar包外向jar包内进行寻找;优先加载带profile

6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

7.jar包内部的application-{profile}.properties或application.yml(spring.profile)配置文件再来加载不带profile

8.jar包外部的application.properties或application.yml(不spring.profile)配置文件

9.jar包内部的application.properties或application.yml(不spring.profile)配置文件

10.@Configuration注解类上的@PropertySource

11.通过SpringApplication.setDefaultProperties指定的默认属性

示例:

java

-jar springbootdemo1-0.0.1-SNAPSHOT.jar -server.port=8088,

启动端口就覆盖配置文件的里的端口设置了,启动端口为8088

二、自动配置原理

配置文件能配置的属性参照:

https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#common-application-properties

观察@ SpringBootApplication源码

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = {

       @Filter(type = FilterType.CUSTOM, classes =

TypeExcludeFilter.class),

       @Filter(type = FilterType.CUSTOM, classes =

AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication

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

2) @EnableAutoConfiguration利用EnableAutoConfigurationImportSelector选择器给容器导入META-INF/Spring.factories中的默认配置对应的组件。(可查看selectImports()方法-->

List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置-->

SpringFactoriesLoader.loadFactoryNames()//扫描所有jar包类路径下META‐INF/spring.factories把扫描到的这些文件的内容包装成properties对象从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中)

3)如上每一个自动配置类进行自动配置

4) 以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;

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

@EnableConfigurationProperties(HttpEncodingProperties.class)

//启动指定类的

ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把

HttpEncodingProperties加入到ioc容器中

@ConditionalOnWebApplication

//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果

满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效

@ConditionalOnClass(CharacterEncodingFilter.class)

//判断当前项目有没有这个类

CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;

@ConditionalOnProperty(prefix= "spring.http.encoding", value = "enabled", matchIfMissing=

true) //判断配置文件中是否存在某个配置spring.http.encoding.enabled;如果不存在,判断也是成立的

//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;

public class HttpEncodingAutoConfiguration{

//他已经和SpringBoot的配置文件映射了

private final HttpEncodingPropertiesproperties;

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

publicHttpEncodingAutoConfiguration(HttpEncodingProperties properties) {

this.properties =properties;

}

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

@ConditionalOnMissingBean(CharacterEncodingFilter.class)

//判断容器没有这个组件?

publicCharacterEncodingFilter characterEncodingFilter() {

CharacterEncodingFilter filter = newOrderedCharacterEncodingFilter();

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

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

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

return filter;

}

根据当前不同的条件判断,决定这个配置类是否生效?

一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的

这里能看出配置文件能配置的属性都是来源于HttpEncodingProperties类。

精髓:

1:spring boot启动会加载大量的自动配置类

2:我们看我们需要的功能有没有springboot默写好的自动配置类

3:我们看这个自动配置类中配置了哪些组件(我们要用的有就不用再配置了)

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

xxxAutoConfigurartion自动配置类,给容器中添加组件,xxxAutoConfigurartion下的xxxxProperties:封装配置文件中相关属性;

上一篇下一篇

猜你喜欢

热点阅读