SpringMVC 源码解析 - Controller 的扫描注册及匹配过程

一、SpringMVC Handler 扫描注册过程

在本专栏前面文章中对 SpringMVC 请求执行过程进行了分析,文章地址如下:

SpringMVC 源码解析 - 请求执行的过程

其中有个重要的组件 HandlerMapping 在源码中起到了非常重要的位置,这里的 Handler 也就是我们常见的 Controller,那 Controller 是如何被识别注册以及查找,本篇文章带领大家一起从源码的角度分析下。

二、Handler 扫描注册过程

在 HandlerMapping 子类中有个RequestMappingHandlerMapping ,其作用是在容器启动后将系统中所有控制器方法的请求条件和控制器方法的对应关系注册到 RequestMappingHandlerMapping 子类 MappingRegistry 的容器中。当有请求进来时,RequestMappingHandlerMapping 会根据请求条件和容器中存储的系统接口信息比对,再执行对应的控制器方法,从而帮助 DispatcherServlet 分发请求。

下面是 RequestMappingHandlerMapping 继承树关系:

在这里插入图片描述

从树中可以看到 RequestMappingHandlerMapping 有实现 InitializingBean 因此在bean加载完成后会触发afterPropertiesSet方法:

在这里插入图片描述
在该方法下做了 config 配置的初始化,主要逻辑则交由父类的 afterPropertiesSet() 方法进行实现。

下面进到 AbstractHandlerMethodMapping 下的 afterPropertiesSet() 方法中:

在这里插入图片描述
这里触发了 initHandlerMethods() 方法,从这里开始核心的逻辑,下面是该方法的源码:

protected void initHandlerMethods() {
    //获取候选的bean名称数组
    for (String beanName : getCandidateBeanNames()) {
        //用于过滤掉以"scopedTarget."开头的bean名称的。
        //这些bean名称是由Spring创建的代理对象,用于支持不同作用域的bean
        //例如session或request。这些代理对象不是真正的handler,所以要排除掉。
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            //处理候选的bean,判断候选的bean是否是一个handler,也就是是否有@Controller注解或者@RequestMapping注解
            //如果是的话,就调用detectHandlerMethods方法,用于检测和注册handler方法
            processCandidateBean(beanName);
        }
    }
    //初始化handler方法, 对所有handler方法进行排序和日志输出
    handlerMethodsInitialized(getHandlerMethods());
}

在 initHandlerMethods() 中首先排除掉以 scopedTarget开头的类,因为这些是 Spring 创建的代理对象,下面主要逻辑会交由 processCandidateBean(String beanName) 方法:

protected void processCandidateBean(String beanName) {
      Class<?> beanType = null;
      try {
          // 从IOC工厂中获取当前 beanName 的 Class
          beanType = obtainApplicationContext().getType(beanName);
      } catch (Throwable ex) {
          // An unresolvable bean type, probably from a lazy bean - let's ignore it.
          if (logger.isTraceEnabled()) {
              logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
          }
      }
      // 如果 beanType 不为空,并且是 handler
      if (beanType != null && isHandler(beanType)) {
          //检测和注册handler方法
          detectHandlerMethods(beanName);
      }
  }

这里会根据 beanName 从 IOC工厂中获取到 Class 类型,下面使用 isHandler(Class<?> beanType) 方法判断是否为 handler ,其实就是判断类上是否带有 Controller 注解 或者 RequestMapping 注解,源码如下:

