SpingMVC中日期格式化与转换的那些事

简介: SpingMVC中日期格式化与转换的那些事

ringMVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder,允许流程如下所示:

【1】什么是WebDataBinder

@InitBinder标识的方法,可以对WebDataBinder对象进行初始化(于目标方法执行前执行)。WebDataBinderDataBinder的子类,用于完成由表单字段到JavaBean属性的绑定。

以下为WebDataBinder的注释:

  • 将请求参数绑定到JavaBean对象;
  • 设计用于web环境,但不依赖Servlet API;用作更具体的DataBinder变体的基类,例如org.springframework.web.bind.ServletRequestDataBinder
  • 包括对字段标记的支持,用于解决HTML复选框和下拉选择的常见问题:检测字段是表单的一部分,但由于字段为空而未生成请求参数。
  • 字段标记允许检测该状态并相应地重置相应的bean属性。对于不存在的参数,默认值可以为字段指定一个值,而不是空值。

需要注意的是:

  • @InitBinder方法不能有返回值,它必须声明为void
  • @InitBinder方法的参数通常是WebDataBinder

通常在@Controller注解的类中使用@ExceptionHandler, @InitBinder, and @ModelAttribute方法。如果你想在全局使用(也就是跨controller),那么你可以在@ControllerAdvice 或@RestControllerAdvice类中声明这些方法。

在项目启动时,@RequestMapping 和@ExceptionHandler 方法的基础结构类检测用@ControllerAdvice注解的Spring bean,然后在运行时应用它们的方法。全局@ExceptionHandler 方法(来自@ControllerAdvice)在本地方法(来自@Controller)之后应用。相比之下,全局@ModelAttribute 和@InitBinder 方法应用于本地方法(当前controller中方法)之前。


默认情况下,@ControllerAdvice注解的方法应用于每个请求(也就是说所有控制器)。

【2】如何使用@InitBinder

① @InitBinder日期转换

如下所示,在某个controller或者全局@ControllerAdvice注解的controller中应用。

@InitBinder
   public void initBinder(WebDataBinder binder) {
       SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       dateFormat.setLenient(false);
       binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
   }

该方法常用于表单–对象的日期属性的类型转换。

② 使用xml配置自定义日期转换器

springmvc.xml

<!-- 注册处理器映射器/处理器适配器 ,添加conversion-service属性-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 创建conversionService,并注入dateConvert-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
   <property name="converters">
       <set>
           <ref bean="dateConvert"/>
       </set>
   </property>
</bean>
<!-- 创建自定义日期转换规则 -->
<bean id="dateConvert" class="com.web.convert.DateConvert"/>

自定义日期转换器

public class DateConvert implements Converter<String, Date> {
   public Date convert(String s) {
       SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       try {
           return simpleDateFormat.parse(s);
       } catch (ParseException e) {
           e.printStackTrace();
       }
       return null;
   }
}

除了使用@InitBinder在数据绑定的时候进行格式化,还可以采用Formatter格式化器或者Convert转换器进行格式化。

【3】自定义Formatter进行日期格式化

默认情况下(也就是不做额外配置)前端传输"yyyy-MM-dd HH:mm:ss"格式的日期字符串,后端使用LocalDateTime接收是接收不到的,此时获取的为null。同样,你将LocalDateTime序列化为前端需要的"yyyy-MM-dd HH:mm:ss"格式的日期字符串也是不可能的。这就需要额外进行配置。


简单的String与LocalDateTime转换


@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
public Formatter<LocalDateTime> localDateTimeFormatter() {
    return new Formatter<LocalDateTime>() {
        @Override
        public LocalDateTime parse(String text, Locale locale)  {
            return LocalDateTime.parse(text, DateTimeFormatter.ofPattern(pattern));
        }
        @Override
        public String print(LocalDateTime localDateTime, Locale locale) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
            return formatter.format(localDateTime);
        }
    };
}

【4】自定义Converter进行日期格式转换

String与LocalDateTime格式转换

