文章目录

  • 一、前言
  • 二、类型转换器Converter
    • 1、自定义类型转换器
  • 三、参数解析器
    • 1、自定义分页参数解析器
    • 2、自定义注解参数解析器

一、前言

Spring MVC源码分析相关文章已出:

  1. Spring MVC <Form>表单中支持REST风格DELETE、PUT类型方法的方式和原理
  2. Spring MVC请求执行流程
  3. Spring MVC如何将请求映射到Controller
  4. 使用FastJsonHttpMessageConverter解析@RequestBody参数
  5. Spring MVC多种请求入参处理方式都在这了

更多Spring系列源码分析文章见SpringBoot专栏:

  • 精通Spring Boot

二、类型转换器Converter

Spring官方文档

Spring 3.0 引入了一个 core.convert,并提供通用类型转换系统的包。系统定义了一个 SPI 来实现类型转换逻辑,并定义一个 API 来在运行时执行类型转换。

这套类型转换系统的顶层接口为:Converter

@FunctionalInterface
public interface Converter<S, T> {/*** Convert the source object of type {@code S} to target type {@code T}.* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})* @return the converted object, which must be an instance of {@code T} (potentially {@code null})* @throws IllegalArgumentException if the source cannot be converted to the desired target type*/@NullableT convert(S source);}

Converter的作用是将类型 S 转换为 T,在参数解析器中使用该接口的实现类 将前端请求参数 转换成 控制器方法(Controller#Method) 需要的参数类型。

此外,还有ConverterFactory<S, R>(将类型S 转换为 R及其子类型)、ConversionService(用来在运行时执行类型转换);

Spring 提供的所有支持的类型转换器实现类都在 org.springframework.core.convert.support 包下,大多数转换器的convert()方法都很简单,感兴趣可以自己看一下源码。

比如:

  1. StringToCollectionConverter将String转换为集合;例如:ids -> 1,2,3 能够用 List<Long> 接收;
  2. StringToBooleanConverter将String转换为Boolean,例如:enable -> no 能够用 Boolean 接收;

1、自定义类型转换器

要实现的效果:

  • 请求中传递JavaModel数据,多个属性之间以英文逗号,分隔;
    比如:POST请求 http://127.0.0.1:38888/person/other?person=saint,15,true,otherInfo&other=hahaha
  • 后端Controller的方法中使用JavaModel接收;

1> Java Model类:

public class Person {private String name;private Integer age;private Boolean sex;private String otherInfo;
}

2> 自定义Converter:

逻辑很简单,就是将Spring字符串用英文逗号,分隔,按Person类的属性顺序,将请求中的参数填充到Person对象中。

@Configuration
public class WebMvcConfig {/*** 自定义参数类型转换器* WebMvcConfigurer#addFormatters()方法用来添加自定义的参数类型转换器;*/@Beanpublic WebMvcConfigurer webMvcConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Person>() {@Overridepublic Person convert(String source) {if (StringUtils.isEmpty(source)) {return null;}final String[] sourceArgs = source.split(",");Person person = new Person();person.setName(sourceArgs[0]);person.setAge(Integer.valueOf(sourceArgs[1]));person.setSex(Boolean.valueOf(sourceArgs[2]));person.setOtherInfo(String.valueOf(sourceArgs[3]));return person;}});}};}
}

3> controller方法:

@PostMapping("/person/other")
public String otherPerson(Person person, String other) {return person.toString() + other;
}

4> 效果:

三、参数解析器

Spring中参数解析器的最上层接口为HandlerMethodArgumentResolver,其中有两个方法:

  • supportsParameter() 判断当前参数解析器是否支持解析的方法参数;
  • resolveArgument() 从请求数据中解析出当前方法参数对应的参数值。
public interface HandlerMethodArgumentResolver {/*** Whether the given {@linkplain MethodParameter method parameter} is* supported by this resolver.*/boolean supportsParameter(MethodParameter parameter);/*** Resolves a method parameter into an argument value from a given request.* A {@link ModelAndViewContainer} provides access to the model for the* request. A {@link WebDataBinderFactory} provides a way to create* a {@link WebDataBinder} instance when needed for data binding and* type conversion purposes.*/@NullableObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}

1、自定义分页参数解析器

前端请求传递两个分页参数 pageNum、pageSize,后端用一个IPage模型接收;

参数解析器策略要支持的是 IPage.class,核心是 HandlerMethodArgumentResolver 的两个方法实现。