protected boolean isHandler(Class<?> beanType) {
	// 类上是否带有 Controller 注解 或者 RequestMapping 注解
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

如果是 handler 则触发 detectHandlerMethods(Object handler) 方法,进行检测和注册 handler

protected void detectHandlerMethods(Object handler) {
    //如果handler是一个字符串,那就从IOC工厂中获取到对应的Class,否则就直接使用 handler.getClass()方法来获取类类型。
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());
    // 如果handler类型不为空
    if (handlerType != null) {
        // 去除代理和增强后的原始类类型,主要为了避免AOP对方法检测造成干扰。
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 存储userType中所有带有映射注解(比如@RequestMapping) 的方法及其对应的映射信息
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        //获取每个方法上定义或继承的映射信息
                        return getMappingForMethod(method, userType);
                    } catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        if (logger.isTraceEnabled()) {
            logger.trace(formatMappings(userType, methods));
        }
        //遍历methods中,每个方法及其映射信息)
        methods.forEach((method, mapping) -> {
            //存储经过 AopUtils.selectInvocableMethod() 方法处理后可以被调用(即没有被final修饰)
            //且与 userType 匹配(即没有被覆盖) 的原始或桥接(即泛型擦除后生成) 方法
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            //注册到处理器映射
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

从上个方法可以看出,该方法接收的 handler 就是 beanName ,因此第一步会再去工厂中获取 Class 类型,下面看下 getMappingForMethod(Method method, Class<?> handlerType) 方法,主要实现了对注解中的参数信息进行提取:

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	//获取方法上定义或继承的@RequestMapping注解,并将其转换为一个RequestMappingInfo对象
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		//获取类上定义或继承的 @RequestMapping 注解,并将其转换为一个RequestMappingInfo对象
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			//合并两个RequestMappingInfo对象
			info = typeInfo.combine(info);
		}
		//获取处理器类上定义或继承的 @PathPrefix 注解,并将其转换为一个字符串前缀
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			//根据指定的路径前缀和配置选项来构建一个新的请求映射信息,并且与原有信息进行合并
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

这里分别对方法上和类上的注解的参数进行了提取,将参数信息封装为 RequestMappingInfo 进行返回,其中方法上和类上的属性解析都由 createRequestMappingInfo 方法完成:

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	// 获取 RequestMapping 注解
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

这里获取到 RequestMapping 注解信息后,交由 createRequestMappingInfo 方法封装成一个 RequestMappingInfo 对象:

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
	// 解析 RequestMapping 注解中的属性,并生成一个 RequestMappingInfo 对象
	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

在 createRequestMappingInfo 方法中就比较明了了,分别取出 RequestMapping 中的不同属性值,填充至 RequestMappingInfo 对象中。

下面再回到detectHandlerMethods(Object handler) 方法中,会通过 MethodIntrospector.selectMethods 方法将 RequestMappingInfo封装成一个 key 为 Method ,value 为 RequestMappingInfo 对象的 Map :

在这里插入图片描述
在 detectHandlerMethods(Object handler) 方法中最后会遍历上面生成的 Map ,触发 registerHandlerMethod(Object handler, Method method, T mapping) 方法进行 handler 的注册工作:

在这里插入图片描述
在 RequestMappingHandlerMapping 中的 registerHandlerMethod 方法同样调用了父类的 registerHandlerMethod 方法,这里来到 AbstractHandlerMethodMapping 类下的 registerHandlerMethod 中:

在这里插入图片描述

又调用了mappingRegistry.register 方法,这里则是注册 handler 的主要逻辑,源码如下:

public void register(T mapping, Object handler, Method method) {
    // Assert that the handler method is not a suspending one.
    if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
        Class<?>[] types = method.getParameterTypes();
        if ((types.length > 0) && "kotlin.coroutines.Continuation".equals(types[types.length - 1].getName())) {
            throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
        }
    }
    this.readWriteLock.writeLock().lock();
    try {
        // 创建 HandlerMethod 对象
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        validateMethodMapping(handlerMethod, mapping);

        //获取匹配条件对应的直接路径,添加到pathLookup中
        Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
        // 更新 pathLookup 查找表
        for (String path : directPaths) {
            this.pathLookup.add(path, mapping);
        }

        // 如果有命名策略,获取 handler 方法的名称,添加到nameLookup中
        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            // 注册新的 HandlerMethod
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
        if (config != null) {
            config.validateAllowCredentials();
            this.corsLookup.put(handlerMethod, config);
        }

        // 将匹配条件和MappingRegistration对象(封装了handler方法、直接路径、名称、跨域配置等信息)添加到registry中
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
    } finally {
        this.readWriteLock.writeLock().unlock();
    }
}

这里首先通过 createHandlerMethod 方法创建了一个 HandlerMethod 对象,源码如下:

protected HandlerMethod createHandlerMethod(Object handler, Method method) {
    if (handler instanceof String) {
        return new HandlerMethod((String) handler,
                obtainApplicationContext().getAutowireCapableBeanFactory(), method);
    }
    return new HandlerMethod(handler, method);
}

这里的 handler 参数就是前面方法一直传递过来的,因此还是 beanName ,所以会进入到第一个判断中,将 beanNameIOC工厂、Method 放入 HandlerMethod 中。

其中 beanName 给了 HandlerMethod 中 bean 属性:

在这里插入图片描述

再回到 mappingRegistry.register 方法中,创建完 HandlerMethod 后,将 url 的路径和前面生成的 RequestMappingInfo 对象建立映射关系放入 pathLookup 容器中,在最后又将 RequestMappingInfo 和 MappingRegistration 对象实例建立映射关系放入 registry 中缓存:

在这里插入图片描述
到这就完成了 Handler 的扫描和注册过程。

三、DispatcherServlet 下获取 Handler

DispatcherServlet 下获取 Handler 在前面 SpringMVC 源码梳理的文章中就分析过,但那次分析的确实不怎么详细,因此这里再继续梳理下,看是如何和上面注册的过程形成呼应的。

DispatcherServlet下的 getHandler 中会循环所有的 HandlerMapping 类,使用 mapping.getHandler 方法尝试获取一个 HandlerExecutionChain 对象,如果有则提前终止,HandlerExecutionChain 对象主要就是对 HandlerMethod 和 拦截器进行再次包装。

在这里插入图片描述

这里 mapping.getHandler 方法我们看到 AbstractHandlerMapping 下的 getHandler 方法中:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 模板方法,留给子类实现
	Object handler = getHandlerInternal(request);
	if (handler == null) {
		//handler为空获取默认的处理器 默认的 handler 也为 null
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// handler 如果是字符串对象,则通过 Spring 容器尝试获取 Handler 实例
	if (handler instanceof String) {
		String handlerName = (String) handler;
		// 获取 bean 实例
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//获取 HandlerExecutionChain 对象 ,包含 handler 和对应的 Interceptor
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}
	//跨域处理
	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		// cors 的检查请求
		CorsConfiguration config = getCorsConfiguration(handler, request);
		if (getCorsConfigurationSource() != null) {
			CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
			config = (globalConfig != null ? globalConfig.combine(config) : config);
		}
		if (config != null) {
			config.validateAllowCredentials();
		}
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

在这里首先使用 getHandlerInternal(HttpServletRequest request) 方法获取一个 handler,其实就是 HandlerMethod 对象,进到该方法中:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取请求路径
    String lookupPath = initLookupPath(request);
    // 映射存储器,尝试获取读锁
    this.mappingRegistry.acquireReadLock();
    try {
        // 通过路径和请求找到 HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        // 如果找到则创建一个新的 HandlerMethod , 主要将String类型的Bean更改为容器中实际的bean
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    } finally {
        // 放行锁
        this.mappingRegistry.releaseReadLock();
    }
}

这里首先拿到请求的路径,主要是去掉上下文路径和后缀名的URL路径,接着尝试获取映射存储器的读锁,主要防止有写的情况造成线程安全问题,拿到锁后通过 lookupHandlerMethod 方法匹配一个合适的 HandlerMethod,下面看到 lookupHandlerMethod 方法中:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 根据 loopuPath 获取对应 RequestInfoMapping 对象集合
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        // 将找到的 RequestInfoMapping 添加到容器缓存
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // 如果没找到,则将所有匹配条件的添加到容器缓存
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    if (!matches.isEmpty()) {
        // 获取容器中的第一个
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            // 数量大于1的话进行排序后取第一个 HandlerMethod 包装类,请求条件数量多的优先
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            bestMatch = matches.get(0);
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            // 取第二个最优匹配
            Match secondBestMatch = matches.get(1);
            // 前两个最优匹配一致,则会抛出异常
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        //设置 HandlerMethod 的 request 属性
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        // 路径变量处理
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    } else {
        return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
}

这里首先根据 lookupPath 通过 mappingRegistry.getMappingsByDirectPath 获取到一个 List 集合,进到mappingRegistry.getMappingsByDirectPath方法中就会发现就是从上面注册过程中的 pathLookup 容器中获取信息,这里List也就是 RequestInfoMapping 对象集合:

在这里插入图片描述

下面获取到 RequestInfoMapping 对象集合后,继续向下使用 addMatchingMappings 方法匹配出 Match 对象放入 matches 集合中,这里Match 对象其实就是 HandlerMethod的一个包装类,进入到 addMatchingMappings 方法中:

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,
                    this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
        }
    }
}

