SpringBoot整合LocalDateTime的過程
一、為什么使用LocalDateTime
LocalDateTime 和 Date 是 Java 中處理日期和時間的兩種不同的類,在 JDK8 中引入了 java.time 包。LocalDateTime 相比 Date 有一些優(yōu)勢,主要體現(xiàn)在以下幾個方面:
- 不可變性:
- LocalDateTime 是不可變的類,一旦創(chuàng)建就不能被修改。任何對 LocalDateTime 的操作都會返回一個新的對象,而不會修改原始對象。這有助于避免在多線程環(huán)境中的并發(fā)問題。
- 線程安全性:
- 由于 LocalDateTime 是不可變的,因此它天然具有線程安全性,可以在多線程環(huán)境中安全使用。
- 可讀性和易用性:
- LocalDateTime 提供了更加清晰和直觀的API,使得處理日期和時間更加易讀、易用。例如,通過使用方法鏈?zhǔn)秸{(diào)用,可以輕松地執(zhí)行各種操作,而不需要復(fù)雜的日期格式化和解析。
- 更好的API設(shè)計:
- LocalDateTime 提供了更豐富、靈活和易用的API,允許進行各種日期和時間的操作,例如增減天數(shù)、小時、分鐘等。而 Date 的 API相對較為古老和不夠直觀。
- 時區(qū)處理:
- LocalDateTime 能夠更好地處理時區(qū)信息,通過 ZonedDateTime 類可以輕松轉(zhuǎn)換到不同的時區(qū)。而 Date 類在處理時區(qū)時較為復(fù)雜,通常需要使用 Calendar 類。
總的來說,LocalDateTime 提供了更現(xiàn)代、清晰和強大的日期和時間處理功能,使得開發(fā)者更容易編寫可讀性高且可維護性強的代碼。在新的代碼中,特別是在使用 JDK 8 及更新版本的項目中,推薦使用 LocalDateTime 替代 Date。
二、使用LocalDateTime遇到的問題
眾所周知,SpringBoot會自動對前端的傳值進行解析,將 HttpServletRequest 中的請求內(nèi)容,轉(zhuǎn)換成后端 Controoler 控制器中的實體類參數(shù),但在實際使用 LocalDateTime 作為參數(shù)時,當(dāng)前端傳入日期格式為 yyyy-MM-dd HH:mm:ss 時,我們會發(fā)現(xiàn) SpringBoot 好像不能正常工作了,下面是兩個問題例子。
GET請求使用 LocalDateTime 作為參數(shù)
下面是使用 @RequestParam 和 @PathVariable 兩種最常見的用法示例
@RestController public class LocalDateTimeController { @GetMapping("/resolveRequestParamDateTime") public void resolveRequestParamDateTime(@RequestParam LocalDateTime dateTime) { System.out.println("ok"); } ? @GetMapping("/resolvePathVariableDateTime/{dateTime}") public void resolvePathVariableDateTime(@PathVariable LocalDateTime dateTime) { System.out.println("ok"); } }
點擊運行,無論是第一個還是第二個方法,都會出現(xiàn)如下異常
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable java.time.LocalDateTime] for value '2023-01-01 12:12:12'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2023-01-01 12:12:12]
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:133)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
?
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable java.time.LocalDateTime] for value '2023-01-01 12:12:12'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2023-01-01 12:12:12]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
?
Caused by: java.lang.IllegalArgumentException: Parse attempt failed for value [2023-01-01 12:12:12]
at org.springframework.format.support.FormattingConversionService$ParserConverter.convert(FormattingConversionService.java:223)
?
Caused by: java.time.format.DateTimeParseException: Text '2023-01-01 12:12:12' could not be parsed at index 2
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
POST請求體中使用 LocalDateTime 作為參數(shù) 下面是使用 @RequestBody 的示例
@RestController public class LocalDateTimeController { ? @PostMapping("/resolveBodyDateTime") public void resolveBodyDateTime(@RequestBody ResolveBody body) { System.out.println("ok"); } ? @Data private static class ResolveBody { private LocalDateTime dateTime; } }
點擊運行,會出現(xiàn)如下異常
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
at [Source: (PushbackInputStream); line: 1, column: 1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:245)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:205)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
?
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
at [Source: (PushbackInputStream); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1442)
簡單分析
觀察堆棧錯誤信息,在使用 GET 請求時拋出的異常的 MethodArgumentTypeMismatchException ,而使用 POST請求時拋出的異常是 HttpMessageNotReadableException 。 二者最外層拋出的異常不一樣,但是我們繼續(xù)往下看最內(nèi)層拋出的異常, GET 請求的報錯很明顯就是 String 類型不能被轉(zhuǎn)換為 LocalDateTime 類型拋出的異常,POST請求拋出的是 jackson 反序列化類型不匹配的異常。 通過分析可得,上述 GET 和 POST 請求的報錯,都是由于 SpringBoot 未能正常將參數(shù)中 String 類型轉(zhuǎn)換為 LocalDateTime 類型拋出的。
三、異常源碼分析
通過堆棧信息可以看出,我們的請求鏈路在進行參數(shù)解析,也就是走到spring mvc 的HandlerMethodArgumentResolverComposite.resolveArgument()這個方法時發(fā)生的異常,從這個方法入手debug打斷點依次往下執(zhí)行,一直走到最內(nèi)層的堆棧位置,也就是實際進行參數(shù)解析的核心代碼,下面列出實際參數(shù)解析代碼。
GET請求
第13行就是實際的轉(zhuǎn)換代碼,走到這里時spring拿到的converter是通過WebMvcAutoConfiguration類自動裝配注入的FormattingConversionService對象 ,但是這個converter默認(rèn)情況下并不能將String轉(zhuǎn)換為LocalDateTime
GenericConversionService.java類源碼: @Nullable public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); if (sourceType == null) { Assert.isTrue(source == null, "Source must be [null] if source type == [null]"); return this.handleResult((TypeDescriptor)null, targetType, this.convertNullSource((TypeDescriptor)null, targetType)); } else if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException("Source to convert from must be an instance of [" + sourceType + "]; instead it was a [" + source.getClass().getName() + "]"); } else { GenericConverter converter = this.getConverter(sourceType, targetType); if (converter != null) { # 這里去做類型轉(zhuǎn)換 Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return this.handleResult(sourceType, targetType, result); } else { return this.handleConverterNotFound(source, sourceType, targetType); } } }
POST請求 在第11~12行就是實際的轉(zhuǎn)換代碼,走到這里時spring拿到的converter是 MappingJackson2HttpMessageConverter ,但是這個converter并不能將String轉(zhuǎn)換為LocalDateTime
AbstractMessageConverterMethodArgumentResolver類源碼: for (HttpMessageConverter<?> converter : this.messageConverters) { Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass(); GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass != null && converter.canRead(targetClass, contentType))) { if (message.hasBody()) { HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); # 這里去做類型轉(zhuǎn)換 body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse)); body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType); } else { body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType); } break; } }
MappingJackson2HttpMessageConverter的read解析方法繼續(xù)往后走,調(diào)用的是父類AbstractJackson2HttpMessageConverter的read方法,實際的解析方法就是第18行的ObjectMapper類的readValue方法
AbstractJackson2HttpMessageConverter類源碼: @Override public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { ? JavaType javaType = getJavaType(type, contextClass); return readJavaType(javaType, inputMessage); } ? private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException { try { if (inputMessage instanceof MappingJacksonInputMessage) { Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView(); if (deserializationView != null) { return this.objectMapper.readerWithView(deserializationView).forType(javaType). readValue(inputMessage.getBody()); } } # jackson實際解析 return this.objectMapper.readValue(inputMessage.getBody(), javaType); } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage); } }
問題分析
SpringBoot GET請求參數(shù)類型轉(zhuǎn)換使用的是GenericConversionService類里注冊的一個個GenericConverter;String轉(zhuǎn)LocalDateTime類型默認(rèn)情況下的StringToLocalDateTimeConverter不能正常解析。
- SpringBoot POST請求參數(shù)類型轉(zhuǎn)換使用的是AbstractMessageConverterMethodArgumentResolver類里L(fēng)ist<HttpMessageConverter<?>> messageConverters里注冊的一個個HttpMessageConverter;
- String轉(zhuǎn)LocalDateTime類型默認(rèn)情況下的MappingJackson2HttpMessageConverter不能正常解析,也就是默認(rèn)的ObjectMapper不能正常解析。
四、解決思路
通過上述問題分析,下面有兩種解決思路(推薦第二種)
通過配置使得默認(rèn)的StringToLocalDateTimeConverter支持GET請求String轉(zhuǎn)LocalDateTime類型,默認(rèn)的MappingJackson2HttpMessageConverter支持POST請求String轉(zhuǎn)LocalDateTime類型
spring注入GenericConverter類型的Bean支持GET請求String轉(zhuǎn)LocalDateTime類型,注入ObjectMapper類型的Bean支持POST請求String轉(zhuǎn)LocalDateTime類型
4.1、GET請求解決思路
GET請求解決思路:通過閱讀源碼,可以發(fā)現(xiàn)以下三種springboot為用戶預(yù)留的實現(xiàn)方式:
application.yml配置
在2.3.x以上版本,springmvc增加了日期時間格式配置,并且可以將格式注冊到對應(yīng)的日期解析器中
WebMvcAutoConfiguration#EnableWebMvcConfiguration類源碼 // springboot自動裝配WebConversionService對象 @Bean public FormattingConversionService mvcConversionService() { WebMvcProperties.Format format = this.mvcProperties.getFormat(); WebConversionService conversionService = new WebConversionService((new DateTimeFormatters()).dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime())); // 這里允許用戶自定往容器中添加Converter this.addFormatters(conversionService); return conversionService; } WebConversionService類源碼: private void addFormatters(DateTimeFormatters dateTimeFormatters) { // 這是默認(rèn)情況下springboot自動注入的日期格式解析器 this.registerJsr310(dateTimeFormatters); this.registerJavaDate(dateTimeFormatters); }
使用@DateTimeFormat注解
DateTimeFormatterRegistrar類源碼: public void registerFormatters(FormatterRegistry registry) { // 這里可以拿到application.yml配置文件指定的格式 DateTimeFormatter df = this.getFormatter(DateTimeFormatterRegistrar.Type.DATE); DateTimeFormatter tf = this.getFormatter(DateTimeFormatterRegistrar.Type.TIME); DateTimeFormatter dtf = this.getFormatter(DateTimeFormatterRegistrar.Type.DATE_TIME); registry.addFormatterForFieldType(LocalDate.class, new TemporalAccessorPrinter(df == DateTimeFormatter.ISO_DATE ? DateTimeFormatter.ISO_LOCAL_DATE : df), new TemporalAccessorParser(LocalDate.class, df)); registry.addFormatterForFieldType(LocalTime.class, new TemporalAccessorPrinter(tf == DateTimeFormatter.ISO_TIME ? DateTimeFormatter.ISO_LOCAL_TIME : tf), new TemporalAccessorParser(LocalTime.class, tf)); registry.addFormatterForFieldType(LocalDateTime.class, new TemporalAccessorPrinter(dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf), new TemporalAccessorParser(LocalDateTime.class, dtf)); // 使用@DateTimeFormat注解來幫忙完成LocalDateTime類型的解析 registry.addFormatterForFieldAnnotation(new Jsr310DateTimeFormatAnnotationFormatterFactory()); }
向容器中添加自定義Converter
我們在WebConversionService對象自動裝配方法中看到了這行代碼:this.addFormatters(conversionService);
這其實就是springboot為用戶預(yù)留的拓展方法,它支持用戶向容器中添加自定義Converter
// WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#addFormatters()方法 public void addFormatters(FormatterRegistry registry) { ApplicationConversionService.addBeans(registry, this.beanFactory); } // ApplicationConversionService#addBeans()方法 public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) { Set<Object> beans = new LinkedHashSet(); beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values()); beans.addAll(beanFactory.getBeansOfType(Converter.class).values()); beans.addAll(beanFactory.getBeansOfType(Printer.class).values()); beans.addAll(beanFactory.getBeansOfType(Parser.class).values()); Iterator var3 = beans.iterator(); // spirngboot會掃描ioc容器中以下所有類型的bean,并添加到WebConversionService中 while(var3.hasNext()) { Object bean = var3.next(); if (bean instanceof GenericConverter) { registry.addConverter((GenericConverter)bean); } else if (bean instanceof Converter) { registry.addConverter((Converter)bean); } else if (bean instanceof Formatter) { registry.addFormatter((Formatter)bean); } else if (bean instanceof Printer) { registry.addPrinter((Printer)bean); } else if (bean instanceof Parser) { registry.addParser((Parser)bean); } } }
4.2、POST請求解決思路
通過異常源碼分析我們可以得知,由于jackson也就是ObjectMapper對象在默認(rèn)情況下并不能完成LocalDateTime類型的解析,所有需要對jackson進行配置;通過閱讀源碼,以下有兩種解決方式:
配置類注入Jackson2ObjectMapperBuilderCustomizer類型的Bean對jackson進行配置(推薦)
JacksonAutoConfiguration#JacksonObjectMapperBuilderConfiguration類源碼: @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Jackson2ObjectMapperBuilder.class) static class JacksonObjectMapperBuilderConfiguration { @Bean @Scope("prototype") @ConditionalOnMissingBean Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(ApplicationContext applicationContext, List<Jackson2ObjectMapperBuilderCustomizer> customizers) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.applicationContext(applicationContext); // ioc容器中獲取所有Jackson2ObjectMapperBuilderCustomizer類型的bean, 調(diào)用customize方法配置Jackson2ObjectMapperBuilder customize(builder, customizers); return builder; } private void customize(Jackson2ObjectMapperBuilder builder, List<Jackson2ObjectMapperBuilderCustomizer> customizers) { for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) { customizer.customize(builder); } } }
直接注入ObjectMapper類型的Bean進行覆蓋
JacksonAutoConfiguration#JacksonObjectMapperConfiguration類源碼 @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Jackson2ObjectMapperBuilder.class) static class JacksonObjectMapperConfiguration { @Bean @Primary @ConditionalOnMissingBean ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { // 通過Jackson2ObjectMapperBuilder構(gòu)建出ObjectMapper return builder.createXmlMapper(false).build(); } }
五、解決方案落地
5.1、GET請求解決方案
application.yml配置 SpringBoot2.3.x以及更高的版本,springmvc增加了日期時間格式配置,既可以解決LocalDateTime類型參數(shù)解析,也可以解決Date類型參數(shù)解析
spring: mvc: date: yyyy-MM-dd time: HH:mm:ss date-time: yyyy-MM-dd HH:mm:ss
注解配置 SpringBoot針對LocalDateTime類型解析增加了@DateTimeFormatter注解,可以在請求參數(shù)中加上這個注解完成解析
@RestController public class LocalDateTimeController { @GetMapping("/resolveRequestParamDateTime") public void resolveRequestParamDateTime(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime) { System.out.println("ok"); } @GetMapping("/resolvePathVariableDateTime/{dateTime}") public void resolvePathVariableDateTime(@PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime) { System.out.println("ok"); } }
Java Config注入Bean
在Spring IOC容器中注入Converter,SpringBoot會自動將IOC容器中的Converter放到GenericConversionService中
@Configuration public class LocalDateTimeConfig { @Bean public Converter<String, LocalDateTime> stringToLocalDateTimeConverter() { return new Converter<String, LocalDateTime>() { @Override public LocalDateTime convert(String source) { return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } }; } @Bean public Converter<String, LocalDate> stringToLocalDateConverter() { return new Converter<String, LocalDate>() { @Override public LocalDate convert(String source) { return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd")); } }; } @Bean public Converter<String, LocalTime> stringToLocalTimeConverter() { return new Converter<String, LocalTime>() { @Override public LocalTime convert(String source) { return LocalTime.parse(source, DateTimeFormatter.ofPattern("HH:mm:ss")); } }; } }
5.2、POST請求解決方案
注解配置 在實體類的字段上使用@JsonFormat注解配置格式,使用 @JsonSerialize注解配置序列化器,使用 @JsonDeserialize注解配置反序列化器
@RestController public class LocalDateTimeController { @PostMapping("/resolveBodyDateTime") public void resolveBodyDateTime(@RequestBody ResolveBody body) { System.out.println("ok"); } @Data private static class ResolveBody { @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime dateTime; } }
Java Config注入Bean
在Spring IOC容器中注入Jackson2ObjectMapperBuilderCustomizer類型的Bean可以對Jackson進行自定義配置;也可以直接注入一個ObjectMapper進行替換
@Configuration public class LocalDateTimeConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return new Jackson2ObjectMapperBuilderCustomizer() { @Override public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { final JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); jacksonObjectMapperBuilder .modules(javaTimeModule) .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .simpleDateFormat("yyyy-MM-dd HH:mm:ss") .timeZone(TimeZone.getTimeZone("GMT+8")) .locale(Locale.CHINA); } }; } }
到此這篇關(guān)于SpringBoot整合LocalDateTime的文章就介紹到這了,更多相關(guān)SpringBoot整合LocalDateTime內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在idea中使用JaCoCo插件統(tǒng)計單元測試覆蓋率的實現(xiàn)
這篇文章主要介紹了在idea中使用JaCoCo插件統(tǒng)計單元測試覆蓋率的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01java Spring整合Freemarker的詳細(xì)步驟
本文對Spring整合Freemarker步驟做了詳細(xì)的說明,按步驟操作一定可以整合通過,這里提供給大家做參考2013-11-11SpringBoot 監(jiān)聽Redis鍵過期事件(過期監(jiān)聽)
Redis鍵過期事件是SpringBoot中常用的緩存失效通知方式,通過配置可以監(jiān)聽特定鍵的過期事件,具有一定的參考價值,感興趣的可以了解一下2024-12-12SpringBoot自定義定時任務(wù)的實現(xiàn)示例
本文主要介紹了SpringBoot自定義定時任務(wù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05