在 Spring Boot 应用中设置默认时区(Timezone)

1、概览

有时,我们希望能够指定应用使用的时区。我们可以通过几种不同的方法来实现这一目标。一种方法是在执行应用时使用 JVM 参数。另一种方法是在启动生命周期的不同阶段以编程式在代码中进行更改。

本文将带你了解在 Spring Boot 应用中设置默认时区的几种方法。

2、主要概念

TimeZone 的默认值基于运行 JVM 的机器的操作系统。我们可以:

  • 通过使用 user.timezone 参数传递 JVM 参数,可以根据运行任务或 JAR 的不同情况,以不同的方式传递参数。
  • 在程序中使用 Bean 生命周期配置选项(在创建 Bean 时/创建 Bean 前),甚至在类内执行过程中使用这些选项。

在 Spring Boot 应用中设置默认 TimeZone 会影响不同的组件,如日志的时间戳、调度程序(Scheduler)、JPA/Hibernate 时间戳等。这意味着我们选择在何处执行取决于何时需要它生效。例如,是希望在创建某个 Bean 时生效,还是在初始化 WebApplicationContext 后生效?

精确地确定何时设置该值非常重要,因为这可能会导致不必要的应用行为。例如,警报服务可能会在时区更改生效前设置警报,从而导致警报在错误的时间启动。

在决定采用哪种方案之前,另一个需要考虑的因素是可测试性。使用 JVM 参数是比较简单的选择,但测试起来可能比较麻烦,也更容易出现错误。我们无法保证单元测试能以与生产部署相同的 JVM 参数运行。

3、设置 bootRun 任务的默认时区

如果使用 bootRun 任务运行应用,我们可以在命令行中使用 JVM 参数传递默认 TimeZone

在这种情况下,我们设置的值从一开始执行就可用:

mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Duser.timezone=Europe/Athens"

4、在执行 JAR 时设置默认时区

与运行 bootRun 任务类似,我们可以在执行 JAR 文件时在命令行中传递默认的 TimeZone 值。

同样,我们设置的值在执行之初就可用:

java -Duser.timezone=Europe/Athens -jar spring-core-4-0.0.1-SNAPSHOT.jar

5、在 Spring Boot 启动时设置默认时区

来看看如何在 Spring 启动过程中的不同部分设置时区。

5.1、Main 方法

首先,假设在 main 方法中设置了值。在这种情况下,在执行的早期阶段,甚至在检测到 Spring Profile 之前就可以使用它:

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {

        // 默认时区
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00"));

        SpringApplication.run(MainApplication.class, args);
    }
}

虽然这是生命周期的第一步,但它并没有利用 Spring 配置的灵活性。要么硬编码时区,要么通过编程式从环境变量中读取时区。

5.2、BeanFactoryPostProcessor

其次,BeanFactoryPostProcessor 是一个工厂 Hook,我们可以用它来修改 Application Context 的 Bean 定义。这样,就可以在任何 Bean 实例化之前设置其值:

@Component
public class GlobalTimezoneBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00"));
    }
}

5.3、PostConstruct

最后,我们可以在 WebApplicationContext 初始化完成后使用 MainApplication 类的 PostConstruct 设置默认 TimeZone 值。

此时,我们可以从配置属性中注入 TimeZone 值:

@SpringBootApplication
public class MainApplication {

    @Value("${application.timezone:UTC}")
    private String applicationTimeZone;

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

    @PostConstruct
    public void executeAfterMain() {
        TimeZone.setDefault(TimeZone.getTimeZone(applicationTimeZone));
    }
}

6、总结

本文介绍了在 Spring Boot 应用中设置默认时区的几种方法


Ref:https://www.baeldung.com/spring-boot-set-default-timezone