问题描述
在SpringMVC中默认可以注入Model,ModelAndView,@RequestParam,@PathVariable 等,那么这个是怎么实现的,以及怎么注入一个自定义的参数呢
HandlerMethodArgumentResolver
在SpringMVC中有一个接口HandlerMethodArgumentResolver,这个就是用来控制解析controller中的参数类型来进行注入的,下面是一个自定义参数的例子
首先自定义resolver
public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {//用来判断参数是否支持当前resolver@Overridepublic boolean supportsParameter(MethodParameter parameter) {Class<?> klass = parameter.getParameterType();if (klass == String.class) {//这里使用参数类型匹配,MethodParameter还包含了方法注解和参数注解信息,可以使用它们来进行识别return true;}return false;}//真正返回要注入的值@Overridepublic 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")@ResponseBodypublic 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 {//获得对应的resolverHandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");//使用resolver进行解析return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}//获得相应的resolverprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {//这里有个ConcurrentHashMap构成的cache可以避免重复的解析HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);//如果cache没有命中则进行解析if (result == null) {//遍历所有resolverfor (HandlerMethodArgumentResolver methodArgumentResolver : argumentResolvers) {if (logger.isTraceEnabled()) {logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +parameter.getGenericParameterType() + "]");}//找到对应resolver并存入cacheif (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参数的解析过程