SpringMVC在@RequestMapping配置兩個相同路徑


  這篇博客來自這個問題: 在SpringMVC中@RequestMapping可以配置兩個相同的url路徑嗎。

  首先,這個問題會點SpringMVC的人可能都知道答案,但是上次面試中我就回答了可以。。。可以。。Spicy Chicken!!!

 

  參考文章: http://lgbolgger.iteye.com/blog/2105108

  

  這個問題要從 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 講起了。 

  首先,在配置文件中聲明了 <mvc:annotation-driven /> 注解之后, 在 initStrategies() 方法中注冊了處理的類:

 1 protected void initStrategies(ApplicationContext context) {  
 2         initMultipartResolver(context);  
 3         initLocaleResolver(context);  
 4         initThemeResolver(context);  
 5  initHandlerMappings(context);  6         initHandlerAdapters(context);  
 7         initHandlerExceptionResolvers(context);  
 8         initRequestToViewNameTranslator(context);  
 9         initViewResolvers(context);  
10         initFlashMapManager(context);  
11     }  

  容器對 @ReqeustMapping 便簽的處理的簡化流程就是首先 RequestMappingHandlerMapping 類去查找有 @Controller 或 @RequestMapping 的類, 然后為含有其中一個注解的類還有類中含有 @RequestMapping 注解的方法構建 HandlerMethod 對象, 之后 RequestMappingHandlerAdapter 判斷是否 support 對應的方法並執行對應的方法。

  具體的過程如下:

  1, RequestMappingHandlerMapping 遍歷所有的 bean, 判斷是否有 @Controller 或 @RequestMapping 注解

 1     protected void initHandlerMethods() {  
 2         if (logger.isDebugEnabled()) {  
 3             logger.debug("Looking for request mappings in application context: " + getApplicationContext());  
 4         }  
 5   
 6         String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?  
 7                 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :  
 8                 getApplicationContext().getBeanNamesForType(Object.class));  
 9   
10         for (String beanName : beanNames) {  
11             if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&  
12  isHandler(getApplicationContext().getType(beanName))){  
13                 detectHandlerMethods(beanName);  
14             }  
15         }  
16         handlerMethodsInitialized(getHandlerMethods());  
17     }  

  isHandler() 判斷的方法:

1     protected boolean isHandler(Class<?> beanType) {  
2         return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||  
3                 (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));  
4     }  

  注意方法中的判斷是用 || 邏輯,說明兩者當中的其中一個符合即可。

  2,處理含有 @RequestMapping 的方法,遍歷第一步中找到的類中的所有方法,用一個 MethodFilter 查找所有的 @ReqeustMapping 注解的方法,並為找到的 @RequestMapping 的方法構建 ReqeustMappingInfo 對象

 1 /** 
 2      * Look for handler methods in a handler. 
 3      * @param handler the bean name of a handler or a handler instance 
 4      */  
 5     protected void detectHandlerMethods(final Object handler) {  
 6         Class<?> handlerType =  
 7                 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());  
 8   
 9         // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances  
10         final Map<Method, T> mappings = new IdentityHashMap<Method, T>();  
11         final Class<?> userType = ClassUtils.getUserClass(handlerType);  
12   
13         Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {  
14             @Override  
15             public boolean matches(Method method) {  
16                 T mapping = getMappingForMethod(method, userType);  
17                 if (mapping != null) {  
18                     mappings.put(method, mapping);  
19                     return true;  
20                 }  
21                 else {  
22                     return false;  
23                 }  
24             }  
25         });  
26   
27         for (Method method : methods) {  
28             registerHandlerMethod(handler, method, mappings.get(method));  
29         }  
30     }  

  其中查找 @RequestMapping 注解的方法的 getMappingForMethod() 方法:

 1 /** 
 2      * Uses method and type-level @{@link RequestMapping} annotations to create 
 3      * the RequestMappingInfo. 
 4      * @return the created RequestMappingInfo, or {@code null} if the method 
 5      * does not have a {@code @RequestMapping} annotation. 
 6      * @see #getCustomMethodCondition(Method) 
 7      * @see #getCustomTypeCondition(Class) 
 8      */  
 9     @Override  
