目录
Spring MVC 请求处理流程
流程说明
框架设计优点:
基于web容器的设计
配置元素读取
容器上下文的建立
开闭原则:
可扩展的handler
拦截器
适配器
参考文章链接:
注明:本文章为看了几位大神文章的学习总结,参考文章链接在最下面
1)用户发送请求到前端控制器 DispatcherServlet
2)DispatcherServlet 接受到用户发起的请求后调用 HandlerMapping 映射处理器
3)映射处理器根据请求的 url 找到具体的 Handler(后端控制器),生成处理器对象以及处理器拦截器(有就生成)并将 Handler 返回给 DispatcherServlet
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器去调用 Handler
5)处理器适配器执行 Handler
6)Handler 处理完毕后返回 ModelAndView 给处理器适配器
7)处理器适配器将 ModelAndView 返回给前端控制器 DispatcherServlet
8)前端控制器 DispatcherServlet 请求视图解析器 ViewResolver 解析视图
9)视图解析器将解析后的视图 View 返回给前端控制器 DispatcherServlet
10)前端控制器 DispatcherServlet 对视图渲染,就是将数据模型填充到 request 域中
11)前端控制器 DispatcherServlet 想用户响应结果
spring mvc是基于web容器的设计,继承spring一脉,可以有效的借助web容器的servlet的能力,转发HTTP请求到spring mvc框架层,借助spring的bean管理,配置元素读取等能力
DispatcherServlet类的初始化入口方法init()定义在HttpServletBean这个父类中,HttpServletBean类作为一个直接继承于HttpServlet类的类,覆写了HttpServlet类的init()方法,实现了自己的初始化行为。
@Override
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, this.environment));
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");
}
}
这里的initServletBean()方法在HttpServletBean类中是一个没有任何实现的空方法,它的目的就是留待子类实现自己的初始化逻辑,也就是我们常说的模板方法设计模式。SpringMVC在此生动的运用了这个模式,init()方法就是模版方法模式中的模板方法,SpringMVC真正的初始化过程,由子类FrameworkServlet中覆写的initServletBean()方法触发。
说白了就是读取配置文件,并将配置文件内容构造成对象
HttpServletBean这个类的设计中,运用了依赖注入思想完成了
SpringMVC使用了Spring容器来容纳自己的配置元素,拥有自己的bean容器上下文。在SpringMVC初始化的过程中,非常关键的一步就是要建立起这个容器上下文,而这个建立上下文的过程,发生在FrameworkServlet类中,由上面init()方法中的initServletBean()方法触发。
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
initFrameworkServlet()方法是一个没有任何实现的空方法,FrameworkServlet这个类,在SpringMVC类体系中的设计目的,它是 用来抽离出建立 WebApplicationContext 上下文这个过程,并将其注册到ServletContext中的。跳脱开SpringMVC体系,我们也能通过继承FrameworkServlet类,得到与Spring容器整合的好处,FrameworkServlet和HttpServletBean一样,是一个可以独立使用的类。
开闭原则是一个很宽泛的原则,具体体现到DispatcherServlet的源码中,我们可以大致摸得到一些线索:
在框架代码中,抽象接口和上层行为,具体的行为可以由子类实现,实现更好的可扩展性
当DispatcherServlet接收到web请求后,由标准Servlet类处理方法doGet或者doPost,经过几次转发后,最终注册在DispatcherServlet类中的HandlerMapping实现类组成的一个List(有点拗口)会在一个循环中被遍历。以该web请求的HttpServletRequest对象为参数,依次调用其getHandler方法,第一个不为null的调用结果,将被返回。
/**
* Return the HandlerExecutionChain for this request.
* Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or null
if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
一个web请求经过处理后,会得到一个HandlerExecutionChain对象,这就是SpringMVC对URl映射给出的回答。需要留意的是,HandlerMapping接口的getHandler方法参数是HttpServletRequest,这意味着,HandlerMapping的实现类可以利用HttpServletRequest中的 所有信息来做出这个HandlerExecutionChain对象的生成”决策“。这包括,请求头、url路径、cookie、session、参数等等一切你从一个web请求中可以得到的任何东西(最常用的是url路径)。
SpirngMVC的第一个扩展点,就出现在这里。我们可以编写任意的HandlerMapping实现类,依据任何策略来决定一个web请求到HandlerExecutionChain对象的生成。
public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;
private List interceptorList;
public HandlerExecutionChain(Object handler) {
this(handler, null);
}
}
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
一个实质执行对象handler,还有一堆拦截器
HandlerExecutionChain整个执行脉络也就清楚了:在真正调用其handler对象前,HandlerInterceptor接口实现类组成的数组将会被遍历,其preHandle方法会被依次调用,然后真正的handler对象将被调用。
handler对象被调用后,就生成了需要的响应数据,在将处理结果写到HttpServletResponse对象之前(SpringMVC称为渲染视图),其postHandle方法会被依次调用。视图渲染完成后,最后afterCompletion方法会被依次调用,整个web请求的处理过程就结束了。
HandlerInterceptor,是SpringMVC的第二个扩展点的暴露,通过自定义拦截器,我们可以在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情。
/**
* 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 {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: Does your handler implement a supported interface like Controller?");
}
HandlerExecutionChain中的handler对象会被作为参数传递进去,在DispatcherServlet类中注册的HandlerAdapter实现类列表会被遍历,然后返回第一个supports方法返回true的HandlerAdapter对象,用这个HandlerAdapter实现类中的handle方法处理handler对象,并返回ModelAndView这个包含了视图和数据的对象。HandlerAdapter就是SpringMVC提供的第三个扩展点,你可以提供自己的实现类来处理handler对象。
Web on Servlet Stack 官网
SpringMVC工作原理 - 平凡希 - 博客园 SpringMVC工作原理
https://blog.csdn.net/lu1005287365/article/details/52287952 SpringMVC工作原理(源码分析)
SpringMVC源码分析系列 - format丶 - 博客园 SpringMVC源码分析系列 里面有入门基础,还有代码解读,建议去看
这篇文章分析的非常到位,本文大部分内容来源于此,这个系列有5篇文章,强烈建议去看
SpringMVC源码剖析(一)- 从抽象和接口说起 - 相见欢的个人空间 - OSCHINA - 中文开源技术交流社区 SpringMVC源码剖析(一)- 从抽象和接口说起
SpringMVC源码剖析(二)- DispatcherServlet的前世今生 - 相见欢的个人空间 - OSCHINA - 中文开源技术交流社区 SpringMVC源码剖析(二)- DispatcherServlet的前世今生
SpringMVC源码剖析(三)- DispatcherServlet的初始化流程 - 相见欢的个人空间 - OSCHINA - 中文开源技术交流社区 SpringMVC源码剖析(三)- DispatcherServlet的初始化流程
SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现 - 相见欢的个人空间 - OSCHINA - 中文开源技术交流社区 SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现
https://my.oschina.net/lichhao/blog/172562 SpringMVC源码剖析(五)-消息转换器HttpMessageConverter