SpringBoot自定义参数解析器
我们都知道SpringMvc的Controller的方法上可以接收各种各样的参数,比如HttpServletRequest
或HttpServletResponse
,各种注解@RequestParam
、@RequestHeader
、@RequestBody
、@PathVariable
、@ModelAttribute
,这些参数是从哪里获取的?
这些参数都是由不同的参数解析器为我们解析出来的,可以解析类也可以解析带注解的类
- 我们可以利用解析器解析自定义的参数(类、注解),在我们需要的传入的controller方法上传入它(不需要每次都要在方法内部通过
request
、response
等参数做一系列操作来获取该类对象)
添加解析器
- 我们想要自定义解析参数的时候我们就需要通过更改SpringBoot的配置来添加自己实现的解析类
自定义配置类实现WebMvcConfigurer
接口,重写其中的addArgumentResolvers
方法来添加自己的解析类(通过自动注入的方法注入)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private SecKillUserArgumentResolvers secKillUserArgumentResolvers;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(secKillUserArgumentResolvers);
}
}
实现自定义解析器
- 我们自定义解析器需要实现
HandlerMethodArgumentResolver处理器方法解析器
接口,并实现其中的supportsParameter
和resolveArgument
方法
HandlerMethodArgumentResolver
的接口定义如下:
(1)supportsParameter
用于判断是否支持对某种参数的解析(支持则返回true)
(2)resolveArgument
将请求中的参数值解析为某种对象(具体的操作获取解析对象)
下面这个自定义解析器用于获取User对象(通过token获取保存在redis中的user),无需每次使用resquest和response在controller方法内部获取,可以直接获取到作为参数传入
/**
* 自定义参数解析器
* 解析每次都要获取的user自动传入,无需每次获取
*/
@Component
public class SecKillUserArgumentResolvers implements HandlerMethodArgumentResolver {
@Autowired
private UserService userService;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
Class<?> c = methodParameter.getParameterType();
return c == User.class;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
assert request != null;
String paramToken = request.getParameter(UserServiceImpl.COOKIE_NAME_TOKEN);//COOKIE_NAME_TOKEN="token"
String cookieToken = getCookieValue(request);
if(StringUtils.isEmpty(cookieToken)&&StringUtils.isEmpty(paramToken)){//通过两种方式获取,如果都获取失败则返回null
return null;
}else {
String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
return UserService.getUserByToken(response,token);
}
}
//遍历cookie获取名称相同的那个cookie的值
private String getCookieValue(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
if(cookie.getName().equals(SecKillUserServiceImpl.COOKIE_NAME_TOKEN)){
return cookie.getValue();
}
}
return null;
}
}
到此,一个自定义参数解析器就实现好了,我们可以通过传入参数的形式直接通过解析器帮我们获取到
解析对象的使用
- 我们在controller方法中传入该参数,可以直接为我们获取到User对象
@RequestMapping("/to_list")
public String toList(Model model,User user){
}
Springboot中其他参数解析器
Model
- 我们知道我们可以传入
Model
对象参数就可以直接使用它,我们看看它的参数解析器
ModelAttributeMethodProcessor
类
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
}
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
} else {
try {
attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
} catch (BindException var10) {
if (this.isBindExceptionRequired(parameter)) {
throw var10;
}
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = var10.getBindingResult();
}
}
if (bindingResult == null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
this.bindRequestParameters(binder, webRequest);
}
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
RequestParam
RequestParamMethodArgumentResolver
类,基于注解的方式
如果传入参数parameter上有该注解,则可以解析
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
return true;
} else {
RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
return requestParam != null && StringUtils.hasText(requestParam.name());
}
} else if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
} else {
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
} else {
return this.useDefaultResolution ? BeanUtils.isSimpleProperty(parameter.getNestedParameterType()) : false;
}
}
}
PathVariable
PathVariableMethodArgumentResolver
类,也是基于注解,与RequestParam类似
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
} else if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
return true;
} else {
PathVariable pathVariable = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
return pathVariable != null && StringUtils.hasText(pathVariable.value());
}
}
基于注解自定义参数解析器
- 编写注解类,需要解析的类需要有此注解
@Target(value = ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamModel {
}
- 实现解析器
public class MyArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(ParamModel.class);//带有注解就解析
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
//实现解析对象代码
}
- 配置到
WebMvcConfigurer
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private MyArgumentResolvers myArgumentResolvers;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(myArgumentResolvers);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。