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来完成转换。 RequestParamMethodArgumentResolver支持的类型有,一种是含@RequestParam注解的参数,另一种就是简单类型,如Integer、String、Date、URI, URL,Locale等:
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来解析的。
@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; } }若想获取所有的header信息:则使用另一个RequestHeaderMapMethodArgumentResolver,它则用来获取所有的header信息:
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形式存放。 PathVariableMethodArgumentResolver:主要针对含有@PathVariable的参数,代码如下:
@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的值,即这个
自从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>