配置文件位置

官方允许配置文件放在以下位置 ./表示当前目录下的文件或文件夹 file表示项目路径 config表示建文件夹放配置件,无config的就直接建application.?文件

  1. file:./config项目路径下可以建一个config目录用于存放配置文件
  2. file:./表示可以将配置文件直接放项目路径下
  3. classpath:/config表示可以在类路径下创建一个config文件夹放配置文件,类路径即src/java。但是一般在同级的**resource**建config文件夹
  4. classpath:/配置文件直接放类路径下 即项目默认在**resource**目录下创建的配置

位置优先级1>2>3>4

优先级

  • 存在多个配置文件时,配置都会生效。如果存在冲突的配置,优先级高的生效
    • 格式优先级:properties>yaml>yml (先加载的冲突配置会被后加载的覆盖)
  • 读取默认配置文件时也跟优先级有关

    多环境配置

  • profile是spring对于不同环境提供不同配置的支持 ,可以通过激活不同环境版本实现快速切换环境

通过使用不同的配置文件来激活不同的环境
当我们在主配置文件编写的时候,文件名可以是 **application-{profile}.properties/yml **, 用来指定多个环境版本配置文件
例如:
**application-test.properties** 代表测试环境配置
**application-dev.properties** 代表开发环境配置

方法1-位置优先级

  • (配置文件不同位置)利用配置文件的位置优先级进行覆盖当前配置

配置文件详解 - 图1

方法2-配置指定

  • properties版

(配置文件同一位置)在一个文件夹中创建多个配置文件,此时优先级最高的是**application.properties**,即优先级最高的是默认的配置文件 在该优先级最高的配置文件中指定使用其他配置文件
例如:在resource目录下存在**application.properties** **application-test.properties** **application-dev.properties** 这样三个文件,那么我们可以在默认配置文件application.properties中以如下配置指定使用其他配置文件
**spring.profiles.active=profile**
如我们要使用dev环境,就**spring.profiles.active=dev**

  • yaml版 ```yaml

server: port: 8081

选择要激活哪个环境

spring: profiles: active: prod #激活prod名称的环境


server: port: 8083 spring: profiles: dev #设置配置环境的名称


server: port: 8084 spring: profiles: prod #配置环境的名称

  1. **---即表示创建一个新的环境配置,相当于properties里再创建一个新配置文件 **<br />**只有yaml里才可以多个环境写在一个文件里**<br />**但是配置庞大复杂时,还是建议分多个文件写**
  2. <a name="DmGJf"></a>
  3. # 配置文件使用简介
  4. SpringBoot使用一个**全局**的配置文件 配置文件**名称固定(命名不仅仅是决定名称,文件后缀决定了配置的语法格式**<br />**yaml)**
  5. - **必须以appliacation命名,其他名称无法识别**
  6. **application.properties 默认命名 默认命名只是指默认后缀是properties,只是讲后缀可以修改,名称前缀不可以改**
  7. - 语法结构 `key=value` `server.port=8080`
  8. **application.yml 官方推荐命名 后缀也可以是.yaml**
  9. - 语法结构 `key:空格 value` `server:port: 8080`
  10. **转为xml: **`**<server><port>8080</port></server>**`现在很少转化去用了
  11. **配置文件的作用 :修改SpringBoot自动配置的默认值 **如默认web服务器,web服务器默认端口 ...
  12. <a name="QLJsS"></a>
  13. # yaml优点
  14. propertiesyaml明显比xml简便。**为什么推荐yaml**而不是properties呢?<br />**因为yaml除了存储key-value,还能存对象。而properties只能存键值对.如**
  15. <a name="laLLT"></a>
  16. # yaml基础语法
  17. 说明:语法要求严格!
  18. 1. 空格不能省略
  19. 1. **以缩进来控制层级关系**,只要是左边对齐的一列数据都是同一个层级的。**缩进不当会产生歧义错误**
  20. 1. 属性和值的大小写都是十分敏感的。
  21. 1. **注释:还是**`**#...**`
  22. 1. 只要是`:或者-`后都要空格,空格可以不止一个
  23. **对象语法 Map类型可看作对象 类类型即对象语法**
  24. ```yaml
  25. student:
  26. name: rao
  27. age: 20
  28. #行内写法
  29. student: {name: rao,age: 20}

数组写法 List类型可看作数组

pets:
 - cat
 - dog
 - pig
#行内写法
pets: [cat,dog,pig]

读取配置文件

@ConfigurationProperties 批量注入

  • **@ConfigurationProperties**作用是将任意类型的配置文件注入实体类(默认是加载全局配置文件,只有一个配置文件时,则为全局 )
    • 注意是注入实体类,所以是将配置文件中的对象进行注入,对于单个属性注入不行
  • 支持yaml,其他还不确定
  • 貌似这种ConfigurationProperties方法必须有setter方法才能注入

原来我们是通过注解或者xml将配置文件中存储的实体类信息注入到实体类中。现在实体类信息存储在yaml中了

@Component //注册bean到容器中
@ConfigurationProperties(prefix = "person")   //指定注入的配置文件中的对象
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
      //一系列方法:有参无参构造、get、set方法、toString()方法   ...
}
  • 这里不知道为什么不写无参数构造,有参数构造那里就报错 错误信息为**could not autowire 没有找到?类型的beans**
  • 可能跟spring 构造器自动装配 有关
    @Component  //注册bean到容器中
    public class Dog {
      private String name;
      private Integer age;
      //有参无参构造、get、set方法、toString()方法  
    }
    
    person: {name: qinjiang,age: 3,happy: false,birth: 2000/01/01}
    #这是birth对象的写法,见Date类
    maps: {k1: v1,k2: v2}
    lists: [code,girl,music]
    dog: {name: 旺财,age: 1}
    name:rzd
    

    可能出现的错误

    写好后会有一个报错/警告(IDEA 提示,springboot配置注解处理器没有找到)。提升你进入一个网站。告诉你需要添加一个依赖。添加即可 依赖如下 ```xml

