Spring Boot 2将持续时间转换为Java 8应用程序属性。

36

我需要在application.properties中定义Duration(spring.redis.timeout)

我尝试使用Spring Boot文档中定义的方法:

Spring Boot专门支持表示时间段。 如果您暴露一个java.time.Duration属性,则可以在应用程序属性中使用以下格式:

常规长表示形式(使用毫秒作为默认单位,除非指定了@DurationUnit) java.util.Duration使用的标准ISO-8601格式 更易读的格式,其中值和单位耦合在一起(例如,10s表示10秒)

当我使用spring.redis.timeout=3s时,Spring Boot应用程序会抛出此异常:

Cannot convert value of type 'java.lang.String' to required type 'java.time.Duration': no matching editors or conversion strategy found

使用Spring Boot 2最新版本,哪种方式是设置Duration属性的正确值的最佳方法?


https://dev59.com/K6fja4cB1Zd3GeqPvGfa - pvpkiran
所以,根据Spring Boot的Github页面,无法使用Duration和@Value等功能。他们原本期望在Spring Boot 2.1.0中实现此功能:https://github.com/spring-projects/spring-boot/issues/13237 - Brother
我已经发布了解决方案,以便能够使用@value。 - CRISTIAN ROMERO MATESANZ
请阅读Spring Boot 2.1.0-M3发布说明的内容:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1.0-M3-Release-Notes。 - Wilder Valera
请参考我的解决方案 https://dev59.com/wmMLtIcB2Jgan1znFiuP#67726164 - chandra kumar
7个回答

48

任何类型为duration的属性都可以通过.properties.yml文件注入。您需要做的就是使用适当的格式。

如果您想注入5秒的持续时间,应将其定义为PT5Spt5sPT5s

  • 字母的大小写不重要,您可以使用任何易于阅读的组合
  • 一般来说,所有人都会使用大写字母

其他示例

PT1.5S       = 1.5 Seconds
PT60S        = 60 Seconds
PT3M         = 3 Minutes
PT2H         = 2 Hours
P3DT5H40M30S = 3Days, 5Hours, 40 Minutes and 30 Seconds

您也可以使用正负符号来表示时间段的正负。

  • 例如,您只能否定一个实体: PT-3H30M = -3小时,+30分钟,基本上是-2.5小时
  • 或者您可以否定整个实体:-PT3H30M = -3小时,-30分钟,基本上是-3.5小时
  • 双重否定也适用于此处:-PT-3H+30M = +3小时,-30分钟,基本上是+2.5小时

注意:

  • 只能使用HOURS或更低的ChronoUnit(NANOSMICROSMILLISSECONDSMINUTESHOURS)表示持续时间,因为它们表示准确的持续时间。
  • 不允许使用更高的ChronoUnit(DAYSWEEKSMONTHSYEARSDECADESCENTURIESMILLENNIAERASFOREVER),因为它们不代表准确的持续时间。由于夏令时天数可能发生变化,月份长度也不同等原因,这些ChronoUnit具有估计值。
  • 例外 - Java将DAYS自动转换为HOURS,但不会对任何其他更高的ChronoUnit(MONTHSYEARS等)进行转换。

如果我们尝试使用“P1D”,Java会自动将其转换为“PT24H”。所以如果我们想要一个月的持续时间,我们将不得不使用PT720HP30D。在P30D的情况下,Java的自动转换将会发生并提供给我们PT720H


如果您觉得这个解释有用的话,请点赞!谢谢!


31
Spring Boot还支持“简单”的时间长度格式,例如:"60s"表示60秒,"5m"表示5分钟,"2h"表示2小时。 - M. Justin
请确保您没有任何尾随空格。 - Vinagy
1
这段代码在使用Spring Boot 2.3.2.RELEASE和Java 11时无法正常工作。会抛出IllegalStateException异常,提示“无法将类型为'java.lang.String'的值转换为所需的类型'java.time.Duration':找不到匹配的编辑器或转换策略”。使用方法如下:@Value("${foo.start.window:PT2H}") private Duration fooStartWindow; - JohnC
@JohnC - 我认为你使用方法不对。你能在这里粘贴代码片段吗? 从技术上讲,代码应该是这样的 @Value("${foo.start.window:startWinPeriod}") private Duration fooStartWindow; 在你的应用程序属性中,你会有类似这样的东西 foo.start.window.startWinPeriod=PT2H或者像下面的yml文件 foo: start: window startWinPeriod:PT2H - Rituraj
在某些情况下,大小写是有影响的。ISO-8601要求使用大写字母 https://en.wikipedia.org/wiki/ISO_8601#Durations,因此每个人都应该使用它。Java的Duration实现对此比较宽松,但其他一些实现不是,包括Spring Boot 2.4.3的DurationStyle.ISO8601,它要求使用大写字母P。 - malamut
显示剩余3条评论