在这里遍历了所有的 RequestInfoMapping 通过 getMatchingMapping 方法获取一个匹配的 RequestInfoMapping 对象,如果存在就生成一个 Match 包装对象放入前面的 matches 集合中,下面继续看 getMatchingMapping 方法,该类在 RequestMappingInfoHandlerMapping 类中:

在这里插入图片描述
这里直接使用的 RequestInfoMapping 类的 getMatchingCondition 方法,在进到 info.getMatchingCondition 方法中:

public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
	// 方法匹配
	RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
	if (methods == null) {
		return null;
	}
	//请求参数条件匹配
	ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
	if (params == null) {
		return null;
	}
	//请求头匹配
	HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
	if (headers == null) {
		return null;
	}
	//可消费 MIME 匹配条件
	ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
	if (consumes == null) {
		return null;
	}
	//可生成 MIME 匹配条件
	ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
	if (produces == null) {
		return null;
	}
	PathPatternsRequestCondition pathPatterns = null;
	//路径匹配,即 url
	if (this.pathPatternsCondition != null) {
		pathPatterns = this.pathPatternsCondition.getMatchingCondition(request);
		if (pathPatterns == null) {
			return null;
		}
	}
	PatternsRequestCondition patterns = null;
	if (this.patternsCondition != null) {
		patterns = this.patternsCondition.getMatchingCondition(request);
		if (patterns == null) {
			return null;
		}
	}
	RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
	if (custom == null) {
		return null;
	}
	return new RequestMappingInfo(
			this.name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom);
}

这里经过不同维度的 Condition 进行匹配,如果符合条件的话才认为这个 RequestInfoMapping 匹配成功,最后生成了一个新的 RequestMappingInfo 对象装入了 Condition 后的不同维度的信息。

下面再回到 addMatchingMappings 方法中,上面提到如果匹配到则创建一个 Match 包装对像,源码如下:

在这里插入图片描述
前面在注册的过程使用 RequestInfoMapping 和 HandlerMethod 组成映射放入了registry 容器中,这里拿到 RequestInfoMapping 后,就可以通过registry 容器获取到 HandlerMethod 了。

再回到 lookupHandlerMethod 中,addMatchingMappings方法匹配到到 RequestInfoMapping也拿到了 HandlerMethod ,正常情况下我们都是一个 url 对应一个接口,如果这里匹配出多个并且一致的话,则会抛出异常,否则最后返回匹配出来的 HandlerMethod :

在这里插入图片描述
下面再回到 getHandlerInternal 方法中,这里拿到 HandlerMethod 后,没有直接选择返回,而是使用 handlerMethod.createWithResolvedBean() 生成了一个新的 HandlerMethod

在这里插入图片描述
这个原因从上面注册过程就可以看出,缓存下的 HandlerMethod 里的 bean 属性,其实还只是 beanName ,我们要通过反射执行方法的话,是需要目标对象的,因此在 createWithResolvedBean 方法中尝试从 IOC 容器中获取出实例:

