SpringMVC处理请求的核心类是:DispatcherServlet,所有的 Web 请求都需要通过它来处理,进行转发,匹配,数据处理后,并转由页面进行展现,它是 SpringMVC 最核心的部分。Tomcat会将所有的请求都交给 DispatcherServlet 的 doService 方法处理。本次分析主要围绕DispatcherServlet来进行的。本次分析需要查看源码,如何查看源码可以参考:
点击跳转
简易的请求处理时序图:
DispatcherServlet类的继承关系图
以下内容为节省篇幅,仅粘贴了部分代码
首选我们查看DispatcherServlet的doService方法,它将请求转交给了doDispatch方法。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
//将请求交由doDispatch处理
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
我们查看doDispatch方法的实现,他的一个简易的流程是:首先是找到处理当前请求的Controller,通过getHandler方法去匹配找到对应的Controller,在通过getHandlerAdapter方法找到它的适配器,由适配器来执行Controller接口的方法;做好准备就开始执行了,首先依次执行拦截器的preHandle方法,第二步执行Controller中的方法,第三步执行拦截器的postHandle,最后处理返回结果,执行拦截器的afterCompletion方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到处理当前请求的Controller
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到当前的处理器的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//按照list顺序执行拦截器的preHandle方法,返回false就返回了
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通过动态代理调用controller的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//根据返回是否需要匹配视图
applyDefaultViewName(processedRequest, mv);
//按照list顺序执行拦截器的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);
}
//处理返回结果,在这将调用拦截器的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 {
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.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
SpringMVC 是如何根据请求地址找到我们对应的Controller以及方法的呢?
Spring将请求与处理方法的对应关系缓存在了
MappingRegistry
这个对象中,这个对象有几个Map成员,缓存的内容即key为请求路径,value为处理方法。
class MappingRegistry {
//缓存请求映射与处理方法
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
//缓存请求路径与对应的方法类型(POST,GET这种)
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
}
以下是存储的请求路径与对应的Controller的debug信息
根据请求路径找的对应的处理方法的的关键在AbstractHandlerMethodMapping类的lookupHandlerMethod方法。在doDispatch方法中,调用本类中的getHandler方法,在调用AbstractHandlerMapping类中的getHandler方法找到对应的handler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//找到controller
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
AbstractHandlerMapping类中的getHandler方法的实现
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根据请求匹配找到handler即Controller信息
Object handler = getHandlerInternal(request);
}
AbstractHandlerMethodMapping类中的getHandlerInternal方法的实现
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//拿到请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
//根据请求路径找到controller 就是根据map匹配到controller
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
AbstractHandlerMethodMapping类中的lookupHandlerMethod的实现,根据请求地址匹配Controller
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//根据请求地址找到对应的路径和请求方式
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//找到对应的controller信息
addMatchingMappings(directPathMatches, matches, request);
}
}
刚刚我们找到了我们请求对应的Controller,那Spring是如何调用我们的Controller对应的方法的呢?
在DispatcherServlet的doDispatch方法中,通过 ha.handle(processedRequest, response, mappedHandler.getHandler()) 这段代码执行我们的Controller的方法,跟进执行发现是由ServletInvocableHandlerMethod类的invokeAndHandle来执行的
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//执行Controller中的方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//设置响应码
setResponseStatus(webRequest);
}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//组装Controller中方法需要的参数
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Arguments: " + Arrays.toString(args));
}
//通过反射调用Controller方法
return this.doInvoke(args);
}
以上是本次Springboot处理请求的源码分析记录,由于处理流程比较繁杂,仅挑选了部分代码讲解,如有问题欢迎指正