protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } //獲取applicationContext中全部的bean name String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); //遍歷beanName數組 for (String beanName : beanNames) { //isHandler會根據bean來判斷bean定義中是否帶有Controller註解或RequestMapping註解 if (isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
@Override protected boolean isHandler(Class<?> beanType) { return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) || (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null)); }
protected void detectHandlerMethods(final Object handler) { //獲取到當前Controller bean的class對象 Class<?> handlerType = (handler instanceof String) ? getApplicationContext().getType((String) handler) : handler.getClass(); //同上,也是該Controller bean的class對象 final Class<?> userType = ClassUtils.getUserClass(handlerType); //獲取當前bean的全部handler method。這裏查找的依據即是根據method定義是否帶有RequestMapping註解。若是有根據註解建立RequestMappingInfo對象 Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { public boolean matches(Method method) { return getMappingForMethod(method, userType) != null; } }); //遍歷並註冊當前bean的全部handler method for (Method method : methods) { T mapping = getMappingForMethod(method, userType); //註冊handler method,進入如下方法 registerHandlerMethod(handler, method, mapping); } }
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = null; //獲取method的@RequestMapping註解 RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (methodAnnotation != null) { RequestCondition<?> methodCondition = getCustomMethodCondition(method); info = createRequestMappingInfo(methodAnnotation, methodCondition); //獲取method所屬bean的@RequtestMapping註解 RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); if (typeAnnotation != null) { RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType); //合併兩個@RequestMapping註解 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); } } return info; }
protected void registerHandlerMethod(Object handler, Method method, T mapping) { //建立HandlerMethod HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); HandlerMethod oldHandlerMethod = handlerMethods.get(mapping); //檢查配置是否存在歧義性 if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped."); } this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod); } //獲取@RequestMapping註解的value,而後添加value->RequestMappingInfo映射記錄至urlMap中 Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping); } } }
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
@Controller @RequestMapping("/AmbiguousTest") public class AmbiguousTestController { @RequestMapping(value = "/test1") @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1") @ResponseBody public String test2(){ return "method test2"; } }
該方法的主要有3個職責html
@Controller @RequestMapping("/UrlMap") public class UrlMapController { @RequestMapping(value = "/test1", method = RequestMethod.GET) @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1") @ResponseBody public String test2(){ return "method test2"; } @RequestMapping(value = "/test3") @ResponseBody public String test3(){ return "method test3"; } }
以上即是SpringMVC初始化的主要過程java
@Controller @RequestMapping("/LookupTest") public class LookupTestController { @RequestMapping(value = "/test1", method = RequestMethod.GET) @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1", headers = "Referer=https://www.baidu.com") @ResponseBody public String test2(){ return "method test2"; } @RequestMapping(value = "/test1", params = "id=1") @ResponseBody public String test3(){ return "method test3"; } @RequestMapping(value = "/*") @ResponseBody public String test4(){ return "method test4"; } }
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); //根據uri獲取直接匹配的RequestMappingInfos List<T> directPathMatches = this.urlMap.get(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } //不存在直接匹配的RequetMappingInfo,遍歷全部RequestMappingInfo if (matches.isEmpty()) { // No choice but to go through all mappings addMatchingMappings(this.handlerMethods.keySet(), matches, request); } //獲取最佳匹配的RequestMappingInfo對應的HandlerMethod 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); } }
進入lookupHandlerMethod方法,其中lookupPath="/LookupTest/test1",根據lookupPath,也就是請求的uri。直接查找urlMap,獲取直接匹配的RequestMappingInfo list。這裏會匹配到3個RequestMappingInfo。以下
git
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, handlerMethods.get(mapping))); } } }
public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result = patternsCondition.compareTo(other.getPatternsCondition(), request); if (result != 0) { return result; } result = paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } result = headersCondition.compareTo(other.getHeadersCondition(), request); if (result != 0) { return result; } result = consumesCondition.compareTo(other.getConsumesCondition(), request); if (result != 0) { return result; } result = producesCondition.compareTo(other.getProducesCondition(), request); if (result != 0) { return result; } result = methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } result = customConditionHolder.compareTo(other.customConditionHolder, request); if (result != 0) { return result; } return 0; }
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public String test5(){ return "method test5"; } @RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.DELETE}) @ResponseBody public String test6(){ return "method test6"; }
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5'
異常。這裏拋該異常是由於RequestMethodsRequestCondition的compareTo方法是比較的method數。代碼以下public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) { return other.methods.size() - this.methods.size(); }
版權聲明
做者:wycm
出處:https://www.cnblogs.com/w-y-c-m/p/8416630.html
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸做者全部,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
程序員