五、Spring MVC主要流程和源码解析

目录

一、spring mvc 功能特性

1、回顾servlet 与jsp 执行过程

2、spring mvc 功能特性 

3、请求处理流程

4、spring mvc 示例 

二、mvc 体系结构详解

1、spring mvc 框架组件

2、HandlerMapping 详解 

3、HandlerAdapter 详解 

4、ViewResolver 与View 详解

三、MVC拦截处理

1、HandlerExceptionResolver 异常处理

2、HandlerInterceptor  调用拦截

四、dispatchServlet 初始化流程​​​

1、创建WebApplicationContext

2、基于策略模型加载各组件


一、spring mvc 功能特性

1、回顾servlet 与jsp 执行过程

流程说明:

  1. 请求Servlet
  2. 处理业务逻辑
  3. 设置业务Model
  4. forward jsp Servlet
  5.  jsp Servlet 解析封装html 返回

2、spring mvc 功能特性 

spring mvc本质上还是在使用Servlet处理,并在其基础上进行了封装简化了开发流程,提高易用性、并使用程序逻辑结构变得更清晰

  1. 基于注解的URL映谢
  2. 表单参数映射
  3. 缓存处理
  4. 全局统一异常处理
  5. 拦截器的实现
  6. 下载处理

3、请求处理流程

4、spring mvc 示例 

为便于理解,这里给出一个最简单,配置最少的spring mvc 示例:

web.xml servlet配置:

<servlet>
    <servlet-name>dispatcherServlet</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>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

编写Control 方法: 

public class SimpleControl implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("/WEB-INF/page/userView.jsp");
        mv.addObject("name", "hello Spring MVC");
        return mv;
    }
}

配置spring-mvc.xml:

<bean name="/hello.do" class="com.zealon.control.SimpleControl"/>

二、mvc 体系结构详解

1、spring mvc 框架组件

从技术角度去思考 任何一个现存的框架都有其存在理由,而这个理由就是解决实际的问题。或者提供更好的解决问题的方案。spring mvc 它解决了什么问题呢?

  1. URL映射
  2. 表单参数映射
  3. 调用目标Control
  4. 数据模型映射
  5. 视图解析
  6. 异常处理

上术解决在spring mvc 中都体现在如下组件当中:

  1. HandlerMapping 'hændlə  'mæpɪŋ  url与控制器的映谢
  2. HandlerAdapter  'hændlə  ə'dæptə 控制器执行适配器
  3. ViewResolver vjuː  riː'zɒlvə 视图仓库
  4. view 具体解析视图
  5. HandlerExceptionResolver  'hændlə  ɪk'sepʃ(ə)n  riː'zɒlvə 异常捕捕捉器
  6. HandlerInterceptor  'hændlə  ɪntə'septə 拦截器

其对应具体uml如下 图:

 mvc 各组件执行流程:

2、HandlerMapping 详解 

其为mvc 中url路径与Control对像的映射,DispatcherServlet 就是基于此组件来寻找对应的Control,如果找不到就会报 No mapping found for HTTP request with URI的异常。

HandlerMapping  作用是通过url找到对应的Handler ,但其HandlerMapping.getHandler()方法并不会直接返回Handler对像,而是返回HandlerExecutionChain对像在通过HandlerExecutionChain.getHandler() 返回最终的handler 

HandlerMapping 接口结构分析:

常用实现类:

目前主流的三种mapping 如下:

  1. SimpleUrlHandlerMapping:基于手动配置 url 与control 映谢
  2. BeanNameUrlHandlerMapping:  基于ioc name 中已 "/" 开头的Bean时行 注册至映谢.
  3. RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映谢

1.SimpleUrlHandlerMapping配置

编写mvc 文件:

<!--简单控制器-->
<bean name="simpleControl" class="com.zealon.control.SimpleControl"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="urlMap">
        <props>
            <prop key="/hello.do">
                simpleControl
            </prop>
        </props>
    </property>
</bean>

SimpleUrlHandlerMapping体系结构:

