SpringMVC探秘-请求之路

开始

今天来分析一下SpringMVC的原理,探究SpringMVC如何把请求传递到每个Controller的方法上,从Servlet到Controller,一个请求走了哪些艰难曲折的路。

基本核心组件

在开始分析之前,先了解SpringMVC中的几个概念,后面提到的时候不至于不知道这个是干什么用的,不过不用彻底知道这个组件是如何实现的,现在只需要知道有这个东西就行了, 先混个脸熟

组件 说明
DispatcherServlet SpringMVC中的中心,处理请求的分发,视图的解析等,本质上是一个Servlet
HandlerMapping 一个映射,<URL,Handler>,可以根据URL找到匹配的Handler,这儿的Handler是HandlerExecutionChain
HandlerAdapter Hander的适配器,委托这个适配器去调用具体的某个Hander
ViewResolver 视图解析器,用于将视图名称解析为视图对象 View。

这些组件都是经过DispatcherServlet的协调,把一个请求分发到具体的一个Hander,然后根据Hander的返回值,来做不同的处理,不同的组件处理不同的逻辑。

追根溯源

请求的入口-Servlet

我们都知道Servlet是一个标准,我们的处理逻辑代码只需要写到Servlet中,而对于Http请求响应的解析处理都不需要我们关心,已经有很多服务器实现了这部分,比如熟悉的小猫猫Tomcat,它就是按照Servlet的标准来实现的。

ServletConfig

/**
* A servlet configuration object used by a servlet container to pass
* information to a servlet during initialization.
* 当servlet实例化的时候,servlet容器传递给servlet的配置
*/
public interface ServletConfig { /**
* Returns the name of this servlet instance. The name may be provided via
* server administration, assigned in the web application deployment
* descriptor, or for an unregistered (and thus unnamed) servlet instance it
* will be the servlet's class name.
* 返回servlet的名字,如果没有指定,则返回servlet的类名
* @return the name of the servlet instance
*/
public String getServletName(); /**
* Returns a reference to the {@link ServletContext} in which the caller is
* executing.
* 返回调用者当前执行环境的ServletContext引用
* @return a {@link ServletContext} object, used by the caller to interact
* with its servlet container
* @see ServletContext
*/
public ServletContext getServletContext(); /**
* Returns a <code>String</code> containing the value of the named
* initialization parameter, or <code>null</code> if the parameter does not
* exist.
* 返回一个字符串类型的值,这个值是初始化的参数
* @param name
* a <code>String</code> specifying the name of the
* initialization parameter
* @return a <code>String</code> containing the value of the initialization
* parameter
*/
public String getInitParameter(String name); /**
* Returns the names of the servlet's initialization parameters as an
* <code>Enumeration</code> of <code>String</code> objects, or an empty
* <code>Enumeration</code> if the servlet has no initialization parameters.
* 返回所有的初始化参数
* @return an <code>Enumeration</code> of <code>String</code> objects
* containing the names of the servlet's initialization parameters
*/
public Enumeration<String> getInitParameterNames();
}

Servlet

package javax.servlet;

import java.io.IOException;

/**
* 所有的servlet都应该实现这里的方法
*
* servlet是一个运行在Web Server的小Java程序,它们接受对来自web客户端的请求并做出响应,这些请求通常通过HTTP传达
*
* 为了实现这个接口,你可以写一个基于javax.servlet.GenericServlet,或者HTTP Servletjavax.servlet.http.HttpServlet派生的servlet类
*
* 这个接口定义了实例化servlet,接受请求,从server移除的方法,这些方法被称为生命周期方法:并且按照一些的顺序调用:
* 1. servlet被实例化,调用init()方法初始化
* 2. 当有来自客户端的调用,serviceI()方法被调用
* 3. servlet退出服务,destory()方法被调用,然后执行GC
*
* 除了生命周期方法外,这个接口还提供了getServletConfig方法来获取启动信息,getServletInfo来获取
* servlet自身的基本信息,比如作者,版本,版权等
*
* @see GenericServlet
* @see javax.servlet.http.HttpServlet
*/
public interface Servlet { /**
* 被servlet容器调用,表示这个servlet将要开始服务
*
* servlet实例化之后马上调用这个方法,在开始处理请求前这个方法必须调用成功
*
* 如果这个方法抛出异常或者在指定的时间段没有返回,那么这个serlet也不能处理请求
* @exception ServletException
* if an exception has occurred that interferes with the
* servlet's normal operation
*
* @see UnavailableException
* @see #getServletConfig
*/
public void init(ServletConfig config) throws ServletException; /**
* 获取ServletConfig,存储了启动参数
* @return the <code>ServletConfig</code> object that initializes this
* servlet
*
* @see #init
*/
public ServletConfig getServletConfig(); /**
* 被Servlet容器调用,用来对一个请求做出响应
*
* 只有当servlet中的init()方法调用成功后这个方法才会调用
*
* 应该为抛出异常或者错误的servlet返回的response设置响应码
*
* 这个方法可能被并发调用,所以开发者对于共享的资源要同步操作,共享的资源比如,文件,网络连接,
* 还有servlet的类变量和实例变量
* @param req
* the <code>ServletRequest</code> object that contains the
* client's request
*
* @param res
* the <code>ServletResponse</code> object that contains the
* servlet's response
*
* @exception ServletException
* if an exception occurs that interferes with the servlet's
* normal operation
*
* @exception IOException
* if an input or output exception occurs
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException; /**
* 获取Servlet基本信息
*
* @return a <code>String</code> containing servlet information
*/
public String getServletInfo(); /**
* 这个方法被调用,表明这个servlet已经不再处理请求,这个方法在多线程环境也只调用一次,调用后,
* service方法将不会再调用
*
* 给一个servlet释放资源的机会,比如文件,还有需要持久化的资源需和内存中的状态同步
*/
public void destroy();
}

GenericServlet

GenericServlet 实现了 Servlet 和 ServletConfig 两个接口,为这两个接口中的部分方法提供了简单的实现。比如该类实现了 Servlet 接口中的 void init(ServletConfig) 方法,并在方法体内调用了内部提供了一个无参的 init 方法,子类可覆盖该无参 init 方法。除此之外,GenericServlet 还实现了 ServletConfig 接口中的 getInitParameter 方法,用户可直接调用该方法获取到配置信息。而不用先获取 ServletConfig,然后再调用 ServletConfig 的 getInitParameter 方法获取

GenericServlet 是一个协议无关的 servlet,是一个比较原始的实现,通常我们不会直接继承该类。一般情况下,我们都是继承 GenericServlet 的子类 HttpServlet,该类是一个和 HTTP 协议相关的 Servlet。那下面我们来看一下这个类

HttpServlet

HttpServlet,从名字上就可看出,这个类是和 HTTP 协议相关。该类的关注点在于怎么处理 HTTP 请求,比如其定义了 doGet 方法处理 GET 类型的请求,定义了 doPost 方法处理 POST 类型的请求等。我们若需要基于 Servlet 写 Web 应用,应继承该类,并覆盖指定的方法。doGet 和 doPost 等方法并不是处理的入口方法,所以这些方法需要由其他方法调用才行。其他方法是哪个方法呢?对,在Servlet接口定义处理请求的方法service中。当然在HttpServlet中还有一些处理缓存的逻辑

请求分发中央-DispatcherServlt

在认识它之前,介绍一下它在家族中的地位

对于Servlet相关的类和接口上面已经介绍过了,这里多了一些以Aware结尾的接口,是Spring IOC中的接口,

HttpServletBean

重写了GenericServlet中的init()方法,设置servlet配置的初始化参数

FrameworkServlet

FrameworkServlet 是 Spring Web 框架中的一个基础类,该类会在初始化时创建一个容器。同时该类覆写了 doGet、doPost 等方法,并将所有类型的请求委托给 doService 方法去处理。doService 是一个抽象方法,需要子类实现。

servlet被实例化后,会调用GenericServlet的init(ServletConfig config)方法,然后会无参init()方法,HttpServletBean重写了init()方法,这个方法会调用交给子类去实现的initServletBean()方法,现在就到了FrameworkServlet中的initServletBean(),该方法会调用initWebApplicationContext(),这里创建多个容器我,他们之间的关系后面我们再说

DispatcherServlet

DispatcherServlet就是核心的一个组件,来负责协调各个组件,同事初始化这些组件如,HanderMapping,HanderAdapter等。

中央处理器DispatcherServlet

我们继续从上面的FrameworkServlet调用的doService方法入手

/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request); // Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
} // Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
} 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);
}
}
}
}

doService中就是给request设置一些参数,然后调用doDispatch()方法。doDispatch()才是真正处理请求的方法

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// 这个对象包含HnadlerInterceptor集合和Handler对象
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
// 检查是否是Multipart request,如果是并且有MultipartResolver,则返回MultipartResolver处理后的request
processedRequest = checkMultipart(request);
// 是否被MultipartResolver处理
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
// 从HanderMapping获取HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
// 获取可执行处理器逻辑的适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
// HTTP缓存相关
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;
} // 调用处理器逻辑,对应步骤
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} // 如果 controller 未返回 view 名称,这里生成默认的 view 名称
applyDefaultViewName(processedRequest, mv);
// 执行拦截器 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);
}
// 解析并渲染视图
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);
}
}
}
}

getHander(HttpServletRequest)

	/**
* 遍历所有的HanderMapping,获取一个HanderExecutionChian
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@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

/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
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");
}

processDispatchResult

/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a 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) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

后记

整个请求的流程就走完了,但是对于HandlerMapping和HandlerAdapter,如何渲染视图这块没有详细说,因为现在我也还不太懂,等后面会再写文章来说明。

再DispatcherServlet初始化后,会创建容器和创建组件,这些在本文中也没有说明,篇幅限制就另外写一篇来做解释了。

最新文章

  1. 3-1 Linux文件管理类命令详解
  2. 【助教】Java获取数据库数据展示
  3. JS控制css float属性的用法经验总结
  4. 9. Palindrome Number
  5. 手机低端市场,联发科 vs 高通
  6. LeetCode Day4——Factorial Trailing Zeroes
  7. python 使用xrld
  8. Python对数据库的增删改查
  9. Android Studio 合并分支代码到主干的操作总结
  10. 大批量delete 优化方案
  11. 清除UIWebView缓存
  12. 简述Python入门小知识
  13. C# 编码标准(三)
  14. 收集的一些python基础教学博客
  15. 一次查找Windows Live Writer的VSPaste插件丢失RTF格式信息的经历
  16. linux 文件搜索命令find、-name、-iname、通配符*?、-size、-user、-group、-amin、-cmin、-mmin、-a、-o、-exec/-ok、-inum
  17. Hadoop学习之路(五)Hadoop集群搭建模式和各模式问题
  18. m2014-architecture-imgserver-&gt;配置lighttpd mod_mem_cache 模块做静态资源服务器
  19. css3 伪元素和伪类选择器详解
  20. MQTT的学习之Mosquitto安装&amp;使用(1)

热门文章

  1. python3 f-string格式化字符串的高级用法
  2. nginx + tomcat 单个域名及多个域名的配置
  3. spring boot 中使用swagger
  4. HDU分拆素数和
  5. JVM调优工具锦囊
  6. 《剑指offer》面试题30. 包含min函数的栈
  7. 【Java】GUI实现贪吃蛇
  8. C# Reflection反射机制
  9. java类的反射机制
  10. golang中sha256、md5加密,base64encode、base64decode