网站首页 > 基础教程 正文
背景
- 前端和后端在时间格式的传递上都走的是时间戳(方便前端自由定制)
- 时间格式大多用的是LocalDate,并不是传统的Date
简单来说就是时间戳在各类形式下转日期类。
当有如下类似代码的时候,后端该做哪些配置来满足上述需求了?
@PostMapping("/date-test/post/{pathDate}")
public LocalDateTime getDatePost(@RequestParam LocalDate date,
@RequestParam LocalDateTime dateTime,
@RequestParam Date originalDate,
@PathVariable("pathDate") LocalDateTime datePath,
LocalDate localDate,
@RequestBody Person person,
Person person2) {
System.out.println(date);
System.out.println(dateTime);
System.out.println(originalDate);
System.out.println(localDate);
System.out.println(datePath);
System.out.println(person);
System.out.println(person2);
return LocalDateTime.now();
}
}
上述案例使用了post来接收参数,如果你是get请求,除了@RequestBody不能使用外,其他不受影响
请求的URL如下所示:
localhost:8099/date-test/post/1568796069368?date=1568796069368&localDate=1568796069368&dateTime=1568796069368&originalDate=1568796069368&name=codingOX&birth=1568796069368
其中json部分的配置如下
正文
SpringMVC处理参数,其核心接口是org.springframework.web.method.support.HandlerMethodArgumentResolver
相关的实现挺多的,如下所示,重点部分我都通过红色框框进行了标记。
我们通过接口来分析下SpringMVC是如何处理参数的
public interface HandlerMethodArgumentResolver {
/**
* 判定当前处理器适合处理那种类型的数据
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 对符合预期的数据,进行具体的逻辑处理
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
更详细的代码你可以查看上图给出的实现,整体而言就是根据根据supportsParameter的结果Boolean来判定当前类是否可以解析这个参数,如果可以继续调用resolveArgument对参数进行解析。
后面我们需要利用该特点来自定义处理类,下面将通过如下几种情况进行处理。
- 通过@RequestParam和@PathVariable修饰LocalDate
- 通过@RequestBody修饰LocalDate
- 不对LocaDate添加任何修饰
- 不对包含LocalDate的对象添加任何修饰
@RequestParam和@PathVariable
这种情况对应的是上面例子形参中的:
public LocalDateTime getDatePost(@RequestParam LocalDate date,
....
@PathVariable LocalDateTime datePath,
....) {...}
Spring的源码部分,通过文字分析和解读起来太累,这里直接给出结论,下同,有兴趣的可以结合结论,反过来阅读。。。
这2个类对应的处理器分别是:
- org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
- org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
如果有兴趣,可以跟下源码,你可以发现,处理这2类注解的解析主要用到的是:WebDataBinder,如果要扩展这类参数的处理方式,可以使用org.springframework.core.convert.converter.Converter或者org.springframework.format.Formatter
这2者区别在于:
- Converter可以定义输入和输出
- Formatter默认输入是字符串,输出为自定义
你可以根据自身需求进行定制,这里我通过Convert转换LocalDate为例,进行转换:
/**
* 接收 形参加有注解 @RequestParam和@PathVariable时候 字符串转Date相关类
*
* @author Liu Chunfu
* @date 2019-09-18 3:57 下午
**/
public class CustomDateConverter {
public static class LocalDateConvert implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String timestamp) {
return LocalDateUtil.timestampToLocalDateTime(timestamp).toLocalDate();
}
}
...
}
定义后,还需要使用,这个使用也很简单,将其注入Spring的容器就可以了。
- 如果是Spring MVC,通过XML定义为类
- 如果是Spring Boot,在@Configuration下通过@Bean声明就可以了。
还是举个例子吧:
@Configuration
public class CustomDateConfig{
@Bean
public Converter<String, LocalDate> localDateConverter() {
//此处不能替换为lambda表达式
return new CustomDateConverter.LocalDateConvert();
}
}
通过@RequestBody修饰
通过@RequestBody很明显的就是要通过JSON序列化进行处理,Spring默认的是Jackson进行处理,所以我们需要对默认的JSON处理器进行日期类型的添加(假如默认不带对应序列化器的情况下)
此时需要用的是JsonDeserializer这个类,对应的实现如下所示:
public class CustomDateDeserializer {
/**
* 反序列化LocalDateTime
*/
public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Long timestamp = Long.valueOf(p.getText());
Instant instant = Instant.ofEpochMilli(timestamp);
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
LocalDate localDate = localDateTime.toLocalDate();
return localDate;
}
}
....
}
有了这个了类,还需要添加到Spring中,Spring利用的是ObjectMapper,我们自定义一个ObjectMapper替换原本的就可以了。
@Configuration
public class CustomDateConfig {
// 省略部分代码
/**
* Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
//不显示为null的字段
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 忽略不能转移的字符
objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
// 过滤对象的null属性.
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//忽略transient
objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
//LocalDateTime系列序列化和反序列化模块,继承自jsr310,我们在这里修改了日期格式
JavaTimeModule javaTimeModule = new JavaTimeModule();
// LocalDateTime
javaTimeModule.addSerializer(LocalDateTime.class, new CustomDateSerializer.LocalDateTimeSerializer());
javaTimeModule.addDeserializer(LocalDateTime.class,new CustomDateDeserializer.LocalDateTimeDeserializer());
// LocalDate
javaTimeModule.addSerializer(LocalDate.class, new CustomDateSerializer.LocalDateSerializer());
javaTimeModule.addDeserializer(LocalDate.class, new CustomDateDeserializer.LocalDateDeserializer());
//Date序列化和反序列化
javaTimeModule.addSerializer(Date.class,new CustomDateSerializer.DateSerializer());
javaTimeModule.addDeserializer(Date.class,new CustomDateDeserializer.DateDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
// 省略部分代码
}
外部参数什么注解也不加
这种情况对应的是形参中的:
public LocalDateTime getDatePost(...,LocalDate localDate,....) { .... }
这种情况下,通过Spring默认的ModelAttributeMethodProcessor进行处理,其处理逻辑为:通过反射创建一个对象,然会在反射对每个参数通过Convert进行转换赋值。
默认情况下,此种情况是会报错的,因为LocalDate没有构造函数。所以我们需要在默认调用之前,自定义处理这此类形参,此时利用的就是HandlerMethodArgumentResolver这个接口。
HandlerMethodArgumentResolver会在ModelAttributeMethodProcessor之前调用,该接口的声明非常简单。
todo
我们可以对此类参数进行自定义。
public class CustomDateArgumentResolverHandler {
public static class LocalDateArgumentResolverHandler implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(LocalDate.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String timestamp = webRequest.getParameter(parameter.getParameterName());
LocalDateTime localDateTime = LocalDateUtil.timestampToLocalDateTime(timestamp);
return localDateTime.toLocalDate();
}
}
......
}
在使用该类的时候,需要在配置类@Configuration中通过WebMvcConfigurer这个接口下的方法addArgumentResolvers来添加进去:
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
大体的代码如下:
@Configuration
public class CustomDateConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CustomDateArgumentResolverHandler.LocalDateArgumentResolverHandler());
// .....
}
}
内部参数什么注解也不加
对应的情况是:
public LocalDateTime getDatePost(Person person2)
此时的处理情况符合上述提到的ModelAttributeMethodProcessor和Convert处理。
所以如果我们按照上面提到的方法进行了处理,那么不用担心时间类型无法解析的问题。
结语
关于时间LocalDate这个类型的处理都完成了,上面的代码还是比较凌乱的,改天我单独做一个Demo,放在GitHub上,不过我还是希望你手动自己试试!
如果本文对你有帮助,欢迎评论、点赞、转发、收藏、关注!
猜你喜欢
- 2024-10-23 Java Date 转 LocalDate javadate转localdate
- 2024-10-23 java date对象的正确使用方法 java中date
- 2024-10-23 「案例学习」Java8 日期时间 「案例学习」Java8 日期时间范围
- 2024-10-23 SpringBoot反序列化LocalDateTime总失败
- 2024-10-23 Java 8 LocalDate类入门实战:初学者必读的用法详解与概念梳理
- 2024-10-23 Java 计算日期属于当月第几周(日期周计算)
- 2024-10-23 从LocalDateTime序列化探讨全局一致性序列化
- 2024-10-23 面试必备:ThreadLocal详解 threadlocale
- 2024-10-23 Java,java.time.LocalDate,根据具体的时间,每一日(天)递增
- 2024-10-23 Java LocalDate 类 和 Date 类 java中date类型
- 最近发表
- 标签列表
-
- jsp (69)
- pythonlist (60)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- mysql教程 (60)
- pythonif (68)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- console.table (62)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)