Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver
Posted 秃秃爱健身
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver相关的知识,希望对你有一定的参考价值。
文章目录
一、前言
Spring MVC源码分析相关文章已出:
更多Spring系列源码分析文章见SpringBoot专栏:
二、类型转换器Converter
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
*/
@Nullable
T convert(S source);
Converter的作用是将类型 S
转换为 T
,在参数解析器中使用该接口的实现类 将前端请求参数 转换成 控制器方法(Controller#Method) 需要的参数类型。
此外,还有ConverterFactory<S, R>
(将类型S 转换为 R及其子类型)、ConversionService
(用来在运行时执行类型转换);
Spring 提供的所有支持的类型转换器实现类都在 org.springframework.core.convert.support
包下,大多数转换器的convert()方法都很简单,感兴趣可以自己看一下源码。
比如:
- StringToCollectionConverter将String转换为集合;例如:
ids -> 1,2,3
能够用List<Long>
接收; - 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()方法用来添加自定义的参数类型转换器;
*/
@Bean
public WebMvcConfigurer webMvcConfigurer()
return new WebMvcConfigurer()
@Override
public void addFormatters(FormatterRegistry registry)
registry.addConverter(new Converter<String, Person>()
@Override
public 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.
*/
@Nullable
Object 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;
@Override
public boolean supportsParameter(MethodParameter parameter)
return IPage.class.equals(parameter.getParameterType());
@Override
public 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
@Bean
public WebMvcConfigurer webMvcConfigurer()
return new WebMvcConfigurer()
@Override
public 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
@Autowired
private UserInfoService userInfoService;
@Override
public boolean supportsParameter(MethodParameter methodParameter)
return methodParameter.getParameterType() == UserInfo.class
&& methodParameter.hasParameterAnnotation(UserParam.class);
@Override
public 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
@Bean
public UserInfoArgumentResolver getUserInfoArgumentResolver()
return new UserInfoArgumentResolver();
@Bean
public WebMvcConfigurer webMvcConfigurer()
return new WebMvcConfigurer()
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
resolvers.add(getUserInfoArgumentResolver());
;
6> 效果:
Spring MVC中自定义类型转换器(Date)
目录
前言
java中自带的类型转换器只有八种,也就是把中基本类型对应的对象类
- Integer
- String
- Character
- Long
- Short
- Double
- Float
- Byte
其余的比如Date就没有自动转换器,需要我们自己来写
Spring MVC中自定义类型转换器
我们首先自己创建一个包converter用来放自定义转换器
DateConverter类继承Converter类,注意这里的Converter一定要选正确
Converter<转换前类型,转换后类型>,一般情况下我们都是字符串转换成其他类型
public class DateConverter implements Converter<String, Date>
public Date convert(String s)
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try
date = format.parse(s);
catch (ParseException e)
e.printStackTrace();
return date;
springmvc.xml配置
<mvc:annotation-driven conversion-service="conversionService"/>
<!--配置自定义转换器-->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="edu.xalead.converter.DateConverter"></bean>
</set>
</property>
</bean>
以上是关于Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver
Spring MVC 和 Thymeleaf 自定义错误消息类型转换
spring jpa之实体属性类型转换器AttributeConverter,自定义Converter,通用Converter