SpringMVC 源码解析 - Controller 的扫描注册及匹配过程
一、SpringMVC Handler 扫描注册过程
在本专栏前面文章中对 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
,所以会进入到第一个判断中,将 beanName
、IOC
工厂、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
的请求执行过程如下图所示:
执行过程如下:
-
用户发送请求至前端控制器
DispatcherServlet
-
DispatcherServlet
收到请求调用HandlerMapping
处理器映射器 -
处理器映射器根据请求
url
找到具体的处理器,生成处理器对象及处理器拦截器,一并返回给DispatcherServlet
-
DispatcherServlet
通过HandlerAdapter
处理器适配器调用处理器 -
执行处理器,也就是执行
Controller
中的方法 -
处理器执行完成返回
ModelAndView
-
HandlerAdapter
将处理器的结果ModelAndView
返回给DispatcherServlet
-
DispatcherServlet
将ModelAndView
传给ViewReslover
视图解析器 -
ViewReslover
解析后返回具体View
-
DispatcherServlet
对View
进行渲染视图,即将模型数据填充至视图中 -
DispatcherServlet
响应用户
从上面是 SpringMVC
的执行过程中可以看出,其中 DispatcherServlet
占据着核心的位置,就像售票处的工作人员一样,当乘客(浏览器发送的请求)来到售票处时,工作人员(DispatcherServlet
)会根据乘客所需要的目的地(请求的处理器)来为他们发放对应的门票(响应结果),下面我们从 DispatcherServlet
入手开始分析 SpringMVC
的源码。
二、SpringMVC 请求执行的过程源码解析
2.1 初始化操作
其中 DispatcherServlet
在 org.springframework.web.servlet
包下,可以看到该类继承了 FrameworkServlet
而 FrameworkServlet
又继承了 HttpServletBean
,HttpServletBean
又继承了 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
方法获取一个默认的 handlerMapping
,getDefaultStrategies
方法的源码如下:
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
中判断是否异步或文件上传,进行结尾操作: