Spring Boot 零碎

spring boot 属性加载顺序

  1. 默认属性(通过SpringApplication.setDefaultProperties设置)
  2. @Configuration配置类通过@PropertySource启用的资源文件
  3. 配置文件
  4. RandomValuePropertySource
  5. 操作系统属性
  6. System.getProperties()
  7. JNDI属性(java:comp/env)
  8. ServletContext初始化参数
  9. ServletConfig初始化参数
  10. SPRING_APPLICATION_JSON
  11. 命令行参数
  12. 单元测试时@SpringBootTest配置的属性
  13. 单元测试时@TestPropertySource启用的属性
  14. devtools使用的$HOME/.config/spring-boot目录下的文件

spring boot 配置文件加载顺序

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.external-config.files

spring boot默认会按照以下顺序, 在下面的文件夹中查找配置文件(优先级由低到高):

  1. classpath根目录
  2. classpath下的/config文件夹
  3. 启动目录
  4. 启动目录下的/config文件夹
  5. 启动目录下的/config文件夹的子文件夹
  • 启动目录指的是你在哪个目录下启动的应用, 举例来说: 应用包user-center-1.0.0.jar/home/test/app/

    1. 如果在/home/test/app/下执行java -jar user-center-1.0.0.jar, 那么启动目录就是/home/test/app/

    2. 如果在/home/test/下执行java -jar app/user-center-1.0.0.jar, 那么启动目录就是/home/test/

  • classpath可以简单理解为应用的resource目录

spring可以通过命令行参数spring.config.location自定义配置文件加载顺序, 如: java -Dspring.config.location=classpath:/properties/,file:./properties/ -jar user-center-1.0.0.jar, spring会完全按照这个配置进行查找, 配置文件加载顺序会变成下面这样:

  1. file:./properties/
  2. classpath:properties/

另外, spring还可以通过命令行参数spring.config.additional-location指定额外的配置文件路径, 如: java -Dspring.config.additional-location=classpath:/custom-config/,file:./custom-config/ -jar user-center-1.0.0.jar, 那么配置文件加载顺序就变成了:

  1. file:./custom-config/
  2. classpath:custom-config/
  3. file:./config/
  4. file:./
  5. classpath:/config/
  6. classpath:/

需要注意的是, 如果一个配置项在多个文件都定义了, 则先定义的优先级更高, 所以应用resource下的配置文件优先级最高

profile配置文件加载

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-profile-specific-properties

spring中可以使用application-{profile}.properties配置作为application.properties配置的补充, 如果没有配置要激活的profile, 则spring会加载application-default.properties配置文件.

指定application-{profile}.properties的优先级高于application.properties, 而且如果激活了多个profile, 则后面的profile会覆盖前者, 这条规则比上面文件目录规则优先级更高, 比如java -jar -Dspring.profiles.active=prod user-center-1.0.0jar启动应用. 结合目录加载顺序, 一个配置项的优先级如下:

  1. file:./config/spring-prod.properties
  2. file:./spring-prod.properties
  3. classpath:/config/spring-prod.properties
  4. classpath:spring-prod.properties
  5. file:./config/spring.properties
  6. file:./spring.properties
  7. classpath:/config/spring.properties
  8. classpath:spring.properties

profile优先级最高, 其次先外部再内部, 然后config目录, 最后是默认配置

配置参数加载顺序

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config

spring中通过${xxx}加载变量时, 会从多个位置查找这个配置项, 具体顺序如下(不完整, 只保留运行时需要关心的):

  1. 命令行
  2. ServletConfig初始化参数
  3. ServletContext初始化参数
  4. System.getProperties()
  5. 操作系统环境变量
  6. 外部目录下的application-{profile}.properties文件
  7. classpath下的application-{profile}.properties文件
  8. 外部目录的application.properties
  9. classpath下的application.properties文件
  10. 应用内使用@PropertySource的配置类(@Configuration)

序列化LocalDateTime成数组问题

问题表现

在JDK8之前, 后端如果想将日期格式的JSON数据转成时间戳返回给前端, 只需要在配置文件中增加如下配置

1
2
# 返回时间戳
spring.jackson.serialization.write-dates-as-timestamps=true

使用@responseBody返回json数据时,会自动将时间格式化为毫秒值。

但是在使用了LocalDateTime后, 返回的数据格式成了这样"createTime":[2020,6,9,15,47,29], 上面的配置没有失效了. 对此官方文档的说明如下: jackson-modules-java8

LocalDate, LocalTime, LocalDateTime, and OffsetTime, which cannot portably be converted to timestamps and are instead represented as arrays when WRITE_DATES_AS_TIMESTAMPS is enabled.

解决办法

  1. 自定义JsonSerialize

    1
    2
    3
    4
    5
    6
    7
    public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }
    }
  2. 在实体类时间属性上,加入@JsonSerialize(using = LocalDateTimeConverter.class)注解即可

发序列化问题