@Configuration
public class MappingConverterAdapter {
    private static  final Logger logger=LoggerFactory.getLogger(MappingConverterAdapter.class);
    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
    private String pattern;
    //String转换为LocalDateTime
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConvert() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                if(StringUtils.isEmpty(source)){
                    return null;
                }
                DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
                LocalDateTime dateTime = null;
                try {
                    //2020-01-01 00:00:00
                    dateTime = LocalDateTime.parse(dateTimeText(source), df);
                } catch (Exception e) {
                   logger.error(e.getMessage(),e);
                }
                return dateTime;
            }
        };
    }
    //LocalDateTime转换为String
    @Bean
    public Converter<LocalDateTime, String> localDateTimeToString() {
        return new Converter<LocalDateTime, String>() {
            @Override
            public String convert(LocalDateTime localDateTime) {
                if(localDateTime==null){
                    return null;
                }
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
                return formatter.format(localDateTime);
            }
        };
    }
    /**
     * 时间字符串格式化
     * @param source
     * @return
     */
    private static String dateTimeText(String source){
        //2020-01-01 00:00:00
        switch (source.length()){
            case 10:
                logger.debug("传过来的是日期格式:{}",source);
                source=source+" 00:00:00";
                break;
            case 13:
                logger.debug("传过来的是日期 小时格式:{}",source);
                source=source+":00:00";
                break;
            case 16:
                logger.debug("传过来的是日期 小时:分钟格式:{}",source);
                source=source+":00";
                break;
        }
        return source;
    }
}    

【5】LocalDateTime日志格式化与JSON序列/反序列化

当使用json序列化时,有时可能会遇到异常情况

  • 序列化/反序列失败
  • 返回的LocalDateTime不是常见的显示标准"yyyy-MM-dd HH:mm:ss",而是多了一个T,如2021-10-22T09:41:40.601(LocalDateTime默认String格式)

这时你可能需要注入下面这个配置:

import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class LocalDateTimeSerializerConfig {
    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
    private String pattern;
    // localDateTime 序列化器
    @Bean
    public LocalDateTimeSerializer localDateTimeSerializer() {
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
    }
    // localDateTime 反序列化器
    @Bean
    public LocalDateTimeDeserializer localDateTimeDeserializer() {
        return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern));
    }
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
//        return new Jackson2ObjectMapperBuilderCustomizer() {
//            @Override
//            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
                jacksonObjectMapperBuilder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
//                jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
//                jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class,localDateTimeDeserializer());
//            }
//        };
        //这种方式同上
        return builder -> {
            builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
            builder.deserializerByType(LocalDateTime.class,localDateTimeDeserializer());
            builder.simpleDateFormat(pattern);
        };
    }
}


目录
相关文章
Hutool工具BeanUtil.copyProperties实现自定义类型转换器之字符串转时间格式化
hutool工具BeanUtil.copyProperties在字符串转LocalDateTime时默认用的格式为yyyy-MM-ddTHH:mm:ss,所以需要自定义转换器才行,在转换时会优先使用自定义的。 在项目启动时执行一次此段代码即可。
506 0
|
安全 Java 编译器
Java注解(一) 注解的属性,格式,说明
Java注解(一) 注解的属性,格式,说明
119 0
|
Java Spring
springmvc自定义类型转换器解决Date类型转换
springmvc自定义类型转换器解决Date类型转换
|
Java Spring
SpringMVC中自定义(日期)类型转换器
SpringMVC中自定义(日期)类型转换器
SpringMVC中自定义(日期)类型转换器
|
Java Spring
SpringMVC - 数据格式化(@DateTimeFormat & @NumberFormat)(一)
SpringMVC - 数据格式化(@DateTimeFormat & @NumberFormat)(一)
278 0
SpringMVC - 数据格式化(@DateTimeFormat & @NumberFormat)(一)
ADI
[分享] 封装时间格式化函数
[分享] 封装时间格式化函数
ADI
185 0
|
Java Spring
SpringMVC - 数据格式化(@DateTimeFormat & @NumberFormat)(二)
SpringMVC - 数据格式化(@DateTimeFormat & @NumberFormat)(二)
162 0
SpringMVC - 数据绑定(自定义数据转换器:PropertyEditor、Formatter、Converter)(三)
SpringMVC - 数据绑定(自定义数据转换器:PropertyEditor、Formatter、Converter)(三)
167 0
|
JSON 前端开发 Java
为啥你用@JsonFormat注解反序列化LocalDateTime总失败?
最近,有个小伙伴问我:我在SpringBoot项目中,使用@JsonFormat注解标注LocalDateTime类型的字段时,LocalDateTime反序列化失败,这个我该怎么处理呢?别急,我们一起来解决这个问题。
954 0
为啥你用@JsonFormat注解反序列化LocalDateTime总失败?
SpringMVC同时使用和日期转换Formatter时出现问题的解决方法
SpringMVC同时使用和日期转换Formatter时出现问题的解决方法