1

上一节:springboot开发笔记-(7)-Profile多环境配置文件加载


8.1 配置文件的优先级

server.servlet.context-path=/app
server.port=8888
spring.config.location=xxx/xxx/application.properties

下面本文中说到"级别高"意思就是会替换其他的, 以它为首选, 它最终会起作用, 其他的会被覆盖;

本节要回答的问题:springboot应用部署后, 配置文件以哪个为准? 假如应用打包成可执行app.jar后有以下这几个配置文件:

  1. jar同目录的 _application.properties_; ->官网称此为: file:./config/
  2. jar同目录的 _config/application.properties_; ->官网称此为: file:./config/
  3. jar内部jar包中的 _classes/application.properties_; ->官网称此为: classpath:/
  4. jar内部jar包中的 _classes/config/application.properties_; ->官网称此为: classpath:/config/
  5. 启动参数时直接覆盖某个单项配置, 比如改端口号: java -jar app.jar --server.port=9999来指定;
  6. 随便指定一个地址放置 application.properties , 使用 启动参数spring.config.location=xxx来指定; (注意: 这个配置实测会互补失效--即: 只有它自己起作用)

--let's guess:

我们先来做一个猜测, 如果是让我们自己设计, 想象实际上线的场景, 我们会期望哪个优先级最高(最终会覆盖其他配置的)?

如果我在线上部署了一个app.jar文件, 执行java -jar app.jar 启动应用; 启动之后线上如果有问题了, 比如端口被占用, 比如数据库配置修改, 我需要马上调整一个参数, 最快的方式是什么?

  1. 首先: 直接重启, java -jar app.jar --some.prop=new_value 肯定是这个最快, 或者干脆自己指定一个配置文件的地址, --参数加命令行后面; (上面5/6)
  2. 除了执行带参数, 方便修改的肯定是 jar平级目录下了, 那么, 应该轮到 app.jar平级目录下的配置了:

    是设置哪个级别高呢? 直接读取application.properties? 还是 config/application.properties?

    有的人认为 config很直观, 就把它下面的设为高级别---没错---springboot的设计者就是这类的(上面2);

  3. 当然 紧接着就是平级的了(上面1)
  4. 所以jar包里面的两处, 应该是低级别的了, 按照第2点的逻辑, 应该是 classpath:/config/application.properties 级别高于 classpath:application.properties;

所以我们就大概出来一个思路, 配置起作用优先级别依次是:

  1. 启动时指定某些参数

    java -jar app.jar --server.port=9999
  2. 启动时指定配置文件(有坑~)

    java -jar app.jar -spring.config.location=/mydir/application.properties
  3. jar同目录的config/application.properties

    app.jar 同级别目录下的 ./config/application.properties
  4. jar同目录的application.properties

    app.jar同级别目录下的 application.properties
  5. jar内部 classpath目录下的 ./config/application.properties

    app.jar/BOOT-INF/classes/config/application.properties
  6. jar内部classpath目录下的 application.properties

    app.jar/BOOT-INF/classes/application.properties

注意: 2处之所以说有坑是因为:

  1. 其他的配置文件只是优先级不同, 所有的配置文件还都是可以加载互补的, 只是优先级更高的会覆盖优先级低的
  2. 但是2处这个怂配置则不然: 假如你指定了 -spring.config.location=xxx , 你要小心了, 这相当于是个渣男: 他只顾自己, 不和其他的互补, 别的都被忽略了!!!

8.2 配置文件的优先级-代码证明

8.2.1 代码核心文件

  1. idea里新建一个springboot的module: springboot-02-config, 创建了4个配置文件, 配置对应关系如图:

image.png

  1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.niewj</groupId>
        <artifactId>springboot-02-config</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot-02-config</name>
        <description>springboot-2-config</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.8.6</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.10</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>app</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
  2. person.java

    package com.niewj.springboot.model;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
*/

@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

   private Integer age;
   private String lastName;
   private boolean student;
   private String location;

   private List<String> hobbies;

}


4. controller:

package com.niewj.springboot.controller;

import com.niewj.springboot.model.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**

* Created by niewj on 2020/8/5 23:17
*/

@RestController
public class HelloController {

   @Autowired
   private Person person;

   @RequestMapping("/hello")
   public Object hello(){
       return person;
   }

}


### 8.2.2 执行流程

1. 编译打包

> mvn clean install -Dmaven.test.skip=true

2. 打包后生成一个 `app.jar`可执行文件

3. `show in explore` 进入 app.jar所在目录, 我们选择把jar包复制到其他目录, 比如: E:/deploy/app.jar

4. 目录下`shift+右键->在此处打开命令行窗口`

>config(目录下有application.properties)
>app.jar
>application.properties

5. 在命令行`java -jar app.jar`

PS E:deploy> java -jar .app.jar

 .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )__ | ' | '_| | '_ / _` | \

\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
 '  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.2.2.RELEASE)

2020-08-06 13:23:53.011 INFO 20304 --- [ main] com.niewj.springboot.ConfigApplication : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 20304 (E:deployapp.jar started by niewj in E:deploy)
2020-08-06 13:23:53.015 INFO 20304 --- [ main] com.niewj.springboot.ConfigApplication : No active profile set, falling back to default profiles: default
2020-08-06 13:23:56.206 INFO 20304 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8004 (http)
2020-08-06 13:23:56.225 INFO 20304 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-08-06 13:23:56.226 INFO 20304 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2020-08-06 13:23:56.343 INFO 20304 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-08-06 13:23:56.343 INFO 20304 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3275 ms
2020-08-06 13:23:56.512 INFO 20304 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-06 13:23:56.721 INFO 20304 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8004 (http) with context path ''
2020-08-06 13:23:56.724 INFO 20304 --- [ main] com.niewj.springboot.ConfigApplication : Started ConfigApplication in 4.112 seconds (JVM running for 4.652)


可以看到, 使用的是端口: `8004 `, 再通过 http://localhost:8004/hello 访问, 显示: 

{"age":30,"lastName":"wj","student":false,"location":"file/config/application.properties","hobbies":["8004","running","coding","cooking"]}


--> 可见起作用的是: `app.jar同目录下的 config/application.properties`

6. 我们删掉这个目录再试一次: 

PS E:deploy> java -jar .app.jar

 .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )__ | ' | '_| | '_ / _` | \

\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
 '  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.2.2.RELEASE)

2020-08-06 13:27:45.312 INFO 7716 --- [ main] com.niewj.springboot.ConfigApplication : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 7716 (E:deployapp.jar started by niewj in E:deploy)
2020-08-06 13:27:45.315 INFO 7716 --- [ main] com.niewj.springboot.ConfigApplication : No active profile set, falling back to default profiles: default
2020-08-06 13:27:48.470 INFO 7716 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8003 (http)
2020-08-06 13:27:48.482 INFO 7716 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-08-06 13:27:48.483 INFO 7716 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2020-08-06 13:27:48.591 INFO 7716 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-08-06 13:27:48.592 INFO 7716 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3227 ms
2020-08-06 13:27:48.754 INFO 7716 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-06 13:27:48.954 INFO 7716 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8003 (http) with context path ''
2020-08-06 13:27:48.957 INFO 7716 --- [ main] com.niewj.springboot.ConfigApplication : Started ConfigApplication in 4.081 seconds (JVM running for 4.606)


可以看到, 使用的是端口: `8003`, 再通过 http://localhost:8003/hello 访问, 显示: 

{"age":30,"lastName":"wj","student":false,"location":"file/application.properties","hobbies":["8003","running","coding","cooking"]}


--> 可见起作用的是: `app.jar同目录下的 ./application.properties`

7. 删掉./application.properties再试:

PS E:deploy> java -jar .app.jar

 .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )__ | ' | '_| | '_ / _` | \

\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
 '  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.2.2.RELEASE)

2020-08-06 13:31:42.852 INFO 3700 --- [ main] com.niewj.springboot.ConfigApplication : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 3700 (E:deployapp.jar started by niewj in E:deploy)
2020-08-06 13:31:42.855 INFO 3700 --- [ main] com.niewj.springboot.ConfigApplication : No active profile set, falling back to default profiles: default
2020-08-06 13:31:46.018 INFO 3700 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8002 (http)
2020-08-06 13:31:46.032 INFO 3700 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-08-06 13:31:46.033 INFO 3700 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2020-08-06 13:31:46.144 INFO 3700 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-08-06 13:31:46.145 INFO 3700 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3239 ms
2020-08-06 13:31:46.306 INFO 3700 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-06 13:31:46.509 INFO 3700 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8002 (http) with context path ''
2020-08-06 13:31:46.514 INFO 3700 --- [ main] com.niewj.springboot.ConfigApplication : Started ConfigApplication in 4.051 seconds (JVM running for 4.579)


