SpringMVC源码 HandlerMapping和HandlerAdapter(2)

来源:互联网 发布:java mina框架 编辑:程序博客网 时间:2024/04/25 19:44

上一节我们看到  当***-serlvet.xml中未声明HandlerMapping的实体bean时,默认构造

org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

的实例。

所以本节就从这两个类入手研究,HandlerMapping中最重要的方法是 getHandler 用于获取HandlerExcutionChain的对象。

先来看BeanNameUrlHandlerMapping类,它的内部没有实现getHandler方法,而它继承自AbstractDetectingUrlHandlerMapping,所以自底向上我们去查看AbstractDetectingUrlHandlerMapping类的实现。

在这个类中,我看到了一个方法

Java代码  收藏代码
  1. /** 
  2.      * Register all handlers found in the current ApplicationContext. 
  3.      * <p>The actual URL determination for a handler is up to the concrete 
  4.      * {@link #determineUrlsForHandler(String)} implementation. A bean for 
  5.      * which no such URLs could be determined is simply not considered a handler. 
  6.      * @throws org.springframework.beans.BeansException if the handler couldn't be registered 
  7.      * @see #determineUrlsForHandler(String) 
  8.      */  
  9.     protected void detectHandlers() throws BeansException {  
  10.         if (logger.isDebugEnabled()) {  
  11.             logger.debug("Looking for URL mappings in application context: " + getApplicationContext());  
  12.         }  
  13.         String[] beanNames = (this.detectHandlersInAncestorContexts ?  
  14.                 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :  
  15.                 getApplicationContext().getBeanNamesForType(Object.class));  
  16.   
  17.         // Take any bean name that we can determine URLs for.  
  18.         for (String beanName : beanNames) {  
  19.             String[] urls = determineUrlsForHandler(beanName);  
  20.             if (!ObjectUtils.isEmpty(urls)) {  
  21.                 // URL paths found: Let's consider it a handler.  
  22.                 registerHandler(urls, beanName);  
  23.             }  
  24.             else {  
  25.                 if (logger.isDebugEnabled()) {  
  26.                     logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");  
  27.                 }  
  28.             }  
  29.         }  
  30.     }  

 whether to detect handler beans in ancestor ApplicationContexts.

Java代码  收藏代码
  1. private boolean detectHandlersInAncestorContexts = false;  

 该方法首先查询了上下文中所有的bean,因为查询的Type是Object.class,得到了所有的bean名称,之后解析bean名称,查看是否存是urlName,determineUrlsForHandler方法在BeanNameUrlHandlerMapping类中具体实现:

Java代码  收藏代码
  1. /** 
  2.      * Checks name and aliases of the given bean for URLs, starting with "/". 
  3.      */  
  4.     @Override  
  5.     protected String[] determineUrlsForHandler(String beanName) {  
  6.         List<String> urls = new ArrayList<String>();  
  7.         if (beanName.startsWith("/")) {  
  8.             urls.add(beanName);  
  9.         }  
  10.         String[] aliases = getApplicationContext().getAliases(beanName);  
  11.         for (int i = 0; i < aliases.length; i++) {  
  12.             if (aliases[i].startsWith("/")) {  
  13.                 urls.add(aliases[i]);  
  14.             }  
  15.         }  
  16.         return StringUtils.toStringArray(urls);  
  17.     }  

 可以看出它查找了所有beanName以“/”开头的bean,将它们的name作为url返回。

接下来就对那些作为处理url请求的bean进行handler的注册,registerHandler方法在AbstractDetectingUrlHandlerMapping的父类AbstractUrlHandlerMapping类中:

Java代码  收藏代码
  1. /** 
  2.      * Register the specified handler for the given URL paths. 
  3.      * @param urlPaths the URLs that the bean should be mapped to 
  4.      * @param beanName the name of the handler bean 
  5.      * @throws BeansException if the handler couldn't be registered 
  6.      * @throws IllegalStateException if there is a conflicting handler registered 
  7.      */  
  8.     protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {  
  9.         Assert.notNull(urlPaths, "URL path array must not be null");  
  10.         for (String urlPath : urlPaths) {  
  11.             registerHandler(urlPath, beanName);  
  12.         }  
  13.     }  
 
Java代码  收藏代码
  1. /** 
  2.      * Register the specified handler for the given URL path. 
  3.      * @param urlPath the URL the bean should be mapped to 
  4.      * @param handler the handler instance or handler bean name String 
  5.      * (a bean name will automatically be resolved into the corresponding handler bean) 
  6.      * @throws BeansException if the handler couldn't be registered 
  7.      * @throws IllegalStateException if there is a conflicting handler registered 
  8.      */  
  9.     protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {  
  10.         Assert.notNull(urlPath, "URL path must not be null");  
  11.         Assert.notNull(handler, "Handler object must not be null");  
  12.         Object resolvedHandler = handler;  
  13.   
  14.         // Eagerly resolve handler if referencing singleton via name.  
  15.         if (!this.lazyInitHandlers && handler instanceof String) {  
  16.             String handlerName = (String) handler;  
  17.             if (getApplicationContext().isSingleton(handlerName)) {  
  18.                 resolvedHandler = getApplicationContext().getBean(handlerName);  
  19.             }  
  20.         }  
  21.   
  22.         Object mappedHandler = this.handlerMap.get(urlPath);  
  23.         if (mappedHandler != null) {  
  24.             if (mappedHandler != resolvedHandler) {  
  25.                 throw new IllegalStateException(  
  26.                         "Cannot map handler [" + handler + "] to URL path [" + urlPath +  
  27.                         "]: There is already handler [" + resolvedHandler + "] mapped.");  
  28.             }  
  29.         }  
  30.         else {  
  31.             if (urlPath.equals("/")) {  
  32.                 if (logger.isInfoEnabled()) {  
  33.                     logger.info("Root mapping to handler [" + resolvedHandler + "]");  
  34.                 }  
  35.                 setRootHandler(resolvedHandler);  
  36.             }  
  37.             else if (urlPath.equals("/*")) {  
  38.                 if (logger.isInfoEnabled()) {  
  39.                     logger.info("Default mapping to handler [" + resolvedHandler + "]");  
  40.                 }  
  41.                 setDefaultHandler(resolvedHandler);  
  42.             }  
  43.             else {  
  44.                 this.handlerMap.put(urlPath, resolvedHandler);  
  45.                 if (logger.isInfoEnabled()) {  
  46.                     logger.info("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");  
  47.                 }  
  48.             }  
  49.         }  
  50.     }  

 handlerMap就是一个K-V保存url对应的handler,初始时我们传入的Object handler参数是String对象beanNames,lazyInit表示是延迟初始化handler,如果设为true那么在注册handler的时候我们存入的handler还是String,如果是false,该方法中被用来取得bean实例,所以handlerMap中存放的V就是bean的对象,使用Object泛型是因为bean的不一致。

Java代码  收藏代码
  1. private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();  

 注意上面的setDefaultHandler方法,它是AbstractUrlHandlerMapping类的父类AbstractHandlerMapping中的方法,在AbstractHandlerMapping中有一个Object对象属性:

Java代码  收藏代码
  1. private Object defaultHandler;  

 从字面意思可以理解为默认的处理器,因为它对应的映射是"/*",即处理所有符合该表达式的url请求。

阅读完BeanNameUrlHandlerMapping和AbstractDetectingUrlHandlerMapping两个类后,我们发现它们中的方法主要进行了handler的set,而handler的get却没有在它们中体现,所以再向上走,我们查看最高的父类AbstractHandlerMapping类,它直接实现了HandlerMapping接口,所以剖析它的getHandler方法更容易让我们理解,handler获取的流程。

Java代码  收藏代码
  1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  2.         Object handler = getHandlerInternal(request);  
  3.         if (handler == null) {  
  4.             handler = getDefaultHandler();  
  5.         }  
  6.         if (handler == null) {  
  7.             return null;  
  8.         }  
  9.         // Bean name or resolved handler?  
  10.         if (handler instanceof String) {  
  11.             String handlerName = (String) handler;  
  12.             handler = getApplicationContext().getBean(handlerName);  
  13.         }  
  14.         return getHandlerExecutionChain(handler, request);  
  15.     }  

 该方法首先调用了getHandlerInternal处理request,getHandlerInternal是其子类AbstractUrlHandlerMapping实现的方法,用来查询handlerMap查找request的url对应的handler。

Java代码  收藏代码
  1. @Override  
  2.     protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  
  3.         String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  
  4.         Object handler = lookupHandler(lookupPath, request);  
  5.         if (handler == null) {  
  6.             // We need to care for the default handler directly, since we need to  
  7.             // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.  
  8.             Object rawHandler = null;  
  9.             if ("/".equals(lookupPath)) {  
  10.                 rawHandler = getRootHandler();  
  11.             }  
  12.             if (rawHandler == null) {  
  13.                 rawHandler = getDefaultHandler();  
  14.             }  
  15.             if (rawHandler != null) {  
  16.                 // Bean name or resolved handler?  
  17.                 if (rawHandler instanceof String) {  
  18.                     String handlerName = (String) rawHandler;  
  19.                     rawHandler = getApplicationContext().getBean(handlerName);  
  20.                 }  
  21.                 validateHandler(rawHandler, request);  
  22.                 handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);  
  23.             }  
  24.         }  
  25.         if (handler != null && this.mappedInterceptors != null) {  
  26.             Set<HandlerInterceptor> mappedInterceptors =  
  27.                     this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher);  
  28.             if (!mappedInterceptors.isEmpty()) {  
  29.                 HandlerExecutionChain chain;  
  30.                 if (handler instanceof HandlerExecutionChain) {  
  31.                     chain = (HandlerExecutionChain) handler;  
  32.                 } else {  
  33.                     chain = new HandlerExecutionChain(handler);  
  34.                 }  
  35.                 chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()]));  
  36.             }  
  37.         }  
  38.         if (handler != null && logger.isDebugEnabled()) {  
  39.             logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'");  
  40.         }  
  41.         else if (handler == null && logger.isTraceEnabled()) {  
  42.             logger.trace("No handler mapping found for [" + lookupPath + "]");  
  43.         }  
  44.         return handler;  
  45.     }  

 下面来分析getHandlerInternal方法内部,第一句使用类中UrlPathHelper对象urlPathHelper调用getLookupPathForRequest方法解析request,从而得到请求的urlPath。

第二句调用lookupHandler方法,用于查找url path对应的handler,从上面的regist我们可以很快理解,这里肯定是在handlerMap中进行查找,它是AbstractUrlHandlerMapping实现的方法:

Java代码  收藏代码
  1. /** 
  2.  *Look up a handler instance for the given URL path. 
  3.  */  
  4. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {  
  5.         // Direct match?  
  6.         Object handler = this.handlerMap.get(urlPath);  
  7.         if (handler != null) {  
  8.             // Bean name or resolved handler?  
  9.             if (handler instanceof String) {  
  10.                 String handlerName = (String) handler;  
  11.                 handler = getApplicationContext().getBean(handlerName);  
  12.             }  
  13.             validateHandler(handler, request);  
  14.             return buildPathExposingHandler(handler, urlPath, urlPath, null);  
  15.         }  
  16.         // Pattern match?  
  17.         List<String> matchingPatterns = new ArrayList<String>();  
  18.         for (String registeredPattern : this.handlerMap.keySet()) {  
  19.             if (getPathMatcher().match(registeredPattern, urlPath)) {  
  20.                 matchingPatterns.add(registeredPattern);  
  21.             }  
  22.         }  
  23.         String bestPatternMatch = null;  
  24.         if (!matchingPatterns.isEmpty()) {  
  25.             Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath));  
  26.             if (logger.isDebugEnabled()) {  
  27.                 logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);  
  28.             }  
  29.             bestPatternMatch = matchingPatterns.get(0);  
  30.         }  
  31.         if (bestPatternMatch != null) {  
  32.             handler = this.handlerMap.get(bestPatternMatch);  
  33.             // Bean name or resolved handler?  
  34.             if (handler instanceof String) {  
  35.                 String handlerName = (String) handler;  
  36.                 handler = getApplicationContext().getBean(handlerName);  
  37.             }  
  38.             validateHandler(handler, request);  
  39.             String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);  
  40.             Map<String, String> uriTemplateVariables =  
  41.                     getPathMatcher().extractUriTemplateVariables(bestPatternMatch, urlPath);  
  42.             return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);  
  43.         }  
  44.         // No handler found...  
  45.         return null;  
  46.     }  

  果不其然,从上面的注释//直接匹配  我们可以看到该方法查找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方法:

Java代码  收藏代码
  1. protected Object buildPathExposingHandler(Object rawHandler,  
  2.             String bestMatchingPattern,  
  3.             String pathWithinMapping,  
  4.             Map<String, String> uriTemplateVariables) {  
  5.   
  6.         HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);  
  7.         chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));  
  8.         if (!CollectionUtils.isEmpty(uriTemplateVariables)) {  
  9.             chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));  
  10.         }  
  11.         return chain;  
  12.     }  

  该方法中,使用handler作为参数构造了一个HandlerExecutionChain的对象,不熟悉HandlerExecutionChain的朋友可以打开该类的源码,可以看到它保存了两个最重要的对象,一个handler和一组interceptor

Java代码  收藏代码
  1. private final Object handler;  
  2.   
  3. private HandlerInterceptor[] interceptors;  
  4.   
  5. private List<HandlerInterceptor> interceptorList;  

 然后调用addInterceptor方法,为chain添加了一个HandlerInterceptor----PathExposingHandlerInterceptor

Java代码  收藏代码
  1. public void addInterceptor(HandlerInterceptor interceptor) {  
  2.         initInterceptorList();  
  3.         this.interceptorList.add(interceptor);  
  4.     }  
 
Java代码  收藏代码
  1. private void initInterceptorList() {  
  2.         if (this.interceptorList == null) {  
  3.             this.interceptorList = new ArrayList<HandlerInterceptor>();  
  4.         }  
  5.         if (this.interceptors != null) {  
  6.             this.interceptorList.addAll(Arrays.asList(this.interceptors));  
  7.             this.interceptors = null;  
  8.         }  
  9.     }  

 当uriTemplateVariables(这是模式匹配时传入buildPathExposingHandler的Map)不为空时,又添加一个HandlerInterceptor-----UriTemplateVariablesHandlerInterceptor

上述的两个interceptor都是AbstractUrlHandlerMapping类的内部类,这里我们先不讨论拦截器,等后面的文章会进行详细阅读。

 后面直到getHandler方法完结,均是对handler和handlerinceptor的拼装,就先不介绍了。后面会着重阅读HandlerInceptor接口及其子类。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 九死, pythen3.7 wheel 数据分析 二值图像 二值图像连通 ajax时间 ajax获取时间传到后台 ajax获取服务器时间传到后台 submit提交时怎么将提交的时间传入后台 gethostbyname 分节符快捷键 ErrorPage! Reason:*Networkbusy*InvalidURL*Failedto install_flash_player_10_active_x 伊织 伊织调教 起重机司机学习 ssni 输入梯形上底下底高,输出面积 python语言,输入梯形上底下底高输出面积 网易产品 https://www.csdndoc.com/blog/390696 https://www.cocoacontrols.com 风之教堂 黄色网站 AV网站 439973416 فیلترشکن WS_CLIPCHILDREN设置键在哪里 数据结构(严蔚敏李冬梅)课后答案 ipz809 一个字包含的二进制位数 linux面试 【题目2】设计一个结构体类型,包含姓名、出生日期。其中出生日期又包含年、月、日三部分信息。输入n个好 【题目2】设计一个结构体类型,包含姓名、出生日期。其中出生日期又包含年、月、日三部分信息。输入n个好 结构体比较好友中年纪最小的 matlab三维矩阵可视化 使QQ崩溃 scanIP 按照规定,在高速公路上行使的机动车,达到或超出本车道限速的10%则处200元罚款;若达到或超出50% c语言输入三个数字