springMVC的請求映射
上一次分析了一下springMVC的大致流程,這次細分一下,對請求映射進行分析。
先從DispatcherServlet中的getHandler()方法分析
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}return null;
}
可以看到這是遍歷所有的handlerMappings然后第一個返回不是NULL的handler既是,那handlerMappings包含了那些實現類呢?我們來看看initHandlerMappings這個方法
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;//detectAllHandlerMappings 默認是trueif (this.detectAllHandlerMappings) {// 從ApplicationContext 里面獲取HandlerMapping的實現類Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);//根據Order對HandlerMappings進行排序if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// We keep HandlerMappings in sorted order.AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {try {//獲取bean name是handlerMapping的HandlerMapping實現類HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}//若HandlerMappings為空,則設置默認的在DispatcherServlet同級目錄下的DispatcherServlet.properties里面設置的org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterif (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}
}
所以若我們想使用自定義的HandlerMapping 可在springMVC的xml配置文件中加上一個HandlerMapping的實現類,若是使用的是 這個進行配置的,其默認會加載RequestMappingHandlerMapping這個實現類(具體在org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser見),而可以在web.xml中將detectAllHandlerMappings 設置為false,然后在springMVC.xml中配置一個name為handlerMapping的實現類。如下
// web.xml
<servlet><servlet-name>myweb</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:mvc.xml</param-value></init-param><!-- 不加載所有的HandlerMapping的實現類的bean --><init-param><param-name>detectAllHandlerMappings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>//mvc.xml
<bean name="handlerMapping" class="xxx具體實現類">
繼續看handlerMapping是怎么根據request獲取到HandlerExecutionChain這個包含攔截器、具體處理的controller的,在AbstractHandlerMapping中以下方法
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//獲取具體的處理類Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}//根據定義的Interceptors過濾出需要執行的攔截器,聚合成HandlerExecutionChain這個執行鏈HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//CORS跨域請求if (CorsUtils.isCorsRequest(request)) {CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}
看getHandlerInternal(request) 這個方法,這個方法是個抽象方法,所以其實現在它的子類中,可以看出這個是一個模板模式,其在抽象類中定義出方法的骨架,而后某些方法交給子類實現,這個方法有兩個實現類,AbstractHandlerMethodMapping,AbstractUrlHandlerMapping 這兩個具體實現類,我們先看AbstractHandlerMethodMapping 這個類實現的
/*** Look up a handler method for the given request.*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {//獲取請求路徑String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);if (logger.isDebugEnabled()) {logger.debug("Looking up handler method for path " + lookupPath);}//開啟讀鎖this.mappingRegistry.acquireReadLock();try {//獲取handlerMethodHandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);if (logger.isDebugEnabled()) {if (handlerMethod != null) {logger.debug("Returning handler method [" + handlerMethod + "]");}else {logger.debug("Did not find handler method for [" + lookupPath + "]");}}return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {//釋放鎖this.mappingRegistry.releaseReadLock();}
}
從這里可以看出 其最后處理的是一個HandlerMethod類,我們來看看這個類的結構
public class HandlerMethod {private final Object bean;private final BeanFactory beanFactory;private final Class<?> beanType;private final Method method;private final Method bridgedMethod;private final MethodParameter[] parameters;private final HandlerMethod resolvedFromHandlerMethod;
}
可以看出其是一個聚合的類,里面包含了bean,method,故而其可以一個方法對應一個請求。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<Match>();//根據請求路徑匹配List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);if (directPathMatches != null) {addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {// No choice but to go through all mappings...addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}if (!matches.isEmpty()) {//最長路徑匹配原則Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));Collections.sort(matches, comparator);if (logger.isTraceEnabled()) {logger.trace("Found " + matches.size() + " matching mapping(s) for [" +lookupPath + "] : " + matches);}Match bestMatch = matches.get(0);if (matches.size() > 1) {if (CorsUtils.isPreFlightRequest(request)) {return PREFLIGHT_AMBIGUOUS_MATCH;}Match secondBestMatch = matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");}}返回handlerMethodhandleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}
}
AbstractHandlerMethodMapping 這個抽象類實現了InitializingBean接口,所以我們可以看看afterPropertiesSet()方法,其在實例化進行的初始化操作,initHandlerMethods()方法,可以看出其在初始化時,就初始化了HandlerMethod這個。
protected void initHandlerMethods() {// 獲取所有的BeanString[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));for (String name : beanNames) {//判斷這個bean是否包含Controller,和RequestMapping這兩個注解if (!name.startsWith(SCOPED_TARGET_NAME_PREFIX) && isHandler(getApplicationContext().getType(name))) {//將其注冊到MappingRegistrydetectHandlerMethods(name);}}//空方法handlerMethodsInitialized(getHandlerMethods());
}