前言
在Spring三大框架Controller层中,经常可以看到@RequestParam,@PathVariable,@RequestBody等注解实现自动封装入参,这些都是通过Spring MVC框架提供的参数解析器HandlerMethodArgumentResolver接口实现的,本篇文章将会介绍参数解析器HandlerMethodArgumentResolver的应用
HandlerMethodArgumentResolver解析
我们来看看HandlerMethodArgumentResolver源码:
public interface HandlerMethodArgumentResolver {
/**
* Whether the given {@linkplain MethodParameter method parameter} is
* supported by this resolver.
* @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter;
* {@code false} otherwise
*/
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.
* @param parameter the method parameter to resolve. This parameter must
* have previously been passed to {@link #supportsParameter} which must
* have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null} if not resolvable
* @throws Exception in case of errors with the preparation of argument values
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
源码可以看到HandlerMethodArgumentResolver提供了两个方法,这两个方法作用如下:
- supportsParameter: 判断请求参数是否是支持该解析器(返回true表示支持,会继续调用resolveArgument方法,返回false表示不支持,参数解析结束)
- resolveArgument: 参数解析的具体实现逻辑,最后返回参数具体解析后封装的对象
方法参数说明:
- MethodParameter parameter: 要解析的方法参数对象,可以从对象获取参数的属性(如参数的类型,所修饰的注解等)
- ModelAndViewContainer mavContainer: 当前请求的 ModelAndViewContainer 容器
- NativeWebRequest webRequest: 当前的请求实体
- WebDataBinderFactory binderFactory: 实例创建工厂
我们再来看看HandlerMethodArgumentResolver有哪些已有的具体实现:
可以看到包括我们常用的@RequestParam等注解都有相应的HandlerMethodArgumentResolver的实现,我们就拿RequestParamMapMethodArgumentResolver这个解析具体的实现
RequestParamMapMethodArgumentResolver源码:
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 获取参数的@RequestParam注解
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
// 判断参数是否被@RequestParam注解修饰并且映射类型是否为Map对象以及是否指定某个key值
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
Map<String, String[]> parameterMap = webRequest.getParameterMap();
// 如何参数映射类型为MultiValueMap对象,则将映射到MultiValueMap对象中
if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
// 将参数设置到MultiValueMap对象中
parameterMap.forEach((key, values) -> {
for (String value : values) {
result.add(key, value);
}
});
return result;
}
// 如何参数映射类型不是MultiValueMap对象,则将映射到Map对象中
else {
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
// 将参数设置到Map对象中
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
result.put(key, values[0]);
}
});
return result;
}
}
}
HandlerMethodArgumentResolver应用例子
上面我们介绍了HandlerMethodArgumentResolver的实现原理,有时候我们也需要自定义一个参数解析器解析某个参数,比如开发中有时候我们需要获取用户的登录信息校验权限,每个需要校验权限接口都写个一段获取用户信息的逻辑这样有些冗余,这时我们可以自定义一个参数解析器去解析用户登录信息并封装成存储用户信息实体的对象
这里拷贝其他地方在这里插入代码片
的一个例子来做示例:
@Component
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private UserService userService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
//获取用户ID
Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
if(object == null){
return null;
}
//获取用户信息
UserEntity user = userService.getById((Long)object);
return user;
}
}
这段逻辑并不复杂,就是发送请求时携带Cookie,由参数解析器解析Cookie获取用户信息并封装成UserEntity对象返回
HandlerMethodArgumentResolver和拦截器Interceptor一样,需要注册到IOC容器,可以通过webMvcConfig提供的addArgumentResolvers方法实现,实现代码如下:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
}
}