在这里插入图片描述
下面再回到 AbstractHandlerMapping 类的 getHandler 方法,第一步通过 getHandlerInternal 方法拿到了一个已经有目标实例对象的HandlerMethod 了,在向下则通过 getHandlerExecutionChain 生成 HandlerExecutionChain 类型的包装类,主要再将拦截器加进来,源码如下:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//封装 HandlerExecutionChain 对象
	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

	// 将 adaptedInterceptors 中,MappedInterceptor中符合要求的添加进去
	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		// Spring内部维护的一个url匹配拦截器
		if (interceptor instanceof MappedInterceptor) {
			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
			if (mappedInterceptor.matches(request)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}
		else {
			// 添加拦截器
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

最后将 HandlerExecutionChain 对象返回就完成了获取 Handler 的工作。

SpringMVC 源码解析 - 请求执行的过程

一、SpringMVC 请求执行的过程

SpringMVC 是一个基于 Spring 框架的 MVC 框架,它简化了 Web 应用程序的开发。采用了前端控制器模式,请求会首先被一个中央控制器 DispatcherServlet 处理,再由其分发到具体的 Controller 进行处理。其中 SpringMVC 的请求执行过程如下图所示:

在这里插入图片描述
执行过程如下:

  1. 用户发送请求至前端控制器DispatcherServlet

  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器

  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器,一并返回给 DispatcherServlet

  4. DispatcherServlet 通过 HandlerAdapter处理器适配器调用处理器

  5. 执行处理器,也就是执行 Controller 中的方法

  6. 处理器执行完成返回ModelAndView

  7. HandlerAdapter将处理器的结果ModelAndView返回给DispatcherServlet

  8. DispatcherServletModelAndView传给ViewReslover视图解析器

  9. ViewReslover解析后返回具体View

  10. DispatcherServletView进行渲染视图,即将模型数据填充至视图中

  11. DispatcherServlet 响应用户

从上面是 SpringMVC 的执行过程中可以看出,其中 DispatcherServlet 占据着核心的位置,就像售票处的工作人员一样,当乘客(浏览器发送的请求)来到售票处时,工作人员(DispatcherServlet)会根据乘客所需要的目的地(请求的处理器)来为他们发放对应的门票(响应结果),下面我们从 DispatcherServlet 入手开始分析 SpringMVC 的源码。

二、SpringMVC 请求执行的过程源码解析

2.1 初始化操作

其中 DispatcherServlet 在 org.springframework.web.servlet 包下,可以看到该类继承了 FrameworkServlet 而 FrameworkServlet 又继承了 HttpServletBeanHttpServletBean 又继承了 HttpServlet ,因此 DispatcherServlet 就是传统 web 项目上的一个 Servlet :

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

那下面我们就以 Servlet 为切入点进行分析:

首先在 Servlet 中初始化时会触发 init() 方法,而这个方法就在 HttpServletBean 类中:

在这里插入图片描述

在初始化方法中会进行子类的初始化工作,则会触发 FrameworkServlet 中的 initServletBean 方法:

在这里插入图片描述

在 FrameworkServlet 主要调用了 initWebApplicationContext 初始化 WebApplicationContext,在 initWebApplicationContext 方法中会触发子类的 onRefresh 方法进行子类的初始化动作:

在这里插入图片描述
下面就可以来到 DispatcherServlet 中的 onRefresh 方法下:

在这里插入图片描述

使用 initStrategies 方法进行组件的初始化操作,其中 initStrategies 方法的源码如下:

protected void initStrategies(ApplicationContext context) {
    //初始化文件上传解析器
    //默认赋值 org.springframework.web.multipart.support.StandardServletMultipartResolver
    initMultipartResolver(context);
    //初始化国际化语言解析器
    //默认赋值 org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    initLocaleResolver(context);
    //初始化主题解析器
    //默认赋值 org.springframework.web.servlet.theme.FixedThemeResolver
    initThemeResolver(context);
    //初始化处理映射器
    initHandlerMappings(context);
    //初始化处理适配器
    initHandlerAdapters(context);
    //初始化异常处理解析器(@ControllerAdvice)
    //默认赋值 org.springframework.web.servlet.HandlerExceptionResolver接口子类
    //具体解析步骤
    initHandlerExceptionResolvers(context);
    //初始化视图转换器
    //默认赋值 org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    initRequestToViewNameTranslator(context);
    //初始化视图解析器
    //默认赋值 org.springframework.web.servlet.ViewResolver接口子类
    initViewResolvers(context);
    //初始化flash管理器
    //默认赋值 org.springframework.web.servlet.support.SessionFlashMapManager
    initFlashMapManager(context);
}

可以看到进行了各种组件的初始化操作,这里我们主要看下 HandlerMappings 和 HandlerAdapters 是如何初始化的,首先看到 initHandlerMappings 方法中:

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    // 从上下文中获取 handlerMapping,分为按类型获取,以及按名称获取
    // 是否检测所有的 handlerMapping , 默认为 true , 通过类型的方式从容器中获取 handlerMapping
    if (this.detectAllHandlerMappings) {
        // 获取全部的 handlerMapping
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        //handlerMapping 结果集不为空
        if (!matchingBeans.isEmpty()) {
            // 将映射器存入 handlerMappings 容器中
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // 根据order配置对handlerMapping进行排序, 所有的HandlerMapping都实现了Ordered接口
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    } else {
        try {
            // 根据名称获取 handlerMapping
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            // 存入容器
            this.handlerMappings = Collections.singletonList(hm);
        } catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }
    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    // 如果不存在 handlerMapping
    if (this.handlerMappings == null) {
        // 使用默认,默认策略不改动的情况下为: BeanNameUrl, RequestMapping, RouterFunction
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
    // 遍历全部的 handlerMapping
    for (HandlerMapping mapping : this.handlerMappings) {
        // 存在 handlerMapping 使用路径解析模式则赋值 parseRequestPath 为 true
        if (mapping.usesPathPatterns()) {
            this.parseRequestPath = true;
            break;
        }
    }
}

这里会去 Spring 容器中获取 handlerMapping ,如果不存在则会通过 getDefaultStrategies 方法获取一个默认的 handlerMappinggetDefaultStrategies 方法的源码如下:

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    if (defaultStrategies == null) {
        try {
            // Load default strategy implementations from properties file.
            // This is currently strictly internal and not meant to be customized
            // by application developers.
            //加载 DispatcherServlet 同级包中的 DispatcherServlet.properties
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
        }
    }
    //获取接口名作为key
    String key = strategyInterface.getName();
    //在 DispatcherServlet.properties 中获取对应的 value 值
    String value = defaultStrategies.getProperty(key);
    // 如果存在 value
    if (value != null) {
        // 如果有多个默认值,则以逗号分割为数组
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        List<T> strategies = new ArrayList<>(classNames.length);
        for (String className : classNames) {
            try {
                // 通过反射构建相应类型的对象实例,并添加进策略列表中
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
            } catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                        "Could not find DispatcherServlet's default strategy class [" + className +
                                "] for interface [" + key + "]", ex);
            } catch (LinkageError err) {
                throw new BeanInitializationException(
                        "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                                className + "] for interface [" + key + "]", err);
            }
        }
        return strategies;
    } else {
        return Collections.emptyList();
    }
}