org.springframework.boot spring-boot-configuration-processor true

```java
@SpringBootTest
class SpringbootApplicationTests {
    @Autowired
Person person;
    @Test
    void contextLoads() {
        System.out.println(person);
    }
}

@ConfigurationProperties进行校验

  • 这种运用可以对配置文件注入进行限制,不正确的数据注入了对象,该对象被使用就会报错

详细

my-profile:
  name:Guide哥
  email:koushuangbwcx@
publicclass ProfileProperties {
   @NotEmpty
   private String name;

   @Email
   @NotEmpty
   private String email;

   //配置文件中没有读取到的话就用默认值
   private Boolean handsome = Boolean.TRUE;

}
@SpringBootApplication
@EnableConfigurationProperties(ProfileProperties.class)
publicclass ReadConfigPropertiesApplication implements InitializingBean {
    privatefinal ProfileProperties profileProperties;

    public ReadConfigPropertiesApplication(ProfileProperties profileProperties) {
        this.profileProperties = profileProperties;
    }

    public static void main(String[] args) {
        SpringApplication.run(ReadConfigPropertiesApplication.class, args);
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println(profileProperties.toString());
    }
}

@Value 单注入

  • @Value("${name}") String name 就这样实现了注入
  • 对于yamlproperties都能用
    • 可以添加默认值@Value("${name:rzd}") 当配置文件中name不存在时,使用默认值rzd
    • @value除了作用于字段,也可以作用于方法参数上,如
      @Bean
      ZoneId createZoneId(@Value("${app.zone:Z}") String zoneId) {
      return ZoneId.of(zoneId);
      }
      

  • @value除了读取配置文件注入字段,还可以读取一个bean的某个信息注入给字段。 ```java @Component public class SmtpConfig { @Value(“${smtp.host}”) private String host;

    @Value(“${smtp.port:25}”) private int port;

    public String getHost() {

      return host;
    

    }

    public int getPort() {

      return port;
    

    } }

//—————————————————————- @Component public class MailService { @Value(“#{smtpConfig.host}”) //表示从smtpConfig这个bean中读取host字段并注入,原理是调用get方法 private String smtpHost;

@Value("#{smtpConfig.port}")
private int smtpPort;

}

<a name="cFsLO"></a>
## @PropertySource()  读取配置文件

