96SEO 2026-02-19 16:58 11
注#xff1a;本文基于Springboot2.0测试#xff0c;如果无法生效可能是spring版本较低导致的。

PS但是spring收到参数后总报错为了全局配置时间类型转换尝试了如下3中方法。
注本文基于Springboot2.0测试如果无法生效可能是spring版本较低导致的。
PS如果你的Controller中的LocalDate类型的参数啥注解RequestParam、PathVariable等都没加也是会出错的因为默认情况下解析这种参数使用ModelAttributeMethodProcessor进行处理而这个处理器要通过反射实例化一个对象出来然后再对对象中的各个参数进行convert但是LocalDate类没有构造函数无法反射实例化因此会报错
当LocalDateTime作为RequestParam或者PathVariable时
这种情况要和时间作为Json字符串时区别对待因为前端json转后端pojo底层使用的是Json序列化Jackson工具HttpMessgeConverter而时间字符串作为普通请求参数传入时转换用的是Converter两者有区别哦。
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;
org.springframework.core.convert.converter.Converter;
org.springframework.http.converter.HttpMessageConverter;import
java.time.format.DateTimeFormatter;Configuration
DateTimeFormatter.ofPattern(yyyy-MM-dd));}};}Beanpublic
DateTimeFormatter.ofPattern(yyyy-MM-dd
mvc的参数解析器好像叫做ParameterConversionService当传入的字符串要转为LocalDateTime类时spring会调用该Converter对这个入参进行转换。
Converter这里遇到了一个坑再这里详细记录下本来想法是为了代码精简将上面匿名内部类的写法精简成lambda表达式的方式
requestMappingHandlerAdapter)public
DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));}
java.lang.IllegalArgumentException:
[com.example.demo126.config.MappingConverterAdapter$$Lambda$522/817994751];
web项目启动注册requestMappingHandlerAdapter的时候会初始化WebBindingInitializer
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
而ConfigurableWebBindingInitializer需要FormattingConversionService,
而FormattingConversionService会将所有的Converter添加进来添加的时候需要获取泛型信息
addFormatters(FormatterRegistry
getBeansOfType(Converter.class))
{registry.addConverter(converter);}for
getBeansOfType(GenericConverter.class))
{registry.addConverter(converter);}for
getBeansOfType(Formatter.class))
{registry.addFormatter(formatter);}
Lambda表达式的接口是Converter并不能得到具体的类型在窥探了SpringMVC源码后才得知原来如此既然指导了原因那解决办法
最简单的方法就是不适用Lambda表达式还是老老实实的使用匿名内部类这样就不会存在上述问题
或者就是等requestMappingHandlerAdapterbean注册完成之后再添加自己的converter就不会注册到FormattingConversionService中
DateTimeUtils.DEFAULT_FORMATTER);
还可以对前端传递的string进行正则匹配如yyyy-MM-dd
已经写好的解析工具类这里就不重复造轮子了下面的方法同样使用了该工具类想要在自己的项目中使用该工具类也很简单在项目pom文件中引入hutool的依赖就可以了。
{binder.registerCustomEditor(LocalDate.class,
{setValue(LocalDate.parse(text,
DateTimeFormatter.ofPattern(yyyy-MM-dd)));}});binder.registerCustomEditor(LocalDateTime.class,
{setValue(LocalDateTime.parse(text,
DateTimeFormatter.ofPattern(yyyy-MM-dd
HH:mm:ss)));}});binder.registerCustomEditor(LocalTime.class,
{setValue(LocalTime.parse(text,
DateTimeFormatter.ofPattern(HH:mm:ss)));}});}
从名字就可以看出来这是在controller做环切这里面还可以全局异常捕获在参数进入handler之前进行转换转换为相应的对象。
这种情况下如同上文描述要利用Jackson的json序列化和反序列化来做
ObjectMapper();//objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);//objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);JavaTimeModule
JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class,new
LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));javaTimeModule.addSerializer(LocalDate.class,new
LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));javaTimeModule.addSerializer(LocalTime.class,new
LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));javaTimeModule.addDeserializer(LocalDateTime.class,new
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));javaTimeModule.addDeserializer(LocalDate.class,new
LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));javaTimeModule.addDeserializer(LocalTime.class,new
LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));objectMapper.registerModule(javaTimeModule).registerModule(new
cn.hutool.core.date.DatePattern;
com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;import
java.time.format.DateTimeFormatter;
java.util.TimeZone;Configuration
Jackson2ObjectMapperBuilderCustomizer
{builder.locale(Locale.CHINA);builder.timeZone(TimeZone.getTimeZone(GMT8:00));builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);JavaTimeModule
JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class,
LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));javaTimeModule.addSerializer(LocalDate.class,
LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));javaTimeModule.addSerializer(LocalTime.class,
LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));javaTimeModule.addDeserializer(LocalDateTime.class,
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));javaTimeModule.addDeserializer(LocalDate.class,
LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));javaTimeModule.addDeserializer(LocalTime.class,
LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));builder.modules(javaTimeModule);};}}
com.fly.hi.common.config;import
com.fasterxml.jackson.core.JsonGenerator;
com.fasterxml.jackson.core.JsonParser;
com.fasterxml.jackson.core.JsonProcessingException;
com.fasterxml.jackson.databind.*;
com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;
org.springframework.core.convert.converter.Converter;import
java.time.format.DateTimeFormatter;
LocalDate转换器用于转换RequestParam和PathVariable参数*/Beanpublic
DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));}};}/***
LocalDateTime转换器用于转换RequestParam和PathVariable参数*/Beanpublic
DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));}};}/***
LocalTime转换器用于转换RequestParam和PathVariable参数*/Beanpublic
DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));}};}/***
Date转换器用于转换RequestParam和PathVariable参数*/Beanpublic
SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);try
Json序列化和反序列化转换器用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json*/Beanpublic
ObjectMapper();objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);//LocalDateTime系列序列化和反序列化模块继承自jsr310我们在这里修改了日期格式JavaTimeModule
JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class,new
LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));javaTimeModule.addSerializer(LocalDate.class,new
LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));javaTimeModule.addSerializer(LocalTime.class,new
LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));javaTimeModule.addDeserializer(LocalDateTime.class,new
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));javaTimeModule.addDeserializer(LocalDate.class,new
LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));javaTimeModule.addDeserializer(LocalTime.class,new
LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//Date序列化和反序列化javaTimeModule.addSerializer(Date.class,
SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);String
formatter.format(date);jsonGenerator.writeString(formattedDate);}});javaTimeModule.addDeserializer(Date.class,
SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);String
RuntimeException(e);}}});objectMapper.registerModule(javaTimeModule);return
{System.out.println(date);System.out.println(dateTime);System.out.println(originalDate);return
InvocableHandlerMethod这里的代码是用来拿到实际参数的
invokeForRequest(NativeWebRequest
getMethodArgumentValues(request,
Arrays.toString(args));}//这里开始调用方法return
getMethodArgumentValues(NativeWebRequest
{//获取方法参数数组包含了入参信息比如类型、泛型等等MethodParameter[]
getMethodParameters();//这个用来存放一会从request
parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);//这里看起来没啥卵用providedArgs为空args[i]
resolveProvidedArgument(parameter,
providedArgs);//这里开始获取到方法实际调用的参数步进if
(this.argumentResolvers.supportsParameter(parameter))
this.argumentResolvers.resolveArgument(parameter,
this.dataBinderFactory);continue;}}return
resolveArgument(MethodParameter
{//根据方法入参获取对应的解析器HandlerMethodArgumentResolver
getArgumentResolver(parameter);//开始解析参数把请求中的parameter转为方法的入参return
resolver.resolveArgument(parameter,
(methodArgumentResolver.supportsParameter(parameter))
methodArgumentResolver;this.argumentResolverCache.put(parameter,
这里遍历参数解析器查找有没有适合的解析器那么有哪些参数解析器呢(我测试的时候有26个)我列出几个重要的看看是不是很眼熟
{RequestParamMethodArgumentResolver7686}
{PathVariableMethodArgumentResolver8359}
{RequestResponseBodyMethodProcessor8366}
{RequestPartMethodArgumentResolver8367}
进入最常用的一个解析器看看它的supportsParameter方法发现就是通过参数注解来获取相应的解析器的。
supportsParameter(MethodParameter
{//如果参数拥有注解RequestParam则走这个分支知道为什么上文要对RequestParam和Json两种数据区别对待了把if
(parameter.hasParameterAnnotation(RequestParam.class))
(Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()))
parameter.getParameterAnnotation(RequestParam.class);return
StringUtils.hasText(requestParam.name()));}else
也就是说对于RequestParam和RequestBody以及PathVariable注解的参数SpringMVC会使用不同的参数解析器进行数据绑定那么这三种解析器分别使用什么Converter解析参数呢分别进入三种解析器看一看首先看下RequestParamMethodArgumentResolver发现内部使用WebDataBinder进行数据绑定底层使用的是ConversionService
binderFactory.createBinder(webRequest,
getTypeConverter().convertIfNecessary(value,
this.propertyEditorRegistry.getConversionService();
TypeDescriptor.forObject(newValue);if
(conversionService.canConvert(sourceTypeDesc,
conversionService.convert(newValue,
然后看下RequestResponseBodyMethodProcessor发现使用的转换器是HttpMessageConverter类型的
//resolveArgument方法内部调用下面进行参数解析
readWithMessageConverters(webRequest,
parameter.getNestedGenericParameterType());//step
readWithMessageConverters()我们看到这里的Converter是HttpMessageConverter
converter.getClass();GenericHttpMessageConverter?
genericConverter.canRead(targetType,
getAdvice().beforeBodyRead(message,
genericConverter.read(targetType,
getAdvice().afterBodyRead(body,
getAdvice().handleEmptyBody(null,
最后看下PathVariableMethodArgumentResolver发现
和RequestParam走的执行路径一致二者都是继承自AbstractNamedValueMethodArgumentResolver解析器因此代码就不贴了。
如果要转换request传来的参数到指定的类型根据入参注解要进行区分
如果是RequestBody那么通过配置ObjectMapper这个玩意儿会注入到Jackson的HttpMessagConverter里面即MappingJackson2HttpMessageConverter中来实现Json格式数据的序列化和反序列化
如果是RequestParam或者PathVariable类型的参数通过配置Converter实现参数转换这些Converter会注入到ConversionService中。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback