Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(2)
上一节我们看到 当***-serlvet.xml中未声明HandlerMapping的实体bean时,默认构造
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
的实例。
所以本节就从这两个类入手研究,HandlerMapping中最重要的方法是 getHandler 用于获取HandlerExcutionChain的对象。
先来看BeanNameUrlHandlerMapping类,它的内部没有实现getHandler方法,而它继承自AbstractDetectingUrlHandlerMapping,所以自底向上我们去查看AbstractDetectingUrlHandlerMapping类的实现。
在这个类中,我看到了一个方法
/** * Register all handlers found in the current ApplicationContext. * <p>The actual URL determination for a handler is up to the concrete * {@link #determineUrlsForHandler(String)} implementation. A bean for * which no such URLs could be determined is simply not considered a handler. * @throws org.springframework.beans.BeansException if the handler couldn't be registered * @see #determineUrlsForHandler(String) */ protected void detectHandlers() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }
whether to detect handler beans in ancestor ApplicationContexts.
private boolean detectHandlersInAncestorContexts = false;
该方法首先查询了上下文中所有的bean,因为查询的Type是Object.class,得到了所有的bean名称,之后解析bean名称,查看是否存是urlName,determineUrlsForHandler方法在BeanNameUrlHandlerMapping类中具体实现:
/** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (int i = 0; i < aliases.length; i++) { if (aliases[i].startsWith("/")) { urls.add(aliases[i]); } } return StringUtils.toStringArray(urls); }
可以看出它查找了所有beanName以“/”开头的bean,将它们的name作为url返回。
接下来就对那些作为处理url请求的bean进行handler的注册,registerHandler方法在AbstractDetectingUrlHandlerMapping的父类AbstractUrlHandlerMapping类中:
/** * Register the specified handler for the given URL paths. * @param urlPaths the URLs that the bean should be mapped to * @param beanName the name of the handler bean * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } }
/** * 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 handler [" + handler + "] to URL path [" + urlPath + "]: There is already handler [" + resolvedHandler + "] mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to handler [" + resolvedHandler + "]"); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to handler [" + resolvedHandler + "]"); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]"); } } } }
handlerMap就是一个K-V保存url对应的handler,初始时我们传入的Object handler参数是String对象beanNames,lazyInit表示是延迟初始化handler,如果设为true那么在注册handler的时候我们存入的handler还是String,如果是false,该方法中被用来取得bean实例,所以handlerMap中存放的V就是bean的对象,使用Object泛型是因为bean的不一致。
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
注意上面的setDefaultHandler方法,它是AbstractUrlHandlerMapping类的父类AbstractHandlerMapping中的方法,在AbstractHandlerMapping中有一个Object对象属性:
private Object defaultHandler;
从字面意思可以理解为默认的处理器,因为它对应的映射是"/*",即处理所有符合该表达式的url请求。
阅读完BeanNameUrlHandlerMapping和AbstractDetectingUrlHandlerMapping两个类后,我们发现它们中的方法主要进行了handler的set,而handler的get却没有在它们中体现,所以再向上走,我们查看最高的父类AbstractHandlerMapping类,它直接实现了HandlerMapping接口,所以剖析它的getHandler方法更容易让我们理解,handler获取的流程。
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); }
该方法首先调用了getHandlerInternal处理request,getHandlerInternal是其子类AbstractUrlHandlerMapping实现的方法,用来查询handlerMap查找request的url对应的handler。
@Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); 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 '" + handler + "'"); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }
下面来分析getHandlerInternal方法内部,第一句使用类中UrlPathHelper对象urlPathHelper调用getLookupPathForRequest方法解析request,从而得到请求的urlPath。
第二句调用lookupHandler方法,用于查找url path对应的handler,从上面的regist我们可以很快理解,这里肯定是在handlerMap中进行查找,它是AbstractUrlHandlerMapping实现的方法:
/** *Look up a handler instance for the given URL path. */ protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? Object handler = this.handlerMap.get(urlPath); 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; if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath)); 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); Map<String, String> uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(bestPatternMatch, urlPath); return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
果不其然,从上面的注释//直接匹配 我们可以看到该方法查找handlerMap中key为urlPath的handler value。
当查找到后,判断我们取出的handler是否是String对象,因为前面我们已经说明了lazyInit,所以这里不难理解。如果是String那么就构造bean,如果不是代码就继续向下走,请允许我先跳过buildPathExposingHandler方法,因为后面还会用到,在这里不是至关重要的,等下再说明它。
当直接匹配没有成功的时候,就进行//模式匹配,从handlerMap的keySet中查找到最符合urlPath模式的key,从而得到handler,如果没有找到,那就返回null。
回到getHandlerInternal方法中,当我们没有查找到urlPath对应的handler时,也就是返回handler==null,则进入第一个if条件中,首先要检查两种情况,因为我们在注册时将"/"set到了rootHandler中,而将"/*"set到了defaultHandler中,当从这两种情况中的一种得到handler后,也是判断是否为String,如果是则构造bean,然后调用buildPathExposingHandler方法。
细心的朋友会注意到getDefaultHandler方法在AbstractHandlerMapping的getHandler方法也被调用了一次,这是当getHandlerInternal返回null时调用的。
到这里我们就来介绍一下buildPathExposingHandler方法:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, Map<String, String> uriTemplateVariables) { HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler); chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping)); if (!CollectionUtils.isEmpty(uriTemplateVariables)) { chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables)); } return chain; }
该方法中,使用handler作为参数构造了一个HandlerExecutionChain的对象,不熟悉HandlerExecutionChain的朋友可以打开该类的源码,可以看到它保存了两个最重要的对象,一个handler和一组interceptor
private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList;
然后调用addInterceptor方法,为chain添加了一个HandlerInterceptor----PathExposingHandlerInterceptor
public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this.interceptorList.add(interceptor); }
private void initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } }
当uriTemplateVariables(这是模式匹配时传入buildPathExposingHandler的Map)不为空时,又添加一个HandlerInterceptor-----UriTemplateVariablesHandlerInterceptor
上述的两个interceptor都是AbstractUrlHandlerMapping类的内部类,这里我们先不讨论拦截器,等后面的文章会进行详细阅读。
后面直到getHandler方法完结,均是对handler和handlerinceptor的拼装,就先不介绍了。后面会着重阅读HandlerInceptor接口及其子类。