SpringMVC运行机制详解——学好基本功,走路好轻松
说明:了解SpringMVC运行机制,有利于加深对SpringMVC框架的理解、在开发过程中能够快速定位到问题所在。同时对搭建SSM项目基础配置更加理解。总之,好处多多;
SpringMVC基本概念
SpringMVC框架:属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面(来自百度百科)。总而言之,SpringMVC框架属于Spring架构中的组成部分。在实际开发中,它主要负责HTTP请求接收、数据处理、请求响应等web层功能实现;故SpringMVC也称为Web层框架;它的设计架构是基于MVC架构。
MVC架构概念
M(Model)模型、V(View)视图、C(Controller)控制器三层分离的软件设计思想;主要目的就让M模型层和V视图层进行分离,只专注实现自己的功能。它们之间的交互的通过C控制器层进行协调交。MVC分层的设计思想降低了代码之间的耦合,有利于代码维护。MVC架构图如下所示:
耦合度:是指业务代码之间相互依赖、不同功能代码分层不明确。杂糅在一起,类似于的线团,如下图所示:左图就是耦合度高的、有图就是耦合度低的;
SpringMVC基本结构
SpringMVC好比一辆车,由很多部件一起结合组成SpringMVC框架。组成SpringMVC部件主要有DispatcherServlet(分发器)、HandlerMapping(处理映射器)、HandlerAdapter(处理适配器)、ViewResolver(视图解析器)等主要部件组成。
- DispatcherServlet(分发器):它属于SpringMVC核心,好比汽车的发动机的重要性。主要用来接收请求、相应结果、调度其他组件的处理/响应请求。由于它的存在,降低了各个组件之间的耦合;
- HandlerMapping(处理映射器):根据请求的URL去匹配程序中以“配置/注解”的方式配置的Handler,然后返回Handler处理器链。
- HandlerAdapter(处理适配器):根据规则去执行相应的Handler处理器,返回的是处理器处理后的结果;
- ViewResolver(视图解析器):将适配器返回的结果ModelAndView,解析成相应的View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
总结:以上主要组件都是围绕着DispatcherServlet(分发器)进行调度的。它们之间不能直接交互。SpringMVC流程图如下所示:
SpringMVC源码分析(看不懂记住上面就够了,等知识储备多了再学习)
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
建议用Idea查看代码,方便的不要不要的。用ctrl+鼠标单击DispatcherServlet进入源码文件;先来看DispatcherServlet源码继承/实现关系图。如下图所示:
说明:以下根据源码分析DispatcherServlet运行机制流程,不会很具体讲解各个方法的作用;
第一问:DispatcherServlet怎样得到用户的HTTP请求?
从上图可知,DispatcherServlet继承了HttpServlet,用过struts的都知道HtppServlet的作用。它是用处理HTTP请求的。具体自行百度。HttpServlet.class源码方法汇总如下:
注意红线标记的地方。类DispatcherServlet继承了抽象类FrameworkServlet。并且在抽象类FrameworkServlet中覆盖了HttpServlet中的service方法。覆盖方法源码如下:
/**
*FrameworkServlet.class源码(行数:432——440)
**/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
super.service(request, response);
} else {
this.processRequest(request, response);
}
}
这说明了当HTTP请求过来时,会执行以上方法,如果HTTP请求正常,则调用HttpServlet类中service方法。判断是否请求类型,然后回调FrameworkServlet抽象类中的doGet/doPost方法。最终执行processRequest方法。processRequest源码如下图所示:
注意图标记的地方;doService是抽象类FrameworkServlet中的抽象方法。并且在子类DispatcherServlet中实现了。
总结:到这为止,知道了DispatcherServlet是通过继承抽象类FrameworkServlet,抽象类FrameworkServlet重写了HttpServlet类的service方法得到HTTP请求;
第二问:HandlerMapping、HandlerAdapte和ViewResolver是怎样初始化?
先看HttpServletBean.class类中init方法源码。如下所示:
/**
* HttpServletBean.class中66-85行
**/
public final void init() throws ServletException {
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
this.initServletBean();
}
类HttpServletBean中的init()重写了类servlet中的init方法。init 方法的执行时刻跟servlet 配置中的load-on-startup标签有关,如下图示所示:
如果值大于等于 0,则在 Servlet 实例化的时候执行,间隔时间由具体的值决定,值越大,则越迟执行。如果小于 0 或者没有配置,则在第一次请求的时候才同步执行 (该方法只执行一次);
其中init方法中涉及到了BeanWrapper,PropertyValues,ResourceLoader,initServletBean方法
- PropertyValues:获取Web.xml里面的servlet的init-param;
- BeanWrapper:封装了bean的行为,提供了设置和获取属性值,它有对应的BeanWrapperImpl;
- ResourceLoader:接口仅有一个getResource(String location)的方法,可以根据一个资源地址加载文件资源。classpath:这种方式指定SpringMVC框架bean配置文件的来源;
- initServletBean():在HttpServletBean类只是提供了参考,具体实现实在子类FrameworkServlet中。源码如下:
initServletBean方法调用了initWebApplicationContext方法。用来初始化springMVC上下文环境。同时通过synchronized关键同步执行onRefresh()方法。源码如下:
onRefresh方法在FrameworkServlet只是提供参考,具体实现实在子类DispatcherServlet中。源码如下:
onRefresh方法中调用了initStrategies方法。在initStrategies初始化了SpringMVC的组件。
//初始化上传文件解析器
initMultipartResolver(context);
//初始化本地解析器
initLocaleResolver(context);
//初始化主题解析器
initThemeResolver(context);
//初始化映射处理器
initHandlerMappings(context);
//初始化适配器处理器
initHandlerAdapters(context);
//初始化异常处理器
initHandlerExceptionResolvers(context);
//初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
总结:目前分析的源码中只涉及了3个类,分别是:FrameworkServlet、DispatcherServlet、HttpServletBean。它们各自做了什么事情?
- HttpServletBean是主要是获取web.xml配置文件中的标签的值;
- FrameworkServlet主要实现了初始化SpringMVC上下文环境;
- DispatcherServlet主要实现了SpringMVC各个组件的初始化;