赞
踩
Spring MVC源码分析相关文章已出:
更多Spring系列源码分析文章见SpringBoot专栏:
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()方法都很简单,感兴趣可以自己看一下源码。
比如:
ids -> 1,2,3
能够用 List<Long>
接收;enable -> no
能够用 Boolean
接收;要实现的效果:
,
分隔;http://127.0.0.1:38888/person/other?person=saint,15,true,otherInfo&other=hahaha
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,其中有两个方法:
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; }
前端请求传递两个分页参数 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对象自定义一个注解用于标注某个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 的参数解析器集合中:
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> 效果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。