SpringMVC 中找到响应URL的具体的方法


SpringMVC 找到响应URL请求的方法

向之所欣,俯仰之间,已为陈迹,犹不能不以之兴怀
疲惫期,慢慢在堕落

URL请求到: org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse) 的时候,得到handler的时候:

// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

针对于Handler中定义的Controller的类信息的解析,最终生成一个:ServletHandlerMethodResolver 实例,其初始化的逻辑是:

public void init(final Class<?> handlerType) {
		Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
		Class<?> specificHandlerType = null;
		if (!Proxy.isProxyClass(handlerType)) {
			handlerTypes.add(handlerType);
			specificHandlerType = handlerType;
		}
		handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
				public void doWith(Method method) {
					Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
					if (isHandlerMethod(specificMethod) &&
							(bridgedMethod == specificMethod || !isHandlerMethod(bridgedMethod))) {
							//关键就是这句,把controller实现类的继承类,以及自身的响应的URL的方法全部的放到handlerMethods中①
						handlerMethods.add(specificMethod);
					}
					else if (isInitBinderMethod(specificMethod) &&
							(bridgedMethod == specificMethod || !isInitBinderMethod(bridgedMethod))) {
						initBinderMethods.add(specificMethod);
					}
					else if (isModelAttributeMethod(specificMethod) &&
							(bridgedMethod == specificMethod || !isModelAttributeMethod(bridgedMethod))) {
						modelAttributeMethods.add(specificMethod);
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}
		this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
		SessionAttributes sessionAttributes = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
		this.sessionAttributesFound = (sessionAttributes != null);
		if (this.sessionAttributesFound) {
			this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value()));
			this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));
		}
	}

然后就是执行:

try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}

最主要的一个方法:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//① 根据handler得到ServletHandlerMethodResolver,其中保存了handler的所有的URL请求的响应的
		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		//②找到针对特定URL的方法
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();
		//执行方法
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

第一步的得到controller的方法的信息,这个是利用了缓存,最初的时候是解析controller类,把能够支持URL的请求的全部的找出来,放到一个Map中:

controller的方法

有意思的是这个map的key值是Method,value是URL对应的映射的信息。

另外HandlerMethods这个成员变量是一个链表的样式:

controller的handler

这样就能够看到为什么Map的key值是Method了?

第二步的具体的代码:

		public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
			String lookupPath = urlPathHelper.getLookupPathForRequest(request);
			Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
			Map<RequestSpecificMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestSpecificMappingInfo, Method>();
			Set<String> allowedMethods = new LinkedHashSet<String>(7);
			String resolvedMethodName = null;
			//得到controller的所有的处理url的方法
			for (Method handlerMethod : getHandlerMethods()) {
				//得到对应的映射的信息
				RequestSpecificMappingInfo mappingInfo = new RequestSpecificMappingInfo(this.mappings.get(handlerMethod));
				boolean match = false;
				if (mappingInfo.hasPatterns()) {
					for (String pattern : mappingInfo.getPatterns()) {
						if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
							pattern = "/" + pattern;
						}
						//找到全路径的URL
						String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
						if (combinedPattern != null) {
							if (mappingInfo.matches(request)) {
								match = true;
								mappingInfo.addMatchedPattern(combinedPattern);
							}
							else {
								if (!mappingInfo.matchesRequestMethod(request)) {
									allowedMethods.addAll(mappingInfo.methodNames());
								}
								break;
							}
						}
					}
					mappingInfo.sortMatchedPatterns(pathComparator);
				}
				else if (useTypeLevelMapping(request)) {
					String[] typeLevelPatterns = getTypeLevelMapping().value();
					for (String typeLevelPattern : typeLevelPatterns) {
						if (!typeLevelPattern.startsWith("/")) {
							typeLevelPattern = "/" + typeLevelPattern;
						}
						boolean useSuffixPattern = useSuffixPattern(request);
						//匹配URL的方法⑤
						if (getMatchingPattern(typeLevelPattern, lookupPath, useSuffixPattern) != null) {
							if (mappingInfo.matches(request)) {
								match = true;
								mappingInfo.addMatchedPattern(typeLevelPattern);
							}
							else {
								if (!mappingInfo.matchesRequestMethod(request)) {
									allowedMethods.addAll(mappingInfo.methodNames());
								}
								break;
							}
						}
					}
					mappingInfo.sortMatchedPatterns(pathComparator);
				}
				else {
					// No paths specified: parameter match sufficient.
					match = mappingInfo.matches(request);
					if (match && mappingInfo.getMethodCount() == 0 && mappingInfo.getParamCount() == 0 &&
							resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
						match = false;
					}
					else {
						if (!mappingInfo.matchesRequestMethod(request)) {
							allowedMethods.addAll(mappingInfo.methodNames());
						}
					}
				}
				if (match) {
					Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
					if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
						if (methodNameResolver != null && !mappingInfo.hasPatterns()) {
							if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
								if (resolvedMethodName == null) {
									resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
								}
								if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
									oldMappedMethod = null;
								}
								if (!resolvedMethodName.equals(handlerMethod.getName())) {
									if (oldMappedMethod != null) {
										targetHandlerMethods.put(mappingInfo, oldMappedMethod);
										oldMappedMethod = null;
									}
									else {
										targetHandlerMethods.remove(mappingInfo);
									}
								}
							}
						}
						if (oldMappedMethod != null) {
							throw new IllegalStateException(
									"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
									oldMappedMethod + ", " + handlerMethod +
									"}. If you intend to handle the same path in multiple methods, then factor " +
									"them out into a dedicated handler class with that path mapped at the type level!");
						}
					}
				}
			}
			if (!targetHandlerMethods.isEmpty()) {
				List<RequestSpecificMappingInfo> matches = new ArrayList<RequestSpecificMappingInfo>(targetHandlerMethods.keySet());
				RequestSpecificMappingInfoComparator requestMappingInfoComparator =
						new RequestSpecificMappingInfoComparator(pathComparator, request);
				Collections.sort(matches, requestMappingInfoComparator);
				RequestSpecificMappingInfo bestMappingMatch = matches.get(0);
				String bestMatchedPath = bestMappingMatch.bestMatchedPattern();
				if (bestMatchedPath != null) {
					extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
				}
				return targetHandlerMethods.get(bestMappingMatch);
			}
			else {
				if (!allowedMethods.isEmpty()) {
					throw new HttpRequestMethodNotSupportedException(request.getMethod(), StringUtils.toStringArray(allowedMethods));
				}
				throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap());
			}
		}


上一篇  一周学习总结 下一篇   从自己所做的工程开始梳理自己技术栈