可以看到, 使用的是端口: `8002`, 再通过 http://localhost:8002/hello 访问, 显示: 

{"age":30,"lastName":"wj","student":false,"location":"resource/config/application.properties","hobbies":["8002","running","coding","cooking"]}


--> 可见起作用的是: `app.jar包内的classpath目录下的: classpath:config/application.properties`

8. 到此就不用再试了, 如果没有config目录, 肯定读取的是classpath: application.properties了, 这个我们平时用的就是它;

正好证实了上面的结论.

9. 我们再试试 `spring.config.location`命令行指定配置文件的执行方式: 在桌面上放了一个 application.properties, 内容如下:
  server.port=8333
  
  person.hobbies=8333,running,coding,cooking
  person.location=spring.config.location/application.properties
  
  ```
  1. java -jar app.jar --spring.config.location=C:\Users\weiju\Desktop\application.properties

    PS E:\deploy> java -jar app.jar --spring.config.location=C:\Users\weiju\Desktop\application.properties
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.2.RELEASE)
    
    2020-08-06 13:39:40.251  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 20548 (E:\deploy\app.jar started by niewj in E:\deploy)
    2020-08-06 13:39:40.254  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : No active profile set, falling back to default profiles: default
    2020-08-06 13:39:43.357  INFO 20548 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8333 (http)
    2020-08-06 13:39:43.368  INFO 20548 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-08-06 13:39:43.368  INFO 20548 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
    2020-08-06 13:39:43.452  INFO 20548 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 13:39:43.453  INFO 20548 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3142 ms
    2020-08-06 13:39:43.615  INFO 20548 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 13:39:43.805  INFO 20548 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8333 (http) with context path ''
    2020-08-06 13:39:43.809  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : Started ConfigApplication in 3.997 seconds (JVM running for 4.531)
    可以看到, 使用的是端口: `8333`, 再通过 http://localhost:8333/hello 访问, 显示: 
    
    ```json
    {"age":null,"lastName":null,"student":false,"location":"spring.config.location/application.properties","hobbies":["8333","running","coding","cooking"]}
    ```
    
    --> 同样: 证明了渣男属性 `spring.config.location` : 只顾自己, 别人都抛弃了, 没有了`互补`功能;
    

需要注意的几点:

  1. 我们可以看到他们之间除了优先级, 其他的是都加载的, 是互补的关系(命令行带参spring.config.properties除外)
  2. 上面的测试, 除了删除之外, 改名也生效, 名字不是 application.properties/application.yml都不会认的;
  3. 我们没有试命令行指定某个参数, 比如 java -jar app.jar --server.port=8080 这个; 它是第一优先级!

8.3 官网说明

4.2.3. Application Property Files


SpringApplication loads properties from application.properties files in the following locations and adds them to the Spring Environment:

  1. A /config subdirectory of the current directory
  2. The current directory
  3. A classpath /config package
  4. The classpath root

The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).

......

Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/. The resulting search order is the following:

  1. file:./config/
  2. file:./config/*/
  3. file:./
  4. classpath:/config/
  5. classpath:/

8.4 加载优先级总结(按高到底:)

优先级高的会覆盖低的, 也可以理解他们的加载时机是顺序相反的!
  1. 启动时指定某些参数 > java -jar app.jar --server.port=9999
  2. 启动时指定配置文件(有坑:互补失效!)

    java -jar app.jar -spring.config.location=/mydir/application.properties
  3. jar同目录的config/application.properties

    app.jar 同级别目录下的 ./config/application.properties
  4. jar同目录的application.properties

    app.jar同级别目录下的 application.properties
  5. jar内部 classpath目录下的 ./config/application.properties

    app.jar/BOOT-INF/classes/config/application.properties
  6. jar内部classpath目录下的 application.properties

    app.jar/BOOT-INF/classes/application.properties

丰木
322 声望19 粉丝

遇见超乎想象的自己!