初始化SimpleUrlHandlerMapping流程关键源码:

>org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap
>org.sontext
>org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers
 // /表示根路径 /* 表示默认路径
>org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()
pringframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationC

获取 Handler流程关键源码:

2.BeanNameUrlHandlerMapping:

BeanNameUrlHandlerMapping 实现上与 SimpleUrlHandlerMapping 一至,唯一区别在于 继承自AbstractDetectingUrlHandlerMapping ,通过对应detectHandlers 可以在无配置的情况下发现url 与handler 映射。

结构图:

 3.RequestMappingHandlerMapping

基于注解配置mvc mapping

<context:component-scan base-package="com.zealon.mvc.control" />
<!-- 注解驱动 -->
<mvc:annotation-driven/>

<!-- 视图仓库 -->
<bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/page/" />
   <property name="suffix" value=".jsp" />
   <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
</bean>
// 注解方法
@RequestMapping("/hello.do")
public ModelAndView hello() {
    ModelAndView mv = new ModelAndView("userView");
    mv.addObject("name", "spring mvc");
    return mv;
}

提问 为什么基于 <mvc:annotation-driven/> 配置就能实现mvc 的整个配置了,之前所提到的 handlerMapping 、与handlerAdapter 组件都不适用了?

只要查看以类的源就可以知晓其中原因:

  1. 认识 NamespaceHandler 接口
  2. 查看 MvcNamespaceHandler
  3. 查看AnnotationDrivenBeanDefinitionParser

具体看这个:

浅析Spring自定义标签的使用 - 简书作者: 一字马胡 转载标志 【2017-11-17】 更新日志 导入 Spring框架的一大强大之处就是框架的设计具有很好的可扩展性,所以只要有想象力,就可以在Spring...https://www.jianshu.com/p/ee199b7ec65b结论

在<mvc:annotation-driven />  对应的解析器,自动向ioc  里面注册了两个BeanDefinition。分别是:RequestMappingHandlerMapping与BeanNameUrlHandlerMapping

实现组成结构

  1. RequestMappingHandlerMapping :URL 映射器
  2. RequestMappingHandlerAdapter:执行适配器
  3. InvocableHandlerMethod:Control目标对象,包含了control Bean 及对应的method 对像,及调用方法
    1.  HandlerMethodArgumentResolverComposite:参数处理器
    2.  ParameterNameDiscoverer:参数名称处理器
    3.  HandlerMethodReturnValueHandlerComposite:返回结构处理器

 调用执行源码解析:

查找mapping源码解析:

基于注解查找 mapping

org.springframework.web.servlet.DispatcherServlet#getHandler
>org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
 >org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
 >org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl

 调用执行过程源码解析

>org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
 >org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal 
 >org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
   >org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
    >org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

3、HandlerAdapter 详解 

Handler 类型

在 AbstractUrlHandlerMapping 我们可以看到存储handler 的Map 值类型是Object ,是否意味着所有的类都可以做来Handler 来使用?

Handler  对应类型如下如图:

  1. Controller 接口:
  2. HttpRequestHandler 接口:
  3. HttpServlet 接口:
  4. @RequestMapping方法注解

可以看出 Handler 没有统一的接口,当dispatchServlet获取当对应的Handler之后如何调用呢?调用其哪个方法?这里有两种解决办法,一是用instanceof 判断Handler 类型然后调用相关方法 。二是通过引入适配器实现,每个适配器实现对指定Handler的调用。

spring mvc 采用适配器模式来适配调用指定Handler,根据Handler的不同种类采用不同的Adapter,其Handler与 HandlerAdapter 对应关系如下:

Handler类别

对应适配器

描述

Controller

SimpleControllerHandlerAdapter

标准控制器,返回ModelAndView

HttpRequestHandler

HttpRequestHandlerAdapter

业务自行处理 请求,不需要通过modelAndView 转到视图

Servlet

SimpleServletHandlerAdapter

基于标准的servlet 处理

HandlerMethod

RequestMappingHandlerAdapter

基于@requestMapping对应方法处理

HandlerAdapter  接口方法 

 HandlerAdapter  接口结构图

基于Servlet 处理  SimpleServletHandlerAdapter

<!-- 配置控制器 -->
<bean id="/hello.do" class="com.zealon.mvc.control.HelloServlet"/>
<!-- 配置适配器 -->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
// 标准Servlet
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("hello Spring MVC ");
    }
}

上述例子中当IOC 中实例化这些类之后 DispatcherServlet 就会通过

org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应handler的适配器 ,如果找不到就会报 如下异常 

javax.servlet.ServletException: No adapter for handler 

4、ViewResolver 与View 详解

找到应的Adapter 之后就会基于适配器调用业务处理,处理完之后业务方会返回一个ModelAndView ,在去查找对应的视图进行处理。其在org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。

BeanNameViewREsolver示例: 

// 添加自定义视图:
public class MyView implements View {
    @Override
    public void render(Map<String, ?> model, HttpServletRequest 
            request, HttpServletResponse response) throws Exception {
        response.getWriter().print("hello Spring MVC.");
    }
}
<!-- 配置视图解析器 -->
<bean name="myView" class="com.tuling.control.MyView"

三、MVC拦截处理

1、HandlerExceptionResolver 异常处理

该组件用于指示 当出现异常时 mvc 该如何处理。 dispatcherServlet 会调用org.springframework.web.servlet.DispatcherServlet#processHandlerException() 方法,遍历 handlerExceptionResolvers 处理异常,处理完成之后返回errorView 跳转到异常视图

HandlerExceptionResolver 结构:

ResponseStatusExceptionResolver(默认): 用于解析带@ResponseStatus的自定义异常

DefaultHandlerExceptionResolver(默认):spring mvc 默认异常处理

SimpleMappingExceptionResolver:异常映射,将指定异常与错误页面相对应SimpleMappingExceptionResolver 示例:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="defaultErrorView" value="error"/>
    <property name="defaultStatusCode" value="500"/>
    <property name="exceptionMappings">
        <map>
            <entry key="java.lang.RuntimeException" value="error"/>
            <entry key="java.lang.IllegalArgumentException" value="argumentError"/>
        </map>
    </property>
</bean>

自定义异常捕捉:

public class SimpleExceptionHandle implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return new ModelAndView("error");
    }
}
<!-- 自定义异常配置 -->
<bean class="com.zealon.mvc.control.SimpleExceptionHandle"/>

2、HandlerInterceptor  调用拦截

HandlerInterceptor   用于对请求拦截,与原生Filter区别在于 Filter只能在业务执行前拦截,而HandlerInterceptor 可以在业务处理前、中、后进行处理。 

演示HandlerInterceptor 

public class SimpleHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
<!--配置interceptor 组件-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.tuling.control.SimpleHandlerInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

其实现机制是基于 HandlerExecutionChain 分别在 doDispatch 方法中执行以下方法:

  1. preHandle :业务处理前执行
  2. postHandle:业务处理后(异常则不执行)
  3. afterCompletion:视图处理后

具体逻辑源码参见:org.springframework.web.servlet.DispatcherServlet#doDispatch 方法

四、dispatchServlet 初始化流程​​​

1、创建WebApplicationContext

源码解析:

​​​​​​​>org.springframework.web.servlet.HttpServletBean#init

>org.springframework.web.servlet.FrameworkServlet#initServletBean

>org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

// 基于当前存在的Spring 上下文做为Root 创建Mvc上下文。 
>org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)

>org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext

>org.springframework.context.support.AbstractApplicationContext#refresh

2、基于策略模型加载各组件

源码解析:

​​​​​​​>org.springframework.web.servlet.HttpServletBean#init

>org.springframework.web.servlet.FrameworkServlet#initServletBean

>org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

>org.springframework.web.servlet.DispatcherServlet#onRefresh(ApplicationContext context)

>org.springframework.web.servlet.DispatcherServlet#initStrategies(ApplicationContext context)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值