您的位置:首页 > 编程语言 > Java开发

SpringMVC工作流程及代码分析

2020-02-17 04:48 393 查看

  每谈到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

  • 点赞
  • 收藏
  • 分享
  • 文章举报
az2334959312 发布了0 篇原创文章 · 获赞 0 · 访问量 311 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: