第三章 SpringMVC之HandlerMapping解析
HandlerMapping的使用主要分为两步:注册和查找。
注册是根据配置文件中的配置将一个字符串和一个Controller类以<key,value>的形式存入到Map中,这个key就是对应的url中的某个字段。
查找就是HandlerMapping根据url中的的某个字段,在Map中以这个字段为key值对应的Controller类,并将Controller类封装成一个HandlerExecutionChain对象,HandlerExecutionChain中除了有Controller对象外,还有一组拦截器。
这里将以SimpleUrlHandlerMapping为例子来分析HandlerMapping是如何根据请求链接找到Controller类的。
一.注册
SpringMVC中的HandlerMapping配置
- <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="mappings">
- <span style="background-color: rgb(255, 0, 0);"><props>
- <prop key="/springMVC.d">/HelloWorld</prop>
- </props></span>
- </property>
- </bean>
- <bean name="/HelloWorld" class="com.wangbiao.springMVC.HelloWorld">
- <property name="methodNameResolver">
- <ref local="methodNameResolver"/>
- </property>
- </bean>
SimpleUrlHandlerMapping的作用就是获取这个集合,然后根据这个集合里的value找到对应的bean,这样就可以把url中的某个字段和我们写的处理器对应起来。下面是SimpleUrlHandlerMapping中的关键源码
- public void initApplicationContext() throws BeansException {
- super.initApplicationContext();
- registerHandlers(this.urlMap);
- }
- 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();
- }
- <span style="color:#ff0000;">registerHandler(url, handler);</span>
- }
- }
- }
AbstractUrlHandlerMapping中的方法,我们来看看AbstractUrlHandlerMapping中的registerHandler(url, handler)
- 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)) {
- <span style="background-color: rgb(255, 255, 255);"><span style="color:#ff0000;">resolvedHandler = getApplicationContext().getBean(handlerName);</span></span>
- }
- }
- 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 {
- <span style="color:#ff0000;">this.handlerMap.put(urlPath, resolvedHandler);</span>
- if (logger.isInfoEnabled()) {
- logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
- }
- }
- }
- }
二.查找
在Dispatcher类中,根据配置文件对handlerMapping进行注册,即对handlerMapping的初始化。
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- <span style="color:#ff0000;">initHandlerMappings(context);</span>
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
- private void initHandlerMappings(ApplicationContext context) {
- this.handlerMappings = null;
- if (this.detectAllHandlerMappings) {
- // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
- <span style="color:#ff0000;">Map<String, HandlerMapping> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);</span>
- if (!matchingBeans.isEmpty()) {
- <span style="color:#ff0000;">this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());</span>
- // We keep HandlerMappings in sorted order.
- OrderComparator.sort(this.handlerMappings);
- }
- }
- else {
- try {
- 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.
- }
- }
下面一步就是真正的根据url中的某个字段到已经注册好了的Map<urlString,Controller>中找出执行这个url请求的Controller,用户的请求在被Dispatcher拦截后,会交给Dispatcher的doDispatch执行。在doDispatch方法中主要看红色标记的getHandler方法
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- int interceptorIndex = -1;
- try {
- ModelAndView mv;
- boolean errorView = false;
- try {
- processedRequest = checkMultipart(request);
- // Determine handler for the current request.
- <span style="color:#ff0000;">mappedHandler = getHandler(processedRequest, false);</span>
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // Process last-modified header, if supported by the handler.
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // Apply preHandle methods of registered interceptors.
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
- // Actually invoke the handler.
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- // Do we need view name translation?
- if (mv != null && !mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- // Apply postHandle methods of registered interceptors.
- if (interceptors != null) {
- for (int i = interceptors.length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
- }
- catch (ModelAndViewDefiningException ex) {
- logger.debug("ModelAndViewDefiningException encountered", ex);
- mv = ex.getModelAndView();
- }
- catch (Exception ex) {
- Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
- mv = processHandlerException(processedRequest, response, handler, ex);
- errorView = (mv != null);
- }
- // Did the handler return a view to render?
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- if (errorView) {
- WebUtils.clearErrorRequestAttributes(request);
- }
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
- "': assuming HandlerAdapter completed request handling");
- }
- }
- // Trigger after-completion for successful outcome.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- }
getHandler方法主要会调用已经注册好了的handlerMapping中的getHandler方法
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() + "'");
- }
- <span style="color:#ff0000;">HandlerExecutionChain handler = hm.getHandler(request);</span>
- if (handler != null) {
- return handler;
- }
- }
- return null;
- }
在来看看HandlerMapping的getHandler方法,可以看到HandlerMapping接口中只有一个getHandler方法。
- /*
- * Copyright 2002-2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.springframework.web.servlet;
- import javax.servlet.http.HttpServletRequest;
- /**
- * Interface to be implemented by objects that define a mapping between
- * requests and handler objects.
- *
- * <p>This class can be implemented by application developers, although this is not
- * necessary, as {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
- * and {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping}
- * are included in the framework. The former is the default if no
- * HandlerMapping bean is registered in the application context.
- *
- * <p>HandlerMapping implementations can support mapped interceptors but do not
- * have to. A handler will always be wrapped in a {@link HandlerExecutionChain}
- * instance, optionally accompanied by some {@link HandlerInterceptor} instances.
- * The DispatcherServlet will first call each HandlerInterceptor's
- * <code>preHandle</code> method in the given order, finally invoking the handler
- * itself if all <code>preHandle</code> methods have returned <code>true</code>.
- *
- * <p>The ability to parameterize this mapping is a powerful and unusual
- * capability of this MVC framework. For example, it is possible to write
- * a custom mapping based on session state, cookie state or many other
- * variables. No other MVC framework seems to be equally flexible.
- *
- * <p>Note: Implementations can implement the {@link org.springframework.core.Ordered}
- * interface to be able to specify a sorting order and thus a priority for getting
- * applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- * @see org.springframework.core.Ordered
- * @see org.springframework.web.servlet.handler.AbstractHandlerMapping
- * @see org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
- * @see org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
- */
- public interface HandlerMapping {
- /**
- * Name of the {@link HttpServletRequest} attribute that contains the path
- * within the handler mapping, in case of a pattern match, or the full
- * relevant URI (typically within the DispatcherServlet's mapping) else.
- * <p>Note: This attribute is not required to be supported by all
- * HandlerMapping implementations. URL-based HandlerMappings will
- * typically support it, but handlers should not necessarily expect
- * this request attribute to be present in all scenarios.
- */
- String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
- /**
- * Name of the {@link HttpServletRequest} attribute that contains the
- * best matching pattern within the handler mapping.
- * <p>Note: This attribute is not required to be supported by all
- * HandlerMapping implementations. URL-based HandlerMappings will
- * typically support it, but handlers should not necessarily expect
- * this request attribute to be present in all scenarios.
- */
- String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
- /**
- * Name of the {@link HttpServletRequest} attribute that contains the URI
- * templates map, mapping variable names to values.
- * <p>Note: This attribute is not required to be supported by all
- * HandlerMapping implementations. URL-based HandlerMappings will
- * typically support it, but handlers should not necessarily expect
- * this request attribute to be present in all scenarios.
- */
- String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
- /**
- * Return a handler and any interceptors for this request. The choice may be made
- * on request URL, session state, or any factor the implementing class chooses.
- * <p>The returned HandlerExecutionChain contains a handler Object, rather than
- * even a tag interface, so that handlers are not constrained in any way.
- * For example, a HandlerAdapter could be written to allow another framework's
- * handler objects to be used.
- * <p>Returns <code>null</code> if no match was found. This is not an error.
- * The DispatcherServlet will query all registered HandlerMapping beans to find
- * a match, and only decide there is an error if none can find a handler.
- * @param request current HTTP request
- * @return a HandlerExecutionChain instance containing handler object and
- * any interceptors, or <code>null</code> if no mapping found
- * @throws Exception if there is an internal error
- */
- HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
- }
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- <span style="color:#ff0000;">Object handler = getHandlerInternal(request);</span>
- 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);
- }
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
- <span style="color:#ff0000;">String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
- Object handler = lookupHandler(lookupPath, request);</span>
- if (handler == null) {
- // We need to care for the default handler directly, since we need to
- // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
- Object rawHandler = null;
- if ("/".equals(lookupPath)) {
- rawHandler = getRootHandler();
- }
- if (rawHandler == null) {
- rawHandler = getDefaultHandler();
- }
- if (rawHandler != null) {
- // Bean name or resolved handler?
- if (rawHandler instanceof String) {
- String handlerName = (String) rawHandler;
- rawHandler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(rawHandler, request);
- handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
- }
- }
- if (handler != null && this.mappedInterceptors != null) {
- Set<HandlerInterceptor> mappedInterceptors =
- this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher);
- if (!mappedInterceptors.isEmpty()) {
- HandlerExecutionChain chain;
- if (handler instanceof HandlerExecutionChain) {
- chain = (HandlerExecutionChain) handler;
- }
- else {
- chain = new HandlerExecutionChain(handler);
- }
- chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()]));
- }
- }
- if (handler != null && logger.isDebugEnabled()) {
- logger.debug("Mapping [" + lookupPath + "] to " + handler);
- }
- else if (handler == null && logger.isTraceEnabled()) {
- logger.trace("No handler mapping found for [" + lookupPath + "]");
- }
- return handler;
- }
- protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
- // Direct match?
- <span style="color:#ff0000;">Object handler = this.handlerMap.get(urlPath);</span>
- if (handler != null) {
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(handler, request);
- return buildPathExposingHandler(handler, urlPath, urlPath, null);
- }
- // Pattern match?
- List<String> matchingPatterns = new ArrayList<String>();
- for (String registeredPattern : this.handlerMap.keySet()) {
- if (getPathMatcher().match(registeredPattern, urlPath)) {
- matchingPatterns.add(registeredPattern);
- }
- }
- String bestPatternMatch = null;
- Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
- if (!matchingPatterns.isEmpty()) {
- Collections.sort(matchingPatterns, patternComparator);
- if (logger.isDebugEnabled()) {
- logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
- }
- bestPatternMatch = matchingPatterns.get(0);
- }
- if (bestPatternMatch != null) {
- handler = this.handlerMap.get(bestPatternMatch);
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(handler, request);
- String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
- // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
- // for all of them
- Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
- for (String matchingPattern : matchingPatterns) {
- if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
- uriTemplateVariables
- .putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
- }
- return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
- }
- // No handler found...
- return null;
- }
废话了那么多,也许大家都已经看的晕头转向了,也没有耐心看下去,那么在这里就对源码中是如何根据url找到对应的controller进行总结一下。
1.SimpleUrlHandlerMapping根据配置文件中的SimpleUrlHandlerMapping的配置,获得一个map集合,map中存储的是{urlString=beanId}。SimpleUrlHandlerMapping调用父类
AbstractUrlHandlerMapping的registerHandler方法。
2.AbstractUrlHandlerMapping的registerHandler方法有SimpleUrlHandlerMapping传入的map中的urlString和beanId,并根据beanId找到对应的bean即controller,将urlString和urlString对应的controller放入AbstractUrlHandlerMapping的handlerMap中。
3.Dispatcher获取IOC容器中已经初始化好的HandlerMapping,再由HandlerMapping调用自己的getHandler方法根据请求返回HandlerExecutionChain对象。AbstractHandlerMapping方法实现了HandlerMapping接口的getHandler方法。AbstractHandlerMapping中的getHandler方法的主要作用是找到controller,并对controller进行封装成HandlerExecutionChain对象,HandlerExecutionChain中除了controller对象外,还有拦截器对象的集合。
4.AbstractHandlerMapping的getHandler方法中又 调用了AbstractHandlerMapping子类的AbstractUrlHandlerMapping getHandlerInternal方法。getHandlerInternal方法就是截取url中对应的controller字段,并以这个字段为key值去AbstractUrlHandlerMapping 的handlerMap中找寻对应的value,即controlle。
总结
handlerMapping的作用
handlerMapping根据用户请求找到执行该请求的controller。
handlerMapping的找到controller过程
1.根据配置文件对url到controller的映射进行注册
2.根据具体的url请求找到执行该请求的controller