下面再看 initHandlerAdapters 方法,初始化处理适配器:

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;
    // 是否检查所有的 HandlerAdapter ,默认为 true
    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        // 根据类型获取全部的 HandlerAdapter
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        // 如果结果集不为空
        if (!matchingBeans.isEmpty()) {
            // 将 HandlerAdapter 记入 handlerAdapters 容器中
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            // 排序
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    } else {
        try {
            // 根据名称获取 HandlerAdapter
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            // 加入容器
            this.handlerAdapters = Collections.singletonList(ha);
        } catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    // 如果不存在 HandlerAdapter
    if (this.handlerAdapters == null) {
        // 使用默认值
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

在该方法中,同样也是会先去 Spring 容器中获取,如果获取不到,则同样使用 getDefaultStrategies 创建一个默认的 HandlerAdapter

2.2 请求执行操作

在 Servlet 中请求进来后,会根据请求类型的不同触发相应的方法,比如 doGet() 或 doPost() 方法,这些方法在 FrameworkServlet 中:

在这里插入图片描述

可以看到,都调用了当前类的 processRequest 方法,在该方法中,则主要触发了子类的 doService 方法:

在这里插入图片描述

下面主要来看 DispatcherServlet 中的 doService 方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 记录请求日志
    logRequest(request);

    // 保存 request 属性,用于请求执行完属性清理后的属性恢复
    Map<String, Object> attributesSnapshot = null;
    // 当为 include 请求时,对 request 的 Attribute 做快照备份
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            // 判断 是否开启属性清理功能  或者 属性名的前缀为 org.springframework.web.servlet
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                //放入缓存中
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // 设置相关资源属性 用于后续请求过程的处理
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    // 如果 flashMapManager 不为空
    if (this.flashMapManager != null) {
        // 用于在 redirect 记录参数信息
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            // 用于保存上次请求中,转发过来的属性
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        // 用于保存本次请求需要转发的属性
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        // 记录 flushManager
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    // 是否进行路径解析(默认不进行)
    RequestPath requestPath = null;
    if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
        requestPath = ServletRequestPathUtils.parseAndCache(request);
    }

    try {
        // 主要处理逻辑
        doDispatch(request, response);
    } finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            // request属性恢复
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
        // 原 request 请求路径恢复
        if (requestPath != null) {
            ServletRequestPathUtils.clearParsedRequestPath(request);
        }
    }
}

