前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
SpringMVC执行流程在面试中经常会被问到,本篇文章通过源码的方式简单的了解一下SpringMVC执行流程。
先看流程
先看一下SpringMVC执行流程再看源码,有助理解:
- ⽤户发送请求⾄前端控制器DispatcherServlet。
- DispatcherServlet 收到请求调⽤ HandlerMapping 处理器映射器。
- 处理器映射器找到具体的处理器(可以根据xml配置、注解进⾏查找),⽣成处理器及处理器拦截器(如果有则⽣成)⼀并返回给DispatcherServlet。
- DispatcherServlet调⽤HandlerAdapter处理器适配器。
- HandlerAdapter经过适配调⽤具体的处理器(Controller,也叫后端控制器)
- Controller执⾏完成返回ModelAndView。
- HandlerAdapter 将 Controller 执⾏结果 ModelAndView 返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
- ViewReslover解析后返回具体View。
- DispatcherServlet根据View进⾏渲染视图(即将模型数据填充⾄视图中)。
- DispatcherServlet 响应⽤户。
再看源码
我们都知道当从用户发起请求到后端是,首先走的就是DispatcherServlet,接着就会调用doService()
方法执行业务逻辑,doService()
方法也只是一个中转站,实际执行逻辑的是doDispatch()
方法,且看源码:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// 省略部分源码
try {
// 执行实际逻辑
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);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
doDispatch 方法:
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);
// 为当前请求获取映射处理器
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用的Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
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);
}
//处理转发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
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);
}
}
}
}
下面来看一下其中几个重要的方法:
getHandler(HttpServletRequest request)
方法:该方法是处理当前请求找到合适的HandlerMapping,并返回一个HandlerExecutionChain,HandlerExecutionChain,HandlerExecutionChain包含了具体的处理器(handler)和拦截器列表。HandlerMapping 默认的实现有org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
和org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
。
@Nullable
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;
}
getHandlerAdapter(Object handler)
根据HandlerExecutionChain中的handler来获取处理器适配器(HandlerAdapter),
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");
}
HandlerAdapter有两个默认实现类,分别是 org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
和 org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
,前者用于没有使用模板引擎的请求,后者用于使用了模板引擎的接口。
实际处理请求的是HandlerAdapter的handle方法,如果是没有使用例如JSP等的模板引擎,handle方法就会返回null,如果使用了模板引擎就会返回一个ModelAndView对象。
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handle方法最终调用的是Controller接口的 handleRequest(HttpServletRequest request, HttpServletResponse response)
方法来处理请求。
以SimpleControllerHandlerAdapter#handle方法源码为例:
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
processDispatchResult
方法用于处理转发结果,该结果要么是一个ModelAndView,要么抛异常。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
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(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..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
processDispatchResult
方法中在正常情况下会调用render
方法。
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) {
// 通过视图名称获取视图
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;
}
}
DispatcherServlet的render
方法是对视图View的封装,最后调用的还是View的render方法。
resolveViewName
方法用于解析视图名称,它会通过视图解析器ViewResolver的resolveViewName
方法解析视图并返回一个视图View,然后再通过View的render方法渲染视图,至于是怎么渲染视图的这里就不介绍了,感兴趣的可以自行查看源码。
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;
}
总结
以上就是正常调用下DispatcherServlet主要流程的源码,面试中问流程的比较多,了解源码会加分,最好是知道一点,这里也是简单的介绍一下,想了解更多的源码可以自行查看。