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中:
有意思的是这个map的key值是Method,value是URL对应的映射的信息。
另外HandlerMethods这个成员变量是一个链表的样式:
这样就能够看到为什么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());
}
}