该方法中主要则进行了一些属性的记录,主要逻辑则调用了 doDispatch 方法,下面进到 doDispatch 方法中:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //请求对象
    HttpServletRequest processedRequest = request;
    //处理请求的处理器链(包含 handler 和对应的 Interceptor)
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    //获取异步处理管理器,servlet3.0后支持异步处理
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        //  Model 和 View 的容器
        ModelAndView mv = null;
        // 处理请求 过程中,抛出的异常
        Exception dispatchException = null;

        try {
            // 检查是不是上传文件
            // 检查请求头中 Content-Type 属性是否为 multipart/form-data or multipart/ 开头
            // 如果是文件上传则构建一个 StandardMultipartHttpServletRequest 对象
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            // 根据 request 找到 handler
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                // 找不到,报404错误
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            // 根据 handler 找到 handlerAdapter 适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            // 获取请求方式
            String method = request.getMethod();
            // 处理 GET,HEAD 请求的  Last-Modified
            // 浏览器第一次跟服务器请求资源时(GET,HEAD请求),服务器的响应头会包含 Last-Modified属性
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                //获取最后修改时间
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                //调用checkNotModified方法验证 http 请求头中的“If-Modified-Since”的时间进行对比,判断页面是否更新过。
                //如果有更新才执行具体的Controller, 没有更新则响应 304 状态码信息(HTTP 304: Not Modified )
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // 调用拦截器的preHandle方法,若返回false,处理结束
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 调用 handler 处理请求,获取 ModelAndView 对象
            // controller 便是在此处执行的
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 如果异步正在执行,直接返回
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 当 view 为空时, 比如 Handler 返回值 为 void, 则根据 request 设置一个默认 view
            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的 postHandle 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        } catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 处理上面处理之后的结果 (包括 处理异常,渲染页面,发出完成通知触发  Interceptor 的 afterCompletion)
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
        // 调用拦截器的 afterCompletion 方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    } catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    } finally {
        //对于异步处理的情况,调用异步处理的拦截器 AsyncHandlerInterceptor 的 afterConcurrentHandlingStarted 方法
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else {
            // Clean up any resources used by a multipart request.
            // 对于multipart的请求,清理资源,比如文件上传的请求,
            // 在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

从该方法的执行逻辑,是不是和文章开头写的执行过程非常相似,没错,doDispatch 方法就是整个 SpringMVC 的核心逻辑方法,下面我们主要就该方法进行解析:

2.3 getHandler

在这里首先使用 getHandler方法找到相应的 handler 也就是 Controller,下面进入到该方法中看下逻辑:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        // 遍历 HandlerMapping 默认由优先级最高的 requestMappingHandlerMapping 来进行处理
        for (HandlerMapping mapping : this.handlerMappings) {
            //获取处理器
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

遍历了所有的处理器映射器HandlerMapping,调用他们的getHandler方法得到能够处理当前请求的HandlerExecutionChain对象, 这个对象中包含了 3 个信息:

  • handler:请求处理器,通常就是我们自定义的 controller 对象及方法
  • interceptorList:拦截器,当前请求匹配到的拦截器列表
  • interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了

下面在进到 mapping.getHandler 方法下,看是如何获取的,这里则主要触发的 AbstractHandlerMapping 类下的 getHandler 方法:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 模板方法,留给子类实现
	Object handler = getHandlerInternal(request);
	if (handler == null) {
		//handler为空获取默认的处理器 默认的 handler 也为 null
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// handler 如果是字符串对象,则通过 Spring 容器尝试获取 Handler 实例
	if (handler instanceof String) {
		String handlerName = (String) handler;
		// 获取 bean 实例
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//获取 HandlerExecutionChain 对象 ,包含 handler 和对应的 Interceptor
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}
	//跨域处理
	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		// cors 的检查请求
		CorsConfiguration config = getCorsConfiguration(handler, request);
		if (getCorsConfigurationSource() != null) {
			CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
			config = (globalConfig != null ? globalConfig.combine(config) : config);
		}
		if (config != null) {
			config.validateAllowCredentials();
		}
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

在该方法中,首先则调用子类的 getHandlerInternal 方法获取到 handler 其实是 HandlerMethod 对象,最后生成了一个 HandlerExecutionChain 封装对象,这里看下 getHandlerInternal 方法的逻辑,该方法在 AbstractHandlerMethodMapping 类下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	// 获取请求路径
	String lookupPath = initLookupPath(request);
	// 映射存储器,尝试获取锁
	this.mappingRegistry.acquireReadLock();
	try {
		// 通过路径和请求找到 HandlerMethod
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		// 如果找到则创建一个新的 HandlerMethod 返回
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		// 放行锁
		this.mappingRegistry.releaseReadLock();
	}
}

首先会拿到请求路径,然后通过 lookupHandlerMethod 方法根据路径找到相应的 HandlerMethod ,下面是该方法的逻辑:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	// 根据 loopuPath 获取对应 RequestInfoMapping 对象集合
	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
	if (directPathMatches != null) {
		// 将找到的 RequestInfoMapping 添加到容器缓存
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// 如果没找到,则将所有匹配条件的添加到容器缓存
		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
	}
	if (!matches.isEmpty()) {
		// 获取容器中的第一个
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			// 数量大于1的话进行排序后取第一个 HandlerMethod 包装类,请求条件数量多的优先
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			// 取第二个最优匹配
			Match secondBestMatch = matches.get(1);
			// 前两个最优匹配一致,则会抛出异常
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		//设置 HandlerMethod 的 request 属性
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		// 路径变量处理
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
	}
}

主要还是根据 loopuPath 获取对应 RequestInfoMapping 对象集合。

下面再回到 DispatcherServlet 中,获取到 HandlerExecutionChain 对象后,如果没有找到相应的 handler ,则无该接口即返回 404 :

在这里插入图片描述

接着则是使用 getHandlerAdapter 根据 handler 找到 handlerAdapter 适配器。

在这里插入图片描述

2.4 getHandlerAdapter

进入到 getHandlerAdapter 方法中:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        // 遍历 handlerAdapters
        for (HandlerAdapter adapter : this.handlerAdapters) {
            // 判断是否支持
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

这里遍历所有的 handlerAdapter 通过 adapter.supports 方法判断是否支持,如果支持则直接返回,最后如果都不支持的话则抛出异常,下面看下 adapter.supports 方法,在 AbstractHandlerMethodAdapter 类下:

public final boolean supports(Object handler) {
	//  handler 必须是 HandlerMethod 的子类
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

判断父类是HandlerMethod类型就可以了,其中 supportsInternal 主要触发的是 RequestMappingHandlerAdapter 类的 supportsInternal 方法,在该类中直接返回的 true

在这里插入图片描述

下面再回到 DispatcherServlet 中,获取到 HandlerAdapter 后,接着向下执行会根据请求方式处理 Last-Modified属性:

在这里插入图片描述
再向下执行则会调用拦截器的preHandle方法:

在这里插入图片描述

2.5 applyPreHandle

下面进入到 mappedHandler.applyPreHandle 方法中:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	for (int i = 0; i < this.interceptorList.size(); i++) {
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		// 拦截器前置处理
		if (!interceptor.preHandle(request, response, this.handler)) {
			// 前置处理返回 false 则执行拦截器中的 afterCompletion 方法
			triggerAfterCompletion(request, response, null);
			return false;
		}
		this.interceptorIndex = i;
	}
	return true;
}

这里直接获取到前面拿到的所有的拦截器,并触发 preHandle 方法,如果返回了 false,则表示在拦截器中就截断了,这里直接调用拦截器的 afterCompletion 方法。

再回到 DispatcherServlet 中,如果拦截器中的 preHandle 方法返回了 false ,那这里也没必要向下走了,直接 return 结束执行。

再向下执行的话,则来到了 ha.handle 中,这里则主要调用 handler 处理请求,获取 ModelAndView 对象,Controller 便是在此处执行的:

在这里插入图片描述

2.6 handle

该方法主要触发的是 AbstractHandlerMethodAdapter 类下的 handle 方法:

在这里插入图片描述

这里又主要调用了子类的 handleInternal 方法,其实是 RequestMappingHandlerAdapter 类的 handleInternal 方法:

protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	// 基于控制器的方法执行时,不论返回值是多少,都会被处理成 ModelAndView
	ModelAndView mav;
	//检查请求中所支持的方法和是否需要session
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	// 是否需要基于session来执行加锁执行
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			//获取锁
			Object mutex = WebUtils.getSessionMutex(session);
			// 上锁
			synchronized (mutex) {
				// 同步执行
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			// 没有session,则直接运行
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// No synchronization on session demanded at all...
		// 没有同步要求,也是直接运行
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	// 如果有 @SessionAttribute 注解,则阻止使用缓存
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		// 检查处理器类,是否有 @SessionAttributes 注解(检查注释里面的 name 和 types参数)
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			// 给response设置缓存过期时间
			prepareResponse(response);
		}
	}
	return mav;
}

从该方法的逻辑中可以看出,实际执行逻辑又调用了 invokeHandlerMethod 方法,下面再来到该方法下:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	// 构建一个ServletWebRequest对象
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		// 参数绑定,主要功能就是实现参数,跟 String 之间的类型转换
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		// 获取ModelFactory,主要用于在处理器具体处理之前,对 Model 进行初始化,处理完请求后,对 Model 参数进行更新
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		// ServletInvocableHandlerMethod 继承自 HandlerMethod,主要用于参数绑定,处理请求  以及 返回值处理
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		//如果参数解析器不为空
		if (this.argumentResolvers != null) {
			// 设置参数解析器
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		// 如果返回值处理器不为空
		if (this.returnValueHandlers != null) {
			// 设置返回值处理器
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		// 设置参数绑定工厂
		invocableMethod.setDataBinderFactory(binderFactory);
		// 设置 parameterNameDiscoverer
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		// 创建一个 ModelAndViewContainer,用于保存 Model 和 View
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		// 将 FlashMap 中的数据,添加到Model
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		// 使用modelFactory 将 sessionAttributes 和 注释了 @ModelAttribute的方法的参数设置到 Model
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		// 对 ignoreDefaultModelOnRedirect进行设置
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		// 异步请求
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		// 设置超时时间
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		//执行并处理请求
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		//返回 ModelAndView
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

该方法则是具体执行请求的处理,首先通过 getDataBinderFactory 方法进行参数的绑定,下面看下该方法的逻辑:

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
	Class<?> handlerType = handlerMethod.getBeanType();
	// 检查当前 Handler中的InitBinger方法 是否已经再缓存中
	Set<Method> methods = this.initBinderCache.get(handlerType);
	// 如果没有,则查找 并设置到缓存中
	if (methods == null) {
		methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
		this.initBinderCache.put(handlerType, methods);
	}
	// 定义保存 InitBinder方法的临时变量
	List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
	// Global methods first
	// 将所有符合条件的全局 InitBinder方法添加到 initBinderMethods
	this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
		if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
			Object bean = controllerAdviceBean.resolveBean();
			for (Method method : methodSet) {
				initBinderMethods.add(createInitBinderMethod(bean, method));
			}
		}
	});
	// 将当前 Handler中的  initBinder方法添加到 initBinderMethods中
	for (Method method : methods) {
		Object bean = handlerMethod.getBean();
		initBinderMethods.add(createInitBinderMethod(bean, method));
	}
	// 创建 DataBinderFactory并返回
	return createDataBinderFactory(initBinderMethods);
}

再回到 invokeHandlerMethod 方法中,继续下面看,会创建一个 ModelAndViewContainer对象,用于保存 Model 和 View,后面该对象则给到了 invokeAndHandle 方法,做执行并处理请求的逻辑,下面进入到 invokeAndHandle 方法:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 使用父类的方法,执行请求
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	// 处理 @ResponseStatus 注解
	setResponseStatus(webRequest);

	// 处理返回值
	if (returnValue == null) {
		// 判断如果 request 的 notModified为真 或者 @ResponseStatus 存在,或者 mavContainer 的 requestandled 为 true
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			//如果是 not modified 类型请求则进行缓存失效
			disableContentCachingIfNecessary(webRequest);
			//请求设置被完整处理
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	// 如果 returnValue 不为空,并且 @ResponseStatus 注解里面有 reason,则也设置为请求已经处理,并返回
	else if (StringUtils.hasText(getResponseStatusReason())) {
		//请求设置被完整处理
		mavContainer.setRequestHandled(true);
		return;
	}

	//请求设置被完整处理
	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		// 处理返回值
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}

在该方法中实际的执行请求则又调用了父类的的 invokeForRequest 方法,就是 InvocableHandlerMethod 类下的 invokeForRequest 方法:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 解析出方法所需要的参数
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//通过反射执行对应处理器方法
	return doInvoke(args);
}

这里通过 getMethodArgumentValues 方法解析出方法所需要的参数:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 获取方法的参数,在HandlerMethod中
	MethodParameter[] parameters = getMethodParameters();
	// 如果为空,则返回空数组
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}

	// 用于保存解析出参数的值
	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		// 给parameter设置参数名解析器
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		// 如果相应类型的参数 已经在proivdedArgs中提供了,则直接设置到 parameter
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		// 如果不支持参数则抛出异常
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			//  参数解析
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}

在回到 invokeForRequest 方法,继续向下,则采用反射 invoke 方式执行方法,可以看下 doInvoke 的逻辑:

protected Object doInvoke(Object... args) throws Exception {
	// 先使用反射将之强制变为可用
	ReflectionUtils.makeAccessible(getBridgedMethod());
	try {
		// 通过反射,调用bean的对应方法
		return getBridgedMethod().invoke(getBean(), args);
	}
	catch (IllegalArgumentException ex) {
		assertTargetBean(getBridgedMethod(), getBean(), args);
		String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
		throw new IllegalStateException(formatInvokeError(text, args), ex);
	}
	catch (InvocationTargetException ex) {
		// Unwrap for HandlerExceptionResolvers ...
		Throwable targetException = ex.getTargetException();
		if (targetException instanceof RuntimeException) {
			throw (RuntimeException) targetException;
		}
		else if (targetException instanceof Error) {
			throw (Error) targetException;
		}
		else if (targetException instanceof Exception) {
			throw (Exception) targetException;
		}
		else {
			throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
		}
	}
}

到这已经通过反射执行了 Controller 中的具体方法。

下面再回到 invokeAndHandle 方法下,这里将实际handler方法的返回,最后通过 returnValueHandlers.handleReturnValue 执行返回值的处理:

在这里插入图片描述

进入 returnValueHandlers.handleReturnValue 方法下:

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

这里又主要触发了 handler.handleReturnValue 方法,主要调用的子类的实现方法,这里的实现比较多:

在这里插入图片描述