22

Spring Boot 2.5.5 更新


我们可以使用@Value注解和application.properties中的值。例如,假设您在application.properties文件中有以下属性:

your.amazing.duration=100ms

然后您可以在@Value注释中使用它:

@Value("${your.amazing.duration}") 
final Duration duration;

仅支持以下时间单位:

  • ns 表示纳秒
  • us 表示微秒
  • ms 表示毫秒
  • s 表示秒
  • m 表示分钟
  • h 表示小时
  • d 表示天

文档: 链接


17
可以使用Spring表达式语言(SpEL)来使用@Value注释。
@Value("#{T(java.time.Duration).parse('${spring.redis.timeout}')}")
private Duration timeout;

4
Spring Boot现在直接支持Duration(无需使用SEL)。已在Spring Boot 2.1.9.RELEASE上进行了测试。 - Jardo

9

在当前版本的Spring-Boot 2.0.4.RELEASE中,无法同时使用@Value注释和Duration,但可以与@ConfigurationProperties一起使用。

对于Redis,您可以使用RedisProperties进行配置:

spring.redis.timeout=5s

并且:

@SpringBootApplication
public class DemoApplication {

  @Autowired
  RedisProperties redisProperties;

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

  @PostConstruct
  void init() {
    System.out.println(redisProperties.getTimeout());
  }
}

它打印出了(解释为5秒):
PT5S

https://docs.oracle.com/javase/8/docs/api//java/time/Duration.html#parse-java.lang.CharSequence-


4

如果您的Spring-Boot版本或其依赖项未将ApplicationConversionService放入上下文中(直到2.1版之前Spring-Boot不会这样做),则可以显式地将其暴露出来。

@Bean
public ConversionService conversionService() {
    return ApplicationConversionService.getSharedInstance();
}

它调用了Duration.parse,所以您可以在属性文件中使用PT3SPT1H30M等。

3
Spring Boot试图在绑定@ConfigurationProperties bean时将外部应用程序属性强制转换为正确的类型。如果需要自定义类型转换,您可以提供一个ConversionService bean(使用名为conversionService的bean)。
请参见:https://docs.spring.io/spring-boot/docs/2.0.4.RELEASE/reference/htmlsingle/#boot-features-external-config-conversion
创建新的ApplicationConversionService bean(它必须命名为conversionService)。这是我使用Spring Boot 2.0.4测试过的代码:
@Configuration
public class Conversion {

@Bean
public ApplicationConversionService conversionService()
{
    final ApplicationConversionService applicationConversionService = new ApplicationConversionService();
    return applicationConversionService;
}

这里有一个使用这种方法的示例项目:

https://github.com/cristianprofile/spring-data-redis-lettuce


2
在2.1.x版本中,这将不再需要。请参见https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1.0-M3-Release-Notes。 - Wilder Valera
我将尝试使用Context ApplicationConversionService Support(2.1)现在,默认情况下,ApplicationConversionService已经在SpringApplication创建的Environment和BeanFactory中注册。这使您可以直接使用应用程序转换器与核心Spring Framework项目(如@Value注释)一起使用:@Value("${my.duration:10s}") private Duration duration; - CRISTIAN ROMERO MATESANZ

1
我在测试过程中遇到了这个错误,但是使用带有@Value注释的Duration的bean在其他情况下可以正常工作。后来发现测试用例类缺少@SpringBottest注释(以及提供它的spring-boot-test依赖项),这只导致可用于使用的标准转换器的子集。

在我的情况下,Duration 是一个后备选项,原始值是 Long,有些转换器可能会丢失,添加 PT 前缀解决了这个问题:@Scheduled(fixedDelayString = "${app.batchFreq:PT10m}") - gavenkoa
我在测试期间也遇到了这个错误,目前我找到的解决方案是在测试上下文中加载一个 conversionService bean,像这样:@Bean public ConversionService conversionService() { return ApplicationConversionService.getSharedInstance(); } - vortex.alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接