HandlerMethodArgumentResolver接口的抽象类:AbstractMessageConverterMethodArgumentResolver仅仅引入了HttpMessageConverter,即转换的工作有这些HttpMessageConverter来完成具体的转换和判断由子类来实现。
如下:
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
protected final Log logger = LogFactory.getLog(getClass());
protected final List<HttpMessageConverter<?>> messageConverters;
protected final List<MediaType> allSupportedMediaTypes;
//略
}
AbstractMessageConverterMethodArgumentResolver 的抽象子类AbstractMessageConverterMethodProcessor仅仅是加入了对响应数据进行转换的支持。
也就是AbstractMessageConverterMethodProcessor的子类不仅可以用来转换请求数据,也可以用来转换响应数据。
AbstractMessageConverterMethodProcessor的子类HttpEntityMethodProcessor,支持请求和响应的转换,代码如下:
@Override
public boolean supportsParameter(MethodParameter parameter) {
return HttpEntity.class.equals(parameter.getParameterType());
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return HttpEntity.class.isAssignableFrom(returnType.getParameterType());
}
AbstractMessageConverterMethodProcessor的子类RequestResponseBodyMethodProcessor:支持@RequestBody和@ResponseBody,代码如下:
@Override
public boolean supportsParameter(MethodParameter parameter) {
//查找参数中是否含有@RequestBody注解
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//查找参数中是否含有@RequestBody注解或者controller类上是否含有@RequestBody
return ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) ||
(returnType.getMethodAnnotation(ResponseBody.class) != null));
}
同理RequestResponseBodyMethodProcessor也会使用相应的HttpMessageConverter来进行转换。如public Map<String,Object> testrequestBody(@RequestBody Map<String,Object> map1)则会选择MappingJackson2HttpMessageConverter或者MappingJacksonHttpMessageConverter来完成转换。
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(paramType)) {
String paramName = parameter.getParameterAnnotation(RequestParam.class).value();
return StringUtils.hasText(paramName);
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
else if (MultipartFile.class.equals(paramType) || "javax.servlet.http.Part".equals(paramType.getName())) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(paramType);
}
else {
return false;
}
}
}
即当请求为 http://localhost:8080/test?name=abc时,处理函数若为test(String name),则对name的解析就是采用RequestParamMethodArgumentResolver来解析的。
RequestHeaderMethodArgumentResolver:主要用来处理含有@RequestHeader注解的参数,但同时该参数又不是Map类型。如下:
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestHeader.class)
&& !Map.class.isAssignableFrom(parameter.getParameterType());
}
@Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
String[] headerValues = request.getHeaderValues(name);
if (headerValues != null) {
return (headerValues.length == 1 ? headerValues[0] : headerValues);
}
else {
return null;
}
}
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
//这里已经写明白了,要求参数必须含有@RequestHeader注解,并且是Map类型
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestHeader.class)
&& Map.class.isAssignableFrom(parameter.getParameterType());
}
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
Class<?> paramType = parameter.getParameterType();
if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result;
if (HttpHeaders.class.isAssignableFrom(paramType)) {
result = new HttpHeaders();
}
else {
result = new LinkedMultiValueMap<String, String>();
}
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
for (String headerValue : webRequest.getHeaderValues(headerName)) {
result.add(headerName, headerValue);
}
}
return result;
}
else {
Map<String, String> result = new LinkedHashMap<String, String>();
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
String headerValue = webRequest.getHeader(headerName);
result.put(headerName, headerValue);
}
return result;
}
}
}
从上面的解析过程可以看出,参数类型可以是普通的Map类型,也可以是MultiValueMap或者进一步的HttpHeaders,他们与普通Map类型的区别是他们对value值后者们是以List形式存放,前者是以String形式存放。
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
return StringUtils.hasText(paramName);
}
return true;
}
@Override
@SuppressWarnings("unchecked")
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars =
(Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
}
对于支持的类型也说明的很详细。首先必须含有@PathVariable注解,其次如果是Map类型,必须要指定@PathVariable的值,即这个
ArgumentResolver只能获取一个uri变量。要想获取多个则要使用PathVariableMapMethodArgumentResolver:
@Override
public boolean supportsParameter(MethodParameter parameter) {
PathVariable annot = parameter.getParameterAnnotation(PathVariable.class);
return ((annot != null) && (Map.class.isAssignableFrom(parameter.getParameterType()))
&& (!StringUtils.hasText(annot.value())));
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> uriTemplateVars =
(Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
if (!CollectionUtils.isEmpty(uriTemplateVars)) {
return new LinkedHashMap<String, String>(uriTemplateVars);
}
else {
return Collections.emptyMap();
}
}
它要求必须含有@PathVariable注解,并且必须是Map类型,并且@PathVariable注解的value没有值。同时我们可以从PathVariableMapMethodArgumentResolver和PathVariableMethodArgumentResolver上面看出,他们的取值都是从request的属性上进行获取的webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);也就是说,在解析完@RequestMapping匹配工作后,便将这些参数设置进request的属性上,属性名为HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE。其他的HandlerMethodArgumentResolver可以自行研究,这里不再说明。
至此,我们就要说明下HandlerMethodArgumentResolver的注册来源:
它的来源分为两部分,一部分spring默认的HandlerMethodArgumentResolver,另一部分就是我们自定义的HandlerMethodArgumentResolver。
还是先看mvc:annotation-driven中配置自定义的HandlerMethodArgumentResolver:
<mvc:annotation-driven >
<mvc:argument-resolvers>
<bean class="xxx"></bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
在mvc:argument-resolvers标签下配置相应的自定义的HandlerMethodArgumentResolver。
然后在mvc:annotation-driven的注解驱动类AnnotationDrivenBeanDefinitionParser中会有这样的代码:
ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
//略
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
其中getArgumentResolvers就是获取我们自定义的HandlerMethodArgumentResolver
private ManagedList<?> getArgumentResolvers(Element element, ParserContext parserContext) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
if (resolversElement != null) {
ManagedList<BeanDefinitionHolder> argumentResolvers = extractBeanSubElements(resolversElement, parserContext);
return wrapWebArgumentResolverBeanDefs(argumentResolvers, parserContext);
}
return null;
}
从上面的代码可以看出,获取我们自定义的HandlerMethodArgumentResolver然后把它设置进RequestMappingHandlerAdapter的customArgumentResolvers参数中,RequestMappingHandlerAdapter有两个与HandlerMethodArgumentResolver有关的参数:
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
private HandlerMethodArgumentResolverComposite argumentResolvers;
HandlerMethodArgumentResolverComposite 也仅仅是内部存放一个List<HandlerMethodArgumentResolver>集合,同时本身又继承HandlerMethodArgumentResolver,所以它的实现都是靠内部的List<HandlerMethodArgumentResolver>集合来实现的。
private final List<HandlerMethodArgumentResolver> argumentResolvers =
new LinkedList<HandlerMethodArgumentResolver>();
//使用了适合高并发的ConcurrentHashMap来进行缓存
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);
/**
* Return a read-only list with the contained resolvers, or an empty list.
*/
public List<HandlerMethodArgumentResolver> getResolvers() {
return Collections.unmodifiableList(this.argumentResolvers);
}
/**
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
* {@link HandlerMethodArgumentResolver}.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
/**
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
*/
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
在RequestMappingHandlerAdapter完成参数设置后,会调用afterPropertiesSet方法
@Override
public void afterPropertiesSet() {
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
initControllerAdviceCache();
}
getDefaultArgumentResolvers方法完成了所有的HandlerMethodArgumentResolver的汇总,如下:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ValueMethodArgumentResolver(getBeanFactory()));
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
//获取我们自定义的HandlerMethodArgumentResolver
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
不仅汇总了spring默认的,同时加进来我们自定义的HandlerMethodArgumentResolver。
自从spring3.1 开始就有了这个接口,可以为@RequestMapping标注的方法扩展传入的参数。
以shiro为例,扩展一个标注,@CurrentUser,只要有这个标注,就可以在shiro的安全上下文中取出适当的对象直接从参数传入,request响应函数。
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Documented
- @Target({ElementType.PARAMETER})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface CurrentUser {
- }
- @RequestMapping(value = "/test1")
- public @ResponseBody String test1(@CurrentUser Long userId) {
- return userId.toString();
- }
- @RequestMapping(value = "/test2")
- public @ResponseBody String test2(@CurrentUser UserDetails userDetails) {
- return userDetails.toString();
- }
- import java.lang.annotation.Annotation;
- 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 com.github.yingzhuo.mycar2.annotation.CurrentUser;
- import com.github.yingzhuo.mycar2.security.SecurityUtils;
- import com.github.yingzhuo.mycar2.security.UserDetails;
- public class CurrentUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
- @Override
- public boolean supportsParameter(MethodParameter parameter) {
- Class<?> klass = parameter.getParameterType();
- if (klass.isAssignableFrom(UserDetails.class) || klass.isAssignableFrom(Long.class)) {
- Annotation[] as = parameter.getParameterAnnotations();
- for (Annotation a : as) {
- if (a.annotationType() == CurrentUser.class) {
- return true;
- }
- }
- }
- return false;
- }
- @Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
- WebDataBinderFactory binderFactory) throws Exception {
- if ((SecurityUtils.isAuthenticated() || SecurityUtils.isRemembered()) == false) {
- return null;
- }
- Class<?> klass = parameter.getParameterType();
- UserDetails userDetails = SecurityUtils.getUserDetails();
- if (klass.isAssignableFrom(UserDetails.class)) {
- return SecurityUtils.getUserDetails();
- }
- if (klass.isAssignableFrom(Long.class)) {
- return userDetails != null ? userDetails.getId() : null;
- }
- return null;
- }
- }
最后,需要配置一下
- <mvc:annotation-driven>
- <mvc:argument-resolvers>
- <bean class="xxx.yyy.CurrentUserHandlerMethodArgumentResolver" />
- </mvc:argument-resolvers>
- </mvc:annotation-driven>