- 这是spring中的注解,不同于上面两种方式,**它的作用仅仅是读取配置文件,不会自动注入**。所以还需要配合`@Value`使用        
```java
@Configuration
@ComponentScan  //添加被扫描
@PropertySource("app.properties") // 表示读取classpath的app.properties
public class AppConfig {
    @Value("${app.zone:Z}")   //注入
    String zoneId;

    @Bean
    ZoneId createZoneId() {
        return ZoneId.of(zoneId);
    }
}
或者
@Bean
ZoneId createZoneId(@Value("${app.zone:Z}") String zoneId) {
    return ZoneId.of(zoneId);
}

可能遇到的读取value都是null的坑

  • 链接

    Environment

    @Autowired
      private Environment environment;
    
      @RequestMapping("/index")
      public String index(String hiName) {
          // 读取配置文件
          String appName = environment.getProperty("app.name");
          return "Hello, " + hiName + " |@" + appName;
      }
    

    Se中读取classpath资源

    try (InputStream input = getClass().getResourceAsStream("/default.properties")) {  
      if (input != null) {...}    //文件不存在返回空,不会报错
    }
    

    注入方式对比


    配置文件详解 - 图2
    1.@ConfiguretionProperties:只需要编写一次即可。而@Value需要每个字段都添加
    2、松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下
    3、JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
    4、复杂类型封装,yml中可以封装对象 , 使用value就不支持

适合需要获取字段多的项目,可以专门编写一个JavaBean来获取值,这时使用该配置注解好用非常多

配置文件占位符

  • 占位符yaml/properties都适用
  • 配置文件还可以编写使用些系统功能
  • 还可以yaml中定义变量,然后引用
  • ${}还可以读取jvm参数,如读取java命令执行时传入的参数
  • **@?@** 可以引用同项目的pom文件里的标签值,?为标签名,该标签必须为最小一级标签,即标签里没有其他标签(特殊标签除外,如profileActive)

    person:
      name: qinjiang${random.uuid} # 随机uuid
      age: ${random.int}  # 随机int
      happy: false
      birth: 2000/01/01
      maps: {k1: v1,k2: v2}
      lists:
        - code
        - girl
        - music
      dog:
        name: ${person.hello:other}_旺财
        age: 1
    
    mytest:
    domainName: https://www.jb51.net
    defaultHead: ${mytest.domainName}/head.jpeg
    
    redis:
      #数据库索引
      database: ${REDIS_DB:0}  //读取系统级参数REDIS_DB,未读取到就使用默认值0
      host: ${REDIS_HOST:127.0.0.1}
      port: ${REDIS_PORT:6379}
      password: ${REDIS_PWD:}
      #连接超时时间
      timeout: 6000
    
      java -jar -DREDIS_HOST=172.16.0.36 -DREDIS_DB=2 xxx.jar  #貌似追加参数需要额外添加 -D
    

    @PropertySource指定配置文件

  • 只支持properties文件

  • 配置文件中指定的默认配置文件是全局配置,而该注解是局部指定

    //再以上基础上再新建一个properties文件,此时要指定配置文件。使用@PropertySource
    @PropertySource(value = "classpath:person.properties")
    @Component //注册beanpublic 
    class Person {
      @Value("${person.name}")    
      private String name;
      ......  }
    

    使用properties时写中文可能会出现乱码,需要额外到设置里搜索properties然后设置下编码为utf-8

    配置详细

  • 配置应用服务器访问端口

    server.port=8888
    
  • 修改项目访问路径

    server.servlet.context-path=/springboot-demo
    
    server:
    port: 8080
    servlet:
      context-path: /demo
    

    读取配置文件

    Resouce法

  • Spring提供了一个org.springframework.core.io.Resource可以用于读取配置文件( 注意不是javax.annotation.Resource),它可以像String、int一样使用@Value注入

  • 路径一般有两种写法:
    • @Value("classpath:/logo.txt") classpath一般指src/main/resources目录
    • @Value("file:/path/to/logo.txt")

  • @PostConstruct:该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行
    • 一般用于需要在初始化bean前执行某些操作
  • Resource.getInputStream()就可以获取到输入流

    @Component
    public class AppService {
      @Value("classpath:/logo.txt")
      private Resource resource;
    
      private String logo;
    
      @PostConstruct
      public void init() throws IOException {  //此init()方法非jdk中的那个init,仅仅表明“初始化之意”
          try (var reader = new BufferedReader(
                  new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
              this.logo = reader.lines().collect(Collectors.joining("\n"));
          }
      }
    }
    

    注解法

  • @PropertySource自动读取配置文件 ,一般使用properties格式的配置文件。该方法读取到的配置会用于ioc容器全局,任何bean都可以读取并引用

    • properties以键值对的方式存储信息,使用.可以直接获取子键
    • 使用:获取值
  • 使用@Value获取配置信息并注入
    • “${app.zone}”表示读取key为app.zone的value,如果key不存在,启动将报错;
    • “${app.zone:Z}”表示读取key为app.zone的value,但如果key不存在,就使用默认值Z。
    • @Value还可以注入到方法参数中
  • @Value还可以使用#{}从bean中读取成员属性。会调用成员属性对应的get方法
    • #{stmpConfig.host} stmpConfig是bean的名称,host是属性@Bean

ZoneId createZoneId(@Value(“${app.zone:Z}”) String zoneId) {
return ZoneId.of(zoneId);
}

  • 使用一个独立的JavaBean持有所有属性,然后在其他Bean中以#{bean.property}注入的好处是,多个Bean都可以引用同一个Bean的某个属性。例如,如果SmtpConfig决定从数据库中读取相关配置项,那么MailService注入的@Value(“#{smtpConfig.host}”)仍然可以不修改正常运行。