SimpleUrlHandlerMapping
<bean id="loginUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <!-- 第一种方法 --> <property name="mappings"> <props> <prop key="/base/login.htm">loginController</prop> <prop key="/base/login.htm?method=doLogin">loginController</prop> </props> </property> <!-- 第二种方法 --> <property name="urlMap"> <map> <entry key="/base/login.htm" value-ref="loginController"/> </map> </property> <!-- 第三种方法 --> <property name="mappings"> <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location"> <value>urlMap.properties</value> <!-- 此时urlMap.properties文件应放在WebRoot目录下! --> </property> </bean> </property> </bean> <!--第四种--> <bean id="rechargeNotifyParamResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver"> <property name="mappings"> <props> <prop key="/app/gateway/recharge/ccb/notify.htm">index</prop> </props> </property> </bean> <bean id="rechargeNotifyController" class="com.ciispay.site.controller.gateway.RechargeNotifyController" parent="baseController"> <property name="methodNameResolver"> <ref bean="rechargeNotifyParamResolver" /> </property> <property name= "rechargeAppService" ref= "rechargeAppService"/> </bean> <!--在父类org.springframework.web.servlet.mvc.multiaction.MultiActionController中有个set方法--> public final void setMethodNameResolver(MethodNameResolver methodNameResolver) { this.methodNameResolver = methodNameResolver; }
SimpleUrlHandlerMapping中的setMappings和setUrlMap方法
/** * Map URL paths to handler bean names. * This is the typical way of configuring this HandlerMapping. * <p>Supports direct URL matches and Ant-style pattern matches. For syntax * details, see the {@link org.springframework.util.AntPathMatcher} javadoc. * @param mappings properties with URLs as keys and bean names as values * @see #setUrlMap */ public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } /** * Set a Map with URL paths as keys and handler beans (or handler bean names) * as values. Convenient for population with bean references. * <p>Supports direct URL matches and Ant-style pattern matches. For syntax * details, see the {@link org.springframework.util.AntPathMatcher} javadoc. * @param urlMap map with URLs as keys and beans as values * @see #setMappings */ public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); }
然后把这些对应的信息注册到Handler,SimpleUrlHandlerMapping就是将请求的url作为key,然后找到对应的Handler。
initApplicationContext()中registerHandlers()。在registerHandlers()方法中调用父类AbstractUrlHandlerMapping的registerHandler()方法。
/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } /** * Register all handlers specified in the URL map for the corresponding paths. * 注册配置在urlMap中的响应路径 * @param urlMap Map with URL paths as keys and handler beans or bean names as values * @throws BeansException if a handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler);//调用父类AbstractUrlHandlerMapping中的registerHandler()方法 } } }
AbstractUrlHandlerMapping
/** * Register the specified handler for the given URL path. * @param urlPath the URL the bean should be mapped to * @param handler the handler instance or handler bean name String * (a bean name will automatically be resolved into the corresponding handler bean) * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
然后看下Spring是如何取出来的
在DispatcherServlet中的doDispatch()方法中 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... mappedHandler = getHandler(processedRequest, false); ... } /** * Return the HandlerExecutionChain for this request. Try all handler mappings in order. * @param request current HTTP request * @param cache whether to cache the HandlerExecutionChain in a request attribute * @return the HandlerExecutionChain, or {@code null} if no handler could be found * @deprecated as of Spring 3.0.4, in favor of {@link #getHandler(javax.servlet.http.HttpServletRequest)}, * with this method's cache attribute now effectively getting ignored */ @Deprecated protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception { return getHandler(request); } /** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ 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; }
我们主要看: HandlerExecutionChain handler = hm.getHandler(request); 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); } return getHandlerExecutionChain(handler, request); } AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping,并且在AbstractHandlerMethodMapping中重写了AbstractHandlerMapping中的getHandlerInternal方法 方法:
@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); } HandlerMethod 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; } 再看下lookupHandlerMethod()方法 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.urlMap.get(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings addMatchingMappings(this.handlerMethods.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) { 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 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(handlerMethods.keySet(), lookupPath, request); } }