这里基于spring-data-common 包下的PageableHandlerMethodArgumentResolver 做一个扩展。

1> maven依赖:

<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-commons</artifactId>
</dependency>

2> 自定义参数解析器PageHandlerMethodArgumentResolver

package com.saint.config;import com.saint.model.IPage;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;/*** 自定义分页参数解析器** @author Saint*/
public class PageHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {private static final int DEFAULT_PAGE = 0;private static final int DEFAULT_SIZE = 10;private static final String DEFAULT_PAGE_PARAM = "pageNum";private static final String DEFAULT_SIZE_PARAM = "pageSize";/*** 组合 `spring-data-commons` 包下的PageableHandlerMethodArgumentResolver,实现分页参数解析功能*/private final PageableHandlerMethodArgumentResolver pageableArgumentResolver;public PageHandlerMethodArgumentResolver() {PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();resolver.setFallbackPageable(PageRequest.of(DEFAULT_PAGE, DEFAULT_SIZE));resolver.setSizeParameterName(DEFAULT_SIZE_PARAM);resolver.setPageParameterName(DEFAULT_PAGE_PARAM);resolver.setOneIndexedParameters(true);this.pageableArgumentResolver = resolver;}@Overridepublic boolean supportsParameter(MethodParameter parameter) {return IPage.class.equals(parameter.getParameterType());}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {Pageable pageable =pageableArgumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);return new IPage(pageable.getPageNumber() + 1, pageable.getPageSize());}
}

3> 将自定义的参数解析器添加到Spring MVC 的参数解析器集合中:

@Configuration
public class WebMvcConfig {@Beanpublic WebMvcConfigurer webMvcConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new PageHandlerMethodArgumentResolver());}};}
}

4> Controller方法:

/*** http://localhost:38888/page?pageNum=9&pageSize=20*/
@GetMapping("/page")
public String page(IPage page) {return page.toString();
}

5> 效果:

  • GET /xxx?pageNum=1&pageSize=10请求的分页参数被解析成了IPage对象

2、自定义注解参数解析器

自定义一个注解用于标注某个JavaModel,JavaModel中的信息是根据请求中的某些数据间接得到。

1> 自定义的注解@UserParam:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserParam {}

2> 自定义的JavaModel:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {private String userCode;private String userName;private String address;
}

3> 自定义的参数解析器:

package com.saint.config;import com.saint.annotation.UserParam;
import com.saint.model.UserInfo;
import com.saint.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;import javax.servlet.http.HttpServletRequest;/*** @author Saint*/
public class UserInfoArgumentResolver implements HandlerMethodArgumentResolver {@Autowiredprivate UserInfoService userInfoService;@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {return methodParameter.getParameterType() == UserInfo.class&& methodParameter.hasParameterAnnotation(UserParam.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);String token = request.getHeader("token");UserInfo userInfo = userInfoService.getUserInfoByToken(token);return userInfo;}
}

4> UserInfoService:

package com.saint.service;import com.saint.model.UserInfo;
import org.springframework.stereotype.Service;/*** @author Saint*/
@Service
public class UserInfoService {public UserInfo getUserInfoByToken(String token) {// todo 调用用户中心返回用户的信息UserInfo user = new UserInfo("code01", "saint", "你心里 " + token);return user;}
}

5> 将自定义的参数解析器添加到Spring MVC 的参数解析器集合中:

  • 一定要先将自定义的参数解析器实例化一个Bean到Spring容器中,否则其中无法调用其他SpringBean。
package com.saint.config;import com.saint.model.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.List;/*** @author Saint*/
@Configuration
public class WebMvcConfig {@Beanpublic UserInfoArgumentResolver getUserInfoArgumentResolver() {return new UserInfoArgumentResolver();}@Beanpublic WebMvcConfigurer webMvcConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(getUserInfoArgumentResolver());}};}
}

6> 效果:

