問題描述
在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參數的解析過程
