问题描述

在SpringMVC中默认可以注入Model,ModelAndView,@RequestParam,@PathVariable 等,那么这个是怎么实现的,以及怎么注入一个自定义的参数呢

HandlerMethodArgumentResolver

在SpringMVC中有一个接口HandlerMethodArgumentResolver,这个就是用来控制解析controller中的参数类型来进行注入的,下面是一个自定义参数的例子
首先自定义resolver

public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
	//用来判断参数是否支持当前resolver
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> klass = parameter.getParameterType();
		if (klass == String.class) {//这里使用参数类型匹配,MethodParameter还包含了方法注解和参数注解信息,可以使用它们来进行识别
			return true;
		}
		return false;
	}
	//真正返回要注入的值
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
			WebDataBinderFactory binderFactory) throws Exception {
		return "custom string";
	}
}

注册在配置文件中

	<mvc:annotation-driven>
		<mvc:argument-resolvers>
			<bean class="web.controller.MyHandlerMethodArgumentResolver" />
		</mvc:argument-resolvers>
	</mvc:annotation-driven>  

之后就可以使用了

	@RequestMapping("test")
	@ResponseBody
	public String test(String a){
		return a;
	}

在页面访问test连接显示的就是自定义的“custom string”。

实现

下面来看下这个的实现,在MVC启动时会生成一个Resolver的Composite对象,这个包含了所有的注册的Resolver

在HandlerMethodArgumentResolverComposite中有如下几个方法

	//每个参数都会调用resolveArgument进行解析
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, WebDataBinderFactory binderFactory)throws Exception {
		//获得对应的resolver
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
		//使用resolver进行解析
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
	//获得相应的resolver
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		//这里有个ConcurrentHashMap构成的cache可以避免重复的解析
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		//如果cache没有命中则进行解析
		if (result == null) {
			//遍历所有resolver
			for (HandlerMethodArgumentResolver methodArgumentResolver : argumentResolvers) {
				if (logger.isTraceEnabled()) {
					logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
							parameter.getGenericParameterType() + "]");
				}
				//找到对应resolver并存入cache
				if (methodArgumentResolver.supportsParameter(parameter)) {
					result = methodArgumentResolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

那自定义的resolver是如何被springmvc探测到的呢,这里列出argumentResolvers初始化时的赋值

		// Annotation-based argument resolution
		//基于注解的resolver明显有RequestParam,PathVariable等
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		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 ExpressionValueMethodArgumentResolver(getBeanFactory()));

		// Type-based argument resolution
		//基于类型的resolver如Request,Response等
		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
		//这里就是自定义的了resolver了
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		//垫底的resolver,第一个RequestParamMethodArgumentResolver与前面注册的第二个构造参数不同主要用来拦截未标注注解的普通变量(如CharSequence,Number,List等)
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

总结

本文看了自定义controller参数的解析过程