Spring MVC自定义类型转换器Converter、参数解析器HandlerMethodArgumentResolver相关推荐

  1. 拦截器HandlerInterceptor+方法参数解析器HandlerMethodArgumentResolver用于统一获取当前登录用户信息

    文章目录 前言 一.拦截器+方法参数解析器 是什么? 二.具体实现步骤 1.自定义权限拦截器LoginInterceptor拦截所有request请求,并将token解析为currentUser,最终 ...

  2. java converter转换器_在SpringMVC中设置自定义类型转换器Converter

    前言 在SpringMVC中为我们提供了许多内置的类型转换器,当我们在HTML表单中发起一个请求时,Spring会根据表单项中name属性的值映射到POJO的属性名,调用相对性属性的set方法帮我们把 ...

  3. Spring自定义参数解析器

      虽然Spring提供了比较完善的参数解析器,但是对于一些特殊的数据类型我们还是需要进行特殊处理,这样会提高代码的复杂度,增加冗余的代码,降低代码可读性和可维护性.所以自定义参数解析器是一个很好的解 ...

  4. 【第二十一讲】参数解析器

    [第二十一讲]参数解析器 文章目录 [第二十一讲]参数解析器 1-常见参数解析器 2-组合模式在 Spring 中的体现 总结 常见参数解析器 组合模式在 Spring 中的体现 ${} #{} 小技 ...

  5. SpringMVC 参数解析器

    一.问题 springMVC对于下面这种接口,参数是怎么解析的: @GetMapping("/hello/{id}") public void hello3(@PathVariab ...

  6. (十六)ATP应用测试平台——java应用中的过滤器Filter、拦截器Interceptor、参数解析器Resolver、Aop切面,你会了吗?

    前言 过滤器Filter.拦截器Interceptor.参数解析器Resolver.Aop切面是我们应用开发中经常使用到的技术,到底该如何使用这些web附属功能, 本小节我们就分别介绍一下其各自的用法 ...

  7. 详解Spring MVC请求参数类型,解决中文乱码问题,自定义类型转换器,Spring MVC相关注解

    #SpringMVC SpringMVC请求 简单类型 简单类型包括:基本类型,基本类型的包装类型,字符串 编写Controller @RequestMapping("/param" ...

  8. springmvc自定义参数解析器/类型转换器

    概述 有些时候我们需要对GET请求的入参做自定义的处理,比较常见的就是字符串反序列化时间类型了,常用的像@DateTimeFormat注解,但是这需要在每个入参的属性上都加上这个注解,比较费手,那么我 ...

  9. spring MVC使用自定义的参数解析器解析参数

    目录 写在前面 编写自定义的参数解析器解析请求参数 项目结构 定义注解 实体类 controller 定义参数解析器 注册参数解析器 启动项目 发起请求查看结果 写在前面 如果还有小伙伴不知道spri ...

最新文章

  1. 在Apache上搭建pywebsocket提供html5的websocket服务
  2. Git-将已有的项目转换为GIT项目托管到 GITHUB 仓库
  3. sqlplus -prelim,sqplus区别
  4. S/4HANA里通过key user tool将Extension field添加到UI上的技术实现
  5. 图数据库neo4j安装、neo4j使用
  6. 【实践案例】Databricks 数据洞察在美的暖通与楼宇的应用实践
  7. tomcat 占用的内存越来越大_智能手机内存为什么越来越大 就这三点原因
  8. 鸿蒙撕系统裂安卓阵营,鸿蒙系统年底问世 安卓阵营是不是该瑟瑟发抖了
  9. 都说 VR 开发坑太多,结果华为云 5G Cloud VR 放了个大招儿!
  10. 搭建etcd 3.4.15集群(详细教程,包括选举过程、数据备份和恢复)
  11. VS2012下创建QT5应用程序
  12. VLC框架总结(二)VLC源码及各modules功能介绍
  13. error: Zip file too big (greater than 4294959102 bytes)
  14. SIFT与SURF算法
  15. excel如何深度隐藏sheet呢?
  16. 有赞搜索系统的技术内幕
  17. python 实现雪容融
  18. uAVS2 AVS2实时编码器
  19. Jetson Nano 从入门到实战(案例:Opencv配置、人脸检测、二维码检测)
  20. 当今世界最牛的25位顶尖大数据科学家

热门文章

  1. 品质管控计划ppt怎样写_分享|一位品质经理的质量管控经验总结
  2. 计算机电子网络战,网络战与电子战融合,''你中有我,我中有你''
  3. 一些有用的网址,实时更新.
  4. 垃圾分类知识竞答活动小程序复盘
  5. 倩女手游经验计算机,倩女手游三界实验室第一期 气血值的计算及提升方法
  6. 关于用硬盘安装工具Win6Ins_v1.2.0.62安装时提示缺少hildr.mbr文件的解决办法
  7. 向“下”出发,陪玩江湖风云再起?
  8. 超薄本笔记本如何清灰--真的是全拆完了呀
  9. 一个cocos2dx的扩展库
  10. 明日之后今天又又又更新了:熟练度溢出问题已解决,即将开放18庄?