在Servlet标准中,请求的处理过程是先通过由所有的Filter组件构成Filter链对请求进行过滤与预处理了,如果在Filter链中没有对请求提前结束处理,则最终会进入Servlet组件中对请求进行处理;
请求一个get方法
由这个调用栈可以看到,过滤器执行完成后,就会调用HttpServlet的service方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
点击service方法,由子类实现:即FrameworkServlet,
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
这里httpMethod,我请求的是get方法,所以会走到else方法中,
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
GET请求的处理有些特殊,进行了HTTP缓存规范判断,如判断请求头中的If-Modified-Since以达到直接从浏览器缓存获取请求结果,不用再去执行方法获取数据的目的;但前提是需要支持LastModified功能,默认为-1即不支持;则走doGet()方法;由子类FrameworkServlet实现
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)\
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
主要是这个doService(request, response);
这个doService方法也是由子类去实现的,由图可知,子类就是DispatcherServlet;
doService方法中也有一个比较重要的点,Map<String, Object> attributesSnapshot = null;
保存请求属性快照用于请求完成后回复
在请求类型为include时,保存当前请求属性的快照,以便在include执行完后恢复请求属性;
目的:
在ServletApi中,请求类型常见的由重定向redirect,转发forward和包含include。重定向通过返回重定向响应实现;转发则直接丢弃当前处理过程,通过服务器内部转发给另一个请求处理流程来处理;而包含则时在一个请求处理过程中包含另一个请求的处理过程,同时还包含请求过程的过处理使用相同的request和response,这样可以做到在一个请求的响应中包含多个内部请求的响应结果;
比如:
1. jsp中的include标签;
2. 请求/root的处理过程中,先进入doService方法,向request中添加一些属性,该请求还在处理中,这时候服务器内部发起一个include请求,路径为/root/include,该include请求也会进入到doService方法,此时先保存了/root请求的属性,再执行/root/include的请求向request中添加属性,这样,再/root/include结束后,就是用快照恢复/root的属性,/root请求的属性就不会丢失;注意这里/root的结束在/root/include后边;
然后调用doDispatch(request, response);方法;
这个方法就是springmvc的最核心方法了;
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//定义一个已处理请求,指向参数中的request,已处理请求后续可能会改变,主要用于比较
HttpServletRequest processedRequest = request;
//定义处理器执行链,内部封装了拦截器列表和处理器
HandlerExecutionChain mappedHandler = null;
//是否为多块请求,默认为否
boolean multipartRequestParsed = false;
//获取与当前请求关联的异步管理器,用于执行异步操作
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//用于保存处理适配器执行处理器后的返回结果
ModelAndView mv = null;
//用于保存处理过程中发生的异常
Exception dispatchException = null;
try {
//1.检查多块请求,如果是多块请求,则返回一个新的请求,processedRequest保存这个新的请求引用,否则返回原始请求,即如果是多块请求,这个request会改变为其他类型的request,这时候赋值给processedRequest ,那么processedRequest 与doDispatch的参数request就不是同一个类型了,则下面判断就会为true
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//2.获取可处理当前请求的请求处理器,通过HanlderMapping查找,请求处理器中封装了拦截器链和对应的处理器,可以是具体的处理器方法
mappedHandler = getHandler(processedRequest);
//如果没有,则执行没有处理器的逻辑
if (mappedHandler == null) {
//内部逻辑判断配置this.throwExceptionIfNoHandlerFound是否为true,如果为true则抛出异常,否则直接设置响应内容为404;可以通过spring.mvc.throwExceptionIfNoHandlerFound设置值,默认为false
noHandlerFound(processedRequest, response);
return;
}
//3.根据当前请求的处理器获取支持该处理器的处理适配器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//4.单独处理last-modified请求头,用于判断请求内容是否修改,如果未修改直接返回,浏览器使用本地缓存\
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
//只有get和head请求执行该判断
if (isGet || HttpMethod.HEAD.matches(method)) {
//具体实现还是通过处理适配器来实现的;
//通过处理适配器的getListModified方法,传入请求与处理器,获取该请求对应的内容最后修改时间;
//一般针对静态资源,返回静态资源的上一次修改时间,动态资源固定返回-1,表示不存在该时间;
//具体看一下这位大佬写的https://blog.csdn.net/iwts_24/article/details/84575045
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
//经过判断,如果最后修改时间在当前请求中浏览器缓存时间之前,则直接返回状态码304,表示未修改,浏览器可直接使用本地缓存作为请求内容;
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//5.通过mapperdHandler这个HanlderExecutionChain执行链的封装,链式执行其中所有拦截器的前置拦截方法preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//6.最终执行处理器适配器的处理方法,传入请求,响应与其对应的处理器,对请求进行处理。在这个处理中,最终调用到了请求对应的处理器方法;
//执行的返回值是ModelAndView类型,封装了模型数据与视图;
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//如果异步处理开始,则直接返回,后续处理均通过异步执行;
//https://blog.csdn.net/woshilijiuyi/article/details/79316105
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//7.应用默认视图名,如果返回值的ModelAndView中不包含视图名,则根据请求设置默认视图名,具体逻辑后面说明;
applyDefaultViewName(processedRequest, mv);
//8.请求处理正常完成,链式执行所有拦截器的postHandle方法。链式顺序与preHandle相反
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.
//在springmvc4.3版本之后,添加了支持Error类型异常的处理。Throwable的子类除了Exception就是Error
//可以通过@ExceptionHandler处理这种类型的异常
//封装为嵌套异常以供异常处理逻辑使用
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//9.对上面逻辑的执行结果进行处理,包括处理适配器的执行结果处理以及发生异常处理等逻辑;
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//10.拦截后链式执行拦截器链的afterCompletion方法。在该方法内部判断mappedHandler是否为空,如果不为空,则执行triggerAfterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//11.拦截Error类型异常,拦截后链式执行拦截器链的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//12.finally块,做资源清理
if (asyncManager.isConcurrentHandlingStarted()) {
//如果在异步请求执行中,则链式执行拦截器链中的atferConcurrentHandlingStarted方法,即针对异步请求的特殊处理
// 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);
}
}
}
}
什么是多块请求:
processedRequest = checkMultipart(request);
在springmvc中,对于多块请求有特殊处理,如@RequestPart绑定多块请求参数与多块请求文件等;要想实现这些特殊处理,就需要先对请求类型为多块的请求执行预处理,在请求分发前就执行该操作,并替换后续使用的请求已经预处理过的多块请求;
上面的checkMultipart方法,就是判断是否为多块请求,使用了多块解析器StandarServletMultipartResolver进行解析,如果该request的content-Type为multipart/开头的话即为多块请求;
查找请求处理器:
mappedHandler = getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这里仅仅是遍历所有的处理器映射,以此尝试获取与当前请求匹配的处理器执行链,至于这个HandlerMapping是什么时候装载完所有的处理器对应关系的,后边说;
获取处理适配器:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
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");
}
Springmvc对于请求处理器的查找与执行是分离的,而根据处理器的类型不同,又需要使用不同的适配器去执行该处理器,这正式HandlerAdapter的作用;
对于@RequestMapping注解注册的处理器,类型为HandlerMethod,通过RequestMappinHandlerMapping返回。该适配器为RequestMappingHandlerAdapter;
执行前置拦截器链:
mappedHandler.applyPreHandle(processedRequest, response)
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)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
执行拦截器的preHandle方法,任意一个拦截器如果返回的是false,则直接停止执行,视为处理完成,触发拦截器的完成后方法triggerAfterCompletion;
至于这个拦截器是什么时候和Handler绑定在一块的,就要看这个mappedHanlder了,其实里边就是封装了handler和它能执行的拦截器;
注意:this.interceptorIndex = i;的作用就是在调用triggerAfterCompletion方法时,只会调用那些已经执行了preHandle方法的拦截器的afterCompletion方法(除了当前preHandle方法返回false的拦截器外);
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
可以看到在for循环中,int i = this.interceptorIndex;
处理适配器执行:ha就是前面的适配器,mappedHandler里封装了handler和拦截器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这个是整个请求处理逻辑最核心的地方,参数的解析和返回值的解析Resolver都是在这里边进行处理;比如说参数解析@RequestParam,@RequestBody之类的,返回值解析@ResponseBody,视图等;
返回值视图名处理:
applyDefaultViewName(processedRequest, mv);
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
为什么要存在这个默认视图名填充呢?
因为有的处理器会使用void,或者返回null,在没有@ResponseBody的情况下,默认是会需要视图去返回给前端的,如果这时候返回null的话,则默认设置请求路径的名字去查找视图,如果没找到的话,就会报错;与这里面的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());一个属性有关系
mavContainer.setRequestHandled(false);如果它为false的话,那么默认是会实例化一个ModelAndView的,那么这个时候回到上面的applyDefaultViewName(processedRequest, mv);,这时候mv是不为null的就会设置默认的视图名;具体可以看下别人写的blog.csdn.net/QAQFyl/arti…
执行后置拦截器链:
mappedHandler.applyPostHandle(processedRequest, response, mv);
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);
}
}
这里是倒序,那么拦截器的prehandle执行顺序就会是A -> B -> C,postHandle就是C -> B -> A;
处理返回值与响应:
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
//标记是否是error视图
boolean errorView = false;
//如果出现了异常
if (exception != null) {
//如果异常类型为ModelAndViewDefiningException
if (exception instanceof ModelAndViewDefiningException) {
//该异常内部包含一个ModelAndView类型的属性,用于提供包含ModelAndView结果的异常封装
logger.debug("ModelAndViewDefiningException encountered", exception);
//直接使用异常中封装的ModelAndView作为最终的ModelAndView结果;
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//其他异常类型,先获取处理器
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//执行process处理器异常方法,获取处理了异常结果后得到的ModelAndView结果
mv = processHandlerException(request, response, handler, exception);
//如果mv不为空,则说明返回了包含异常的视图,即返回的视图为异常视图;
errorView = (mv != null);
}
}
// Did the handler return a view to render?
//如果视图与模型不为空,且视图与模型没有标记为被清理(被清理表示调用过ModelAndView的clear方法,清理后的ModelAndView相当于null)
if (mv != null && !mv.wasCleared()) {
//视图与模型不为空时,执行渲染视图的操作
render(mv, request, response);
//如果时异常视图,渲染后需要清空请求属性中的异常信息\
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
//如果视图为null,则打印一个日志
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..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
在无异常的情况下,主要处理对象是处理适配器返回的ModelAndView。将它render出去;
如果存在异常了,异常处理后形成ModelAndView,使用这个ModelAndView继续执行后续处理;
其中mv = processHandlerException(request, response, handler, exception);是@ExceptionHandler的处理地方:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
//如果处理器异常解析器列表不为空
if (this.handlerExceptionResolvers != null) {
//遍历该列表
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
//执行处理器异常解析器的解析异常方法,拿到解析的ModelAndView结果
exMv = resolver.resolveException(request, response, handler, ex);
//如果不为空,则将此结果作为对异常处理后的ModelAndView结果使用,中断后续的遍历动作
if (exMv != null) {
break;
}
}
}
//如果返回的异常ModelAndView不为null
if (exMv != null) {
//如果ModelAndView内部为空(View为空且Model为空)
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
//如果异常ModelAndView 不包含视图,但是包含Model不为空
if (!exMv.hasView()) {
//采用与doDispatch方法中相同的处理逻辑来根据请求获取默认视图名
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
else if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
//暴露一些异常信息到请求属性中
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
//如果没有处理器异常解析器,exMv为null,则原封不懂抛出原始异常,交给web框架处理
throw ex;
}
然后回到processDispatchResult方法,mv = processHandlerException(request, response, handler, exception);
这时候mv一定不为null,如果为null,在里边就抛错了;
另一个比较重要的方法就是render(mv, request, response);到这里,ModelAndView是一定不为null的;
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
//先通过Locale解析器获取请求对应的Locale
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
//设置获取的Locale为相应的Locale
response.setLocale(locale);
//最终获取的视图
View view;
//如果ModelAndView中的视图为视图名,则获取这个视图名
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 {
//到这,view肯定不为null
//如果ModelAndView中的status部位为空,则把其设置为相应的状态码,@ResponseStatus设置的状态码功能就是通过这里实现的
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//执行视图的渲染方法,每种模板引擎都有相应的视图实现,视图渲染对应于模板引擎的渲染模板,fremarker,thymeleaf这些,还有默认的jsp -> AbstractView -> InternalResourceView类;
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
视图名解析器:
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}