主要看下 ViewNameMethodReturnValueHandler 解析器下的 handleReturnValue 方法,这个使用的比较多:

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue instanceof CharSequence) {
		String viewName = returnValue.toString();
		// 如果返回值为String,则将其设置到 mavContainer 的 view 中
		mavContainer.setViewName(viewName);
		if (isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else if (returnValue != null) {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type: " +
				returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
	}
	// void 返回值,什么也不处理
}

这里会将结果值填充至 ModelAndViewContainer 中的 viewName 属性中。

下面再回到 invokeHandlerMethod 方法中,通过 invocableMethod.invokeAndHandle 已经将返回值放入到 mavContainer 中了,最后通过 getModelAndView 方法获取了一个 ModelAndView

在这里插入图片描述
进入到该方法下:

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
		ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

	// 更新Model,包括设置 SessionAttributes 和 给Model 设置 BindingResult
	modelFactory.updateModel(webRequest, mavContainer);
	// 请求被完整处理
	if (mavContainer.isRequestHandled()) {
		return null;
	}
	// 获取ModelMap
	ModelMap model = mavContainer.getModel();
	//获取ModelAndView对象 封装请求对应视图和数据
	ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
	if (!mavContainer.isViewReference()) {
		//设置视图
		mav.setView((View) mavContainer.getView());
	}
	// flash属性填充
	if (model instanceof RedirectAttributes) {
		Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if (request != null) {
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
	}
	return mav;
}

直接创建了一个 ModelAndView 对象,并进行了属性的渲染,到这一步就拿到了 ha.handle 方法的主要目标 ModelAndView 了。

下面再回到 DispatcherServlet 中,继续向下走,如果 ModelAndView为空时, 比如 Handler 返回值为 void, 则根据 request 设置一个默认 view

在这里插入图片描述

再向下走的话则会调用拦截器的 postHandle 方法:

2.7 postHandle

进入到 mappedHandler.applyPostHandle 方法下:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
		throws Exception {

	for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		// 拦截器后置处理
		interceptor.postHandle(request, response, this.handler, mv);
	}
}

和前面 applyPreHandle 方法类似,遍历当前的拦截器,触发后置处理。

再回到 DispatcherServlet 中,再向下执行使用 processDispatchResult 方法,处理上面返回之后的结果:

在这里插入图片描述

2.8 processDispatchResult

进入到该方法下:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    //是否为错误视图
    boolean errorView = false;

    // 如果存在异常
    if (exception != null) {
        // ModelAndViewDefiningException 异常处理
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            // 其他异常处理
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            // 统一异常处理,并未阻断流程
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    // 视图渲染
    if (mv != null && !mv.wasCleared()) {
        // 渲染视图,具体在 render方法中执行
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        // 发出请求处理完成的通知,触发 Interceptor 的 afterCompletion
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

在该方法中进行了视图的渲染,并在最后触发了 Interceptor 的 afterCompletion,这里看下视图渲染方法 render :

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale =
            (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);

    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // We need to resolve the view name.
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    } else {
        // No need to lookup: the ModelAndView object contains the actual View object.
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }

    // Delegate to the View object for rendering.
    if (logger.isTraceEnabled()) {
        logger.trace("Rendering view [" + view + "] ");
    }
    try {
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // 视图解析
        view.render(mv.getModelInternal(), request, response);
    } catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "]", ex);
        }
        throw ex;
    }
}

在该方法中有主要调用了 view.render 完成视图解析,该方法则是 AbstractView 类下的 render 方法:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
		HttpServletResponse response) throws Exception {

	if (logger.isDebugEnabled()) {
		logger.debug("View " + formatViewName() +
				", model " + (model != null ? model : Collections.emptyMap()) +
				(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
	}

	//用于把参数的模型 与内置模型属性合并
	//合并顺序为:本视图的所有静态属性、当前请求的路径,传入的模型参数及封装好的请清求上下文
	//请求上下文中包含了主题源与信息源的相关内容
	//暴露的这些属性可以在后续处理中使用,以实现各种功能
	Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
	// 为视图设置相应的请求头
	prepareResponse(request, response);
	//将model 与视图进行整合
	renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

这里进行了模型属性的合并,最终使用 renderMergedOutputModel 方法将model 与视图进行整合,该方法为抽象方法,由子类实现具体逻辑:

在这里插入图片描述
这里主要看 InternalResourceView 下的 renderMergedOutputModel 方法:

protected void renderMergedOutputModel(
		Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

	// 将模型对象作为请求属性公开
	exposeModelAsRequestAttributes(model, request);

	// Expose helpers as request attributes, if any.
	exposeHelpers(request);

	// Determine the path for the request dispatcher.
	// 获取分发视图的路径
	String dispatcherPath = prepareForRendering(request, response);

	// Obtain a RequestDispatcher for the target resource (typically a JSP).
	// 构造jsp视图请求分发器
	RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
	if (rd == null) {
		throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
				"]: Check that the corresponding file exists within your web application archive!");
	}

	// 如果已包含或响应已提交,则执行包含,否则执行转发
	if (useInclude(request, response)) {
		response.setContentType(getContentType());
		if (logger.isDebugEnabled()) {
			logger.debug("Including [" + getUrl() + "]");
		}
		rd.include(request, response);
	}

	else {
		// Note: The forwarded resource is supposed to determine the content type itself.
		if (logger.isDebugEnabled()) {
			logger.debug("Forwarding to [" + getUrl() + "]");
		}
		// 转发
		rd.forward(request, response);
	}
}

下面再回到 DispatcherServlet 中,再继续向下看,如果发生了异常,则触拦截器的 afterCompletion 方法,将异常传递出去:

在这里插入图片描述
最后在 finally 中判断是否异步或文件上传,进行结尾操作:

在这里插入图片描述

posted @ 2024-12-12 09:20  CharyGao  阅读(27)  评论(0编辑  收藏  举报