SpringBoot整合LocalDateTime的过程

 更新时间:2024年08月21日 09:57:33   作者:独辟蹊径的鱼  
LocalDateTime 和 Date 是 Java 中处理日期和时间的两种不同的类,在 JDK8 中引入了 java.time 包,这篇文章主要介绍了SpringBoot整合LocalDateTime的过程,需要的朋友可以参考下

一、为什么使用LocalDateTime

        LocalDateTime 和 Date 是 Java 中处理日期和时间的两种不同的类,在 JDK8 中引入了 java.time 包。LocalDateTime 相比 Date 有一些优势,主要体现在以下几个方面:

  • 不可变性:
    • LocalDateTime 是不可变的类,一旦创建就不能被修改。任何对 LocalDateTime 的操作都会返回一个新的对象,而不会修改原始对象。这有助于避免在多线程环境中的并发问题。
  • 线程安全性:
    • 由于 LocalDateTime 是不可变的,因此它天然具有线程安全性,可以在多线程环境中安全使用。
  • 可读性和易用性:
    • LocalDateTime 提供了更加清晰和直观的API,使得处理日期和时间更加易读、易用。例如,通过使用方法链式调用,可以轻松地执行各种操作,而不需要复杂的日期格式化和解析。
  • 更好的API设计:
    • LocalDateTime 提供了更丰富、灵活和易用的API,允许进行各种日期和时间的操作,例如增减天数、小时、分钟等。而 Date 的 API相对较为古老和不够直观。
  • 时区处理:
    • LocalDateTime 能够更好地处理时区信息,通过 ZonedDateTime 类可以轻松转换到不同的时区。而 Date 类在处理时区时较为复杂,通常需要使用 Calendar 类。

总的来说,LocalDateTime 提供了更现代、清晰和强大的日期和时间处理功能,使得开发者更容易编写可读性高且可维护性强的代码。在新的代码中,特别是在使用 JDK 8 及更新版本的项目中,推荐使用 LocalDateTime 替代 Date。

二、使用LocalDateTime遇到的问题

        众所周知,SpringBoot会自动对前端的传值进行解析,将 HttpServletRequest 中的请求内容,转换成后端 Controoler 控制器中的实体类参数,但在实际使用 LocalDateTime 作为参数时,当前端传入日期格式为 yyyy-MM-dd HH:mm:ss 时,我们会发现 SpringBoot 好像不能正常工作了,下面是两个问题例子。

GET请求使用 LocalDateTime 作为参数

下面是使用 @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");
    }
}

点击运行,无论是第一个还是第二个方法,都会出现如下异常

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 作为参数 下面是使用 @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;
    }
}

点击运行,会出现如下异常

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 。 二者最外层抛出的异常不一样,但是我们继续往下看最内层抛出的异常, GET 请求的报错很明显就是 String 类型不能被转换为 LocalDateTime 类型抛出的异常,POST请求抛出的是 jackson 反序列化类型不匹配的异常。 通过分析可得,上述 GET 和 POST 请求的报错,都是由于 SpringBoot 未能正常将参数中 String 类型转换为 LocalDateTime 类型抛出的。

三、异常源码分析

        通过堆栈信息可以看出,我们的请求链路在进行参数解析,也就是走到spring mvc 的HandlerMethodArgumentResolverComposite.resolveArgument()这个方法时发生的异常,从这个方法入手debug打断点依次往下执行,一直走到最内层的堆栈位置,也就是实际进行参数解析的核心代码,下面列出实际参数解析代码。

GET请求

        第13行就是实际的转换代码,走到这里时spring拿到的converter是通过WebMvcAutoConfiguration类自动装配注入的FormattingConversionService对象 ,但是这个converter默认情况下并不能将String转换为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) {
            # 这里去做类型转换
            Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
            return this.handleResult(sourceType, targetType, result);
        } else {
            return this.handleConverterNotFound(source, sourceType, targetType);
        }
    }
}

POST请求 在第11~12行就是实际的转换代码,走到这里时spring拿到的converter是 MappingJackson2HttpMessageConverter ,但是这个converter并不能将String转换为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);
            # 这里去做类型转换
            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解析方法继续往后走,调用的是父类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请求参数类型转换使用的是GenericConversionService类里注册的一个个GenericConverter;String转LocalDateTime类型默认情况下的StringToLocalDateTimeConverter不能正常解析。                

  •         SpringBoot POST请求参数类型转换使用的是AbstractMessageConverterMethodArgumentResolver类里List<HttpMessageConverter<?>> messageConverters里注册的一个个HttpMessageConverter;
  • String转LocalDateTime类型默认情况下的MappingJackson2HttpMessageConverter不能正常解析,也就是默认的ObjectMapper不能正常解析。

四、解决思路

        通过上述问题分析,下面有两种解决思路(推荐第二种)

通过配置使得默认的StringToLocalDateTimeConverter支持GET请求String转LocalDateTime类型,默认的MappingJackson2HttpMessageConverter支持POST请求String转LocalDateTime类型

spring注入GenericConverter类型的Bean支持GET请求String转LocalDateTime类型,注入ObjectMapper类型的Bean支持POST请求String转LocalDateTime类型

4.1、GET请求解决思路

         GET请求解决思路:通过阅读源码,可以发现以下三种springboot为用户预留的实现方式:  

application.yml配置

        在2.3.x以上版本,springmvc增加了日期时间格式配置,并且可以将格式注册到对应的日期解析器中

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) {
    // 这是默认情况下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为用户预留的拓展方法,它支持用户向容器中添加自定义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对象在默认情况下并不能完成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, 调用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构建出ObjectMapper
        return builder.createXmlMapper(false).build();
    }
}

五、解决方案落地

5.1、GET请求解决方案

application.yml配置 SpringBoot2.3.x以及更高的版本,springmvc增加了日期时间格式配置,既可以解决LocalDateTime类型参数解析,也可以解决Date类型参数解析

spring:
  mvc:
    date: yyyy-MM-dd
    time: HH:mm:ss
    date-time: yyyy-MM-dd HH:mm:ss

注解配置 SpringBoot针对LocalDateTime类型解析增加了@DateTimeFormatter注解,可以在请求参数中加上这个注解完成解析

@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);
            }
        };
    }
}

到此这篇关于SpringBoot整合LocalDateTime的文章就介绍到这了,更多相关SpringBoot整合LocalDateTime内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析Java中关键词volatile底层的实现原理

    浅析Java中关键词volatile底层的实现原理

    在 Java 并发编程中,有 3 个最常用的关键字:synchronized、ReentrantLock 和 volatile,这篇文章主要来和大家聊聊volatile底层的实现原理,感兴趣的可以了解下
    2024-02-02
  • Java调用ChatGPT的实现代码

    Java调用ChatGPT的实现代码

    这篇文章主要介绍了Java调用ChatGPT的实现代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • java 中http请求为了防止乱码解决方案

    java 中http请求为了防止乱码解决方案

    这篇文章主要介绍了java 中http请求为了防止乱码解决方案的相关资料,需要的朋友可以参考下
    2017-02-02
  • Spring中@Scheduled和HttpClient的连环坑

    Spring中@Scheduled和HttpClient的连环坑

    这篇文章主要给大家介绍了关于Spring中@Scheduled和HttpClient的连环坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-03-03
  • Java深入数据结构理解掌握抽象类与接口

    Java深入数据结构理解掌握抽象类与接口

    在类中没有包含足够的信息来描绘一个具体的对象,这样的类称为抽象类,接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成,本文给大家介绍Java抽象类和接口,感兴趣的朋友一起看看吧
    2022-05-05
  • SpringBoot拦截器实现登录拦截的方法示例

    SpringBoot拦截器实现登录拦截的方法示例

    这篇文章主要介绍了SpringBoot拦截器实现登录拦截的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • Java @SpringBootApplication注解深入解析

    Java @SpringBootApplication注解深入解析

    这篇文章主要给大家介绍了关于Java @SpringBootApplication注解的相关资料,@SpringBootApplication这个注解是Spring Boot项目的基石,创建SpringBoot项目之后会默认在主类加上,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-02-02
  • RocketMQ broker文件清理源码解析

    RocketMQ broker文件清理源码解析

    这篇文章主要为大家介绍了RocketMQ broker文件清理源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Java中Stream流去除List重复元素的方法

    Java中Stream流去除List重复元素的方法

    这篇文章主要为大家详细介绍了Java中Stream流去除List重复元素的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Java设计模式中的代理设计模式详细解析

    Java设计模式中的代理设计模式详细解析

    这篇文章主要介绍了Java设计模式中的代理设计模式详细解析,代理模式,重要的在于代理二字,何为代理,我们可以联想到生活中的例子,比如秘书、中介这类职业,我们可以委托中介去帮我们完成某些事情,而我们自己只需要关注我们必须完成的事情,需要的朋友可以参考下
    2023-12-12

最新评论