10     protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {  
11         RequestMappingInfo info = null;  
12         RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);  
13         if (methodAnnotation != null) {  
14             RequestCondition<?> methodCondition = getCustomMethodCondition(method);  
15             info = createRequestMappingInfo(methodAnnotation, methodCondition);  
16             RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);  
17             if (typeAnnotation != null) {  
18                 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);  
19                 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);  
20             }  
21         }  
22         return info;  
23     }  

  構建 RequestMappingInfo 對象的 createRequestMappingInfo() 方法:

 1 /** 
 2      * Created a RequestMappingInfo from a RequestMapping annotation. 
 3      */  
 4     protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {  
 5         String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());  
 6         return new RequestMappingInfo(  
 7                 new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),  
 8                         this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),  
 9                 new RequestMethodsRequestCondition(annotation.method()),  
10                 new ParamsRequestCondition(annotation.params()),  
11                 new HeadersRequestCondition(annotation.headers()),  
12                 new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),  
13                 new ProducesRequestCondition(annotation.produces(), annotation.headers(), this.contentNegotiationManager),  
14                 customCondition);  
15     }  

  3,構建完 RequestMappingInfo 對象后, 存儲到 Map 類型的 handlerMethods 對象中

 1 /** 
 2      * Register a handler method and its unique mapping. 
 3      * @param handler the bean name of the handler or the handler instance 
 4      * @param method the method to register 
 5      * @param mapping the mapping conditions associated with the handler method 
 6      * @throws IllegalStateException if another method was already registered 
 7      * under the same mapping 
 8      */  
 9     protected void registerHandlerMethod(Object handler, Method method, T mapping) {  
10         HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);  
11         HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);  
12         if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {  
13             throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +  
14                     "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +  
15                     oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");  
16         }  
17   
18         this.handlerMethods.put(mapping, newHandlerMethod);  
19         if (logger.isInfoEnabled()) {  
20             logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);  
21         }  
22   
23         Set<String> patterns = getMappingPathPatterns(mapping);  
24         for (String pattern : patterns) {  
25             if (!getPathMatcher().isPattern(pattern)) {  
26                 this.urlMap.add(pattern, mapping);  
27             }  
28         }  
29     }  

   ***在這個地方, 給出了文章開頭的問題的答案, 如果存在 @RequestMapping 注解有相同的訪問路徑時,會在此處拋出異常 (注釋很重要)*************

  4, 構建完了  private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap(); 這個對象之后,該對象就包含了所有的它應該包含的 bean 了,它的key為RequestMappingInfo對象,value為handler和它中含有@RequestMapping注釋的方法method構建的HandlerMethod。

  5, url 匹配路徑時, RequestMappingHandlerAdapter 依據為是否是HandlerMethod 類型判斷是否執行該方法。(這點我不太懂)

1 public final boolean supports(Object handler) {  
2         return handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler);  
3     }  

 


  最后,針對文章開頭的問題, 對第三步步驟截圖做一個簡單的示例。

  聲明兩個相同路徑的 @RequestMapping 注解並查看錯誤:

  

  查看錯誤:

  

  正好是步驟三中拋出異常的位置,問題結束!!

 


  一直用 IntelliJ 查看 Spring 的源碼, 今天用 Maven 工程,查看源碼的時候直接可以打開源碼的文件,不用再 attach Source 了,而且發現 eclipse 里面的源碼還帶有英文的注釋, IntelliJ 反編譯后的源碼文件都不帶注釋的, 以后的源碼查看可能要改改了, 算是新發現吧。這個問題就到這了( ̄▽ ̄)~*

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM