SpringMVC工作流程及代码分析
每谈到SpringMVC的工作流程,首先接触到的就是下面这个图。从这个图可以大致明白SpringMVC是如何工作的。但是我是一个喜欢探究来龙去脉的人,如果不告诉我为什么这么做,单单知道流程就是这样,抱歉,我真的记不住,更不用提里面这么多专业名词了。所以,通过翻阅了源码,大致知道流程是具体怎么实现的,也学到了一些新的设计模式,所以我将阅读源码的所得记录下来,希望本文可以帮助到和我一样喜欢探究来龙去脉的人。
1,SpringMVC是如何被初始化的?
是通过在web.xml配置文件中配置servlet,servlet的class指向org.springframework.web.servlet.DispatcherServlet,并匹配指定的url(一般情况下使用“/”匹配所有url),指定的请求都会进入DispatcherServlet,由DispatcherServlet进行处理。
<!-- spring mvc核心:分发servlet --> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- spring mvc的配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
2,DispatcherServlet的实例化
为什么要讲DispatcherServlet的初始化,而不是直接说SpringMVC的工作流程,因为在初始化的过程中,建立了HandlerMappings,HandlerAdapters,我会在下面描述这两个如何定义及使用的,有了这些概念,再接触SpringMVC的工作流程,就会非常清晰流畅了。
Tomcat启动时,就会对DispatcherServlet进行实例化,调用他的init()方法。这里并没有直接在DispatcherServlet中重写init(),而是使用了模板方法模式。抽象类HttpServletBean中定义并实现了init(),FrameworkServlet继承了HttpServletBean,DispatcherServlet继而继承了FrameworkServlet。如果对于模板方法模式不了解,可以查看我的另外一篇文章模板方法模式(Template Method)及应用,也可以自行搜索学习,这里不再赘述。
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like.让子类去实现的方法 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
2.1 FrameworkServlet的initServeltBean()
@Override protected final void initServletBean() throws ServletException { ... try { // 初始化 WebApplicationContext (即SpringMVC的IOC容器) this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); //这个没有具体实现,如果后续发现会补上 } catch (ServletException ex) { } catch (RuntimeException ex) { } ... }
2.2 FrameworkServlet的initServeltBean()
protected WebApplicationContext initWebApplicationContext() {
//获取在web.xml中定义的监听器ContextLoaderListener,初始化并注册在ServeltBean中的根容器,即Spring容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it 因为webApplicationContext不为空,说明在构造时已经注入。
wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); //将Spring容器设为SpringMVC容器的父容器,这也就是为什么,SpringMVC容器可以调用Spring容器而反过来不可以。 } configureAndRefreshWebApplicationContext(cwac); 20000 } } } if (wac == null) { wac = findWebApplicationContext(); //如果为空,则进行查找,能找到就说明上下文已在别处初始化,详见2.3 } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); //如果webApplicationContext仍为空,则以Spring的容器作为父容器建立一个新的。
} if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); //模板方法,由DispatcherServlet实现,详见2.4 } if (this.publishContext) { // 发布这个webApplicationContext容器到ServeltContext中。 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
2.3 查找webApplicationContext: findWebApplicationContext();
在servletContext中根据attrName查找
protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); if (attrName == null) { return null; }
//从ServletContext中查找已发布的容器 WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }
2.4 DispatcherServelt 中的
从源码中可以看到,在这个方法中,主要是对各个组件的初始化,包括在整个流程中非常重要的 处理器映射器(HandlerMapping) 和 处理器适配器(HandlerAdapter)
protected void onRefresh(ApplicationContext context) { initStrategies(context); }
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
2.4.1 HanderMapping是什么?
2.4.2 HanderAdapter是什么?
3 DispatcherServelt处理请求
根据Servlet处理请求的流程,是要调用其doGet(),doPost()方法。DispatcherServelt本质也是个Servlet,所以这两个方法是在其父类FrameworkServelt中实现的。
3.1 FrameworkServelt中的doGet(),doPost()
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
这些方法都统一调用了processRequest(request, response);
3.2 processRequest(request, response)
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // 返回与当前线程相关联的 LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境 LocaleContext localeContext = buildLocaleContext(request); // 返回当前绑定到线程的 RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); // 根据请求构建ServletRequestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //WebAsyncManager:用来管理异步请求的处理。什么时候要用到异步处理呢?就是业务逻辑复杂(或者其他原因),为了避免请求线程阻塞,需要委托给另一个线程的时候。 // 获取当前请求的 WebAsyncManager,如果没有找到则创建 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); // 使 LocaleContext 和 requestAttributes 关联 initContextHolders(request, localeContext, requestAttributes); try { // 由 DispatcherServlet 实现 doService(request, response); } catch (ServletException ex) { } catch (IOException ex) { } catch (Throwable ex) { } finally { // 重置 LocaleContext 和 requestAttributes,解除关联 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); }// 发布 ServletRequestHandlerEvent 事件 publishRequestHandledEvent(request, startTime, failureCause); } }
3.3 DispatcherServetl 中的 doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; //processedRequest:加工过的请求 HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; //上传相关的?后续补充 // 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 检查是否有 Multipart,有则将请求转换为 Multipart 请求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。(如果对handler不理解,不要急,到第二章去看) mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 处理 last-modified 请求头 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; } } // 遍历拦截器,执行它们的 preHandle() 方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // 执行实际的处理程序 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // 遍历拦截器,执行它们的 postHandle() 方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } // 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { } catch (Error err) { } finally { if (asyncManager.isConcurrentHandlingStarted()) { // 遍历拦截器,执行它们的 afterCompletion() 方法 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
通过阅读源码,已经可以整理出最开始的流程图和源码之间的对应了,还有一些东西有空会补充上。知其然,更要知其所以然,才是与别人拉开差距的真正意义。
转载于:https://www.cnblogs.com/handler4J/p/10055228.html
- 点赞
- 收藏
- 分享
- 文章举报
- No qualifying bean of type 'org.springframework.mail.javamail.JavaMailSender' available: expected at...
- 宠物领养网站(一):简 4000 单搭建SpringBoot+JPA+Gradle+Mysql项目
- 解决 https://start.spring.io 访问报错问题
- 宠物领养网站(二):SpringBoot支持JSP开发配置
- JDK,JRE,JVM的区别与联系
- java中 equals 和 hashcode 的关系
- java知识点总结
- java实现九九乘法表-超简单的一目了然
- Java验证码读取
- java界面实现骰子比赛改进
- BES-多模块Springboot项目MyBatis通用Mapper配置(Controller Service Dao在不同子模块中)
- BES-SpringCloud Gateway网关整合多模块项目-Predicates与Filter
- java实现一个删除固定后缀文件的程序
- java编写贪吃蛇小游戏源代码分享给你们
- @蓝桥杯javaB组习题集入门(4)之第二题:序列求和
- @蓝桥杯javaB组习题集入门(4)第四题:Fibonacci数列
- @java蓝桥杯B组习题基础篇(30)第二题:01字串
- @java蓝桥杯B组习题基础篇(30)第四题:数列特征
- @java蓝桥杯B组习题基础篇(30)第八题:回文数
- @java蓝桥杯B组习题基础篇(30)第10题:十进制转十六进制