Spring MVC


一、Spring MVC 功能

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

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

1. Servlet 处理流程

  1. 请求Servlet。
  2. 处理业务逻辑。
  3. 设置业务Model。
  4. Forward Jsp Servlet。
  5. Jsp Servlet 解析封装 html,并返回。
    在这里插入图片描述

2. MVC 处理流程

  1. URL映射。
  2. 表单参数映射。
  3. 调用目标Controller。
  4. 数据模型映射。
  5. 视图解析。
  6. 异常处理。
    在这里插入图片描述

二、Spring MVC 开始


1. POM

<!-- servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

<!-- webmvc -->
<dependency>
 	<groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>

2. web.xml

<!-- 全局调度器。-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!-- `mvc`配置路径。-->
        <param-name>contextConfigLocation</param-name>
        <!-- classpath*:/spring-mvc.xml -->
        <param-value>
            classpath:/spring-mvc.xml
        </param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- 这里不要写`/*`-->
    <url-pattern>/</url-pattern>
</servlet-mapping>

3. Controller

public class SimpleController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView("/WEB-INF/page/user_view.jsp");
        modelAndView.addObject("name", "骑士");
        return modelAndView;
    }
}

4. user_view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户页面</title>
</head>
<body>
登录人:${name}
</body>
</html>

5. spring-mvc.xml

<!-- `BeanNameUrlHandlerMapping`基于 IOC name 中 "/"开头的Bean 进行注册至映射。-->
<bean name="/hello.do" class="com.qs.springmvcfast.controller.SimpleController"/>

三、Spring MVC 组件

  • MVC 组件在这里插入图片描述

1. HandlerMapping—处理映射

  • URI与控制器的映射
  1. 其为 MVC 中 URI路径 与 Controller对象 的映射。
  2. DispatcherServlet 就是基于 此组件 来寻找对应的 Controller。
  3. 如果找不到就会报 No mapping found for HTTP request with URI 的异常。

  1. HandlerMapping 作用是通过 URI 找到对应的 Handler。
  2. 但其 HandlerMapping.getHandler() 方法并不会直接返回 Handler对像,而是返回 HandlerExecutionChain对象,再通过 HandlerExecutionChain.getHandler() 返回最终的 Handler。
    在这里插入图片描述

  • HandlerMapping 接口结构在这里插入图片描述

  • SimpleUrlHandlerMapping:基于 手动配置URI 与 Controller 映射。
  • BeanNameUrlHandlerMapping:基于 IOC beanName 中 "/"开头的Bean 进行注册至映射。
  • RequestMappingHandlerMapping:基于 @RequestMapping注解 配置对应映射。
    在这里插入图片描述

1.1 SimpleUrlHandlerMapping—简单
<!--
二、`SimpleUrlHandlerMapping`:基于`手动配置URI`与`Controller`映射。
-->

<!-- Url映谢器。-->
<bean name="simpleController" class="com.qs.springmvcfast.controller.SimpleController"/>
<bean name="simpleController2" class="com.qs.springmvcfast.controller.SimpleController"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <!-- 属性注入。-->
    <property name="urlMap">
        <props>
            <!-- `http://localhost:8080/hello1.do` -->
            <prop key="/hello1.do">
                simpleController
            </prop>
            <!-- `http://localhost:8080/hello2.do` -->
            <prop key="/hello2.do">
                simpleController2
            </prop>
        </props>
    </property>
</bean>

  • SimpleUrlHandlerMapping 接口结构
    在这里插入图片描述

  • SimpleUrlHandlerMapping 初始化流程。
  1. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap
  2. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationContext
  3. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers
  4. org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()
  1. /:表示根Handler。
  2. /*:表示默认Handler。

  • 获取 Handler 流程。
  1. org.springframework.web.servlet.DispatcherServlet#doService
  2. org.springframework.web.servlet.DispatcherServlet#doDispatch
  3. org.springframework.web.servlet.DispatcherServlet#getHandler
  4. org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
  5. org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
  6. org.springframework.web.util.UrlPathHelper#getPathWithinApplication
    获取 URI 路径。
  7. org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler
    查找 Handler。
  8. org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
    封装执行链。

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

  • BeanNameUrlHandlerMapping 接口结构
    在这里插入图片描述

<!--
一、`BeanNameUrlHandlerMapping`:基于`IOC beanName`中`"/"开头的Bean`进行注册至映射。
默认配置`spring-webmvc-4.3.8.RELEASE.jar!\org\springframework\web\servlet\DispatcherServlet.properties`
-->

<!-- 1、`Controller`接口。-->
<bean name="/hello.do" class="com.qs.springmvcfast.controller.SimpleController"/>

<!-- 2、`HttpRequestHandler`接口。-->
<bean name="/hello2.do" class="com.qs.springmvcfast.controller.SimpleRequestHandler"/>

<!-- 3、`HttpServlet`接口。-->
<!-- 3.1、配置控制器。-->
<bean id="/hello3.do" class="com.qs.springmvcfast.controller.SimpleServlet"/>
<!-- 3.2、配置适配器。-->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>

<!-- 4、`RequestMapping`注解。-->
<!-- 4.1、包扫描。-->
<context:component-scan base-package="com.qs.springmvcfast.controller"/>
<!-- 4.2、注解驱动。-->
<mvc:annotation-driven/>

1.3 RequestMappingHandlerMapping—注解
  • 基于注解实现。

2. Handler—类型

  • 在 AbstractUrlHandlerMapping类 可以看到存储 Handler的Map 值类型是 Object。
  • 意味着所有的类都可以做来 Handler 来使用。
    在这里插入图片描述

  • Controller 接口。
  • HttpRequestHandler 接口。
  • HttpServlet 接口。
  • @RequestMapping 方法注解。
    在这里插入图片描述
  • 可以看出 Handler 没有统一的接口,当 DispatchServlet 获取当对应的 Handler 之后如何调用呢?调用其哪个方法?这里有两种解决办法。
  1. 一是用 instanceof 判断 Handler 类型然后调用相关方法。
  2. 二是通过 引入适配器 实现,每个 适配器 实现对指定 Handler 的调用。Spring 采用后者

3. HandlerAdapter—处理适配器

  • Spring MVC 采用 适配器模式 来适配调用指定 Handler。
  • 根据 Handler 的不同种类,采用不同的 Adapter。

  • HandlerAdapter 接口方法。
    在这里插入图片描述

  • HandlerAdapter 接口结构
    在这里插入图片描述

3.1 Handler 与 HandlerAdapter 对应关系
Handler类别对应适配器说明
Controller 接口SimpleControllerHandlerAdapter标准控制器,返回 ModelAndView
HttpServlet 接口SimpleServletHandlerAdapter基于标准的 Servlet 处理
HttpRequestHandlerHttpRequestHandlerAdapter业务自行处理请求,不需要通过 ModelAndView 转到视图
HandlerMethodRequestMappingHandlerAdapter基于 @RequestMapping 对应方法处理

3.2 SimpleServletHandlerAdapter 标准控制器
<!-- 3、`HttpServlet`接口。-->
<!-- 3.1、配置控制器。-->
<bean id="/hello3.do" class="com.qs.springmvcfast.controller.SimpleServlet"/>
<!-- 3.2、配置适配器。-->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>

/**
 * @author wy
 * describe 标准Servlet。
 */
public class SimpleServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
        resp.getWriter().println("Hello SimpleServlet!");
    }
}

  • 当 IOC 实例化这些类之后,DispatcherServlet 就会通过 org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应 Handler适配器,如果找不到就会报异常 No adapter for handler

4. ViewResolver—视图解析器

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

4.1 BeanNameViewResolver
<!-- 四、配置视图解析器。-->
<bean name="simpleView" class="com.qs.springmvcfast.view.SimpleView"/>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>

/**
 * @author wy
 * describe 自定义视图处理器。
 */
public class SimpleView implements View {

    @Override
    public String getContentType() {
        return null;
    }

    @Override
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        httpServletResponse.getWriter().print("Hello View!");
    }
}

5. View—视图

  • 基于 ViewResolver.resolveViewName() 获取对应 View 来解析生成 html 并返回。

  • View 接口结构
    在这里插入图片描述

<!-- 默认资源解析器。-->
<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.InternalResourceView"/>
</bean>

<!-- 视图仓库。-->
<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>

public class SimpleController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//        ModelAndView modelAndView = new ModelAndView("/WEB-INF/page/user_view.jsp");
        ModelAndView modelAndView = new ModelAndView("myView");
        modelAndView.addObject("name", "骑士");
        return modelAndView;
    }
}

6. HandlerExceptionResolver—异常处理器

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

  • HandlerExceptionResolver 接口结构
    在这里插入图片描述
  1. DefaultHandlerExceptionResolver:【默认】Spring mvc 默认异常处理。
  2. ResponseStatusExceptionResolver:【默认】用于解析带 @ResponseStatus 的自定义异常。
  3. SimpleMappingExceptionResolver:异常映射,将 指定异常 与 错误页面 相对应。

6.1 DefaultHandlerExceptionResolver—默认
<!-- 五、配置异常处理器。-->
<!-- 1、自定义异常处理器。-->
<bean class="com.qs.springmvcfast.exception.SimpleHandlerExceptionResolver"/>
<!-- 2、响应状态异常处理器(默认存在)。-->
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver"/>

public class SimpleHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        return new ModelAndView("error");
    }
}

6.2 SimpleMappingExceptionResolver—简单
<!-- 3、`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"/>
            <!-- IllegalArgumentException 是 RuntimeException 子类。-->
            <entry key="java.lang.IllegalArgumentException" value="argumentError"/>
        </map>
    </property>
</bean>

7. HandlerInterceptor—拦截器

  • HandlerInterceptor 用于对请求拦截,与 原生Filter 区别在于 Filter 只能在业务执行前拦截,而 HandlerInterceptor 可以在业务 处理 前、中、后 进行处理。
  • 实现机制是基于 HandlerExecutionChain 分别在 doDispatch 方法中执行以下方法。
  1. preHandle:业务处理前执行。
  2. postHandle:业务处理后(异常则不执行)。
  3. afterCompletion:视图处理后。
  • 逻辑源码 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法。

<!-- 六、配置拦截器`Interceptor`组件。-->
<bean name="simpleHandlerInterceptor" class="com.qs.springmvcfast.interceptor.SimpleHandlerInterceptor"/>

public class SimpleHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.printf("`%s`.preHandle", this.getClass().getName()).println();
        // false拦截。
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.printf("`%s`.postHandle", this.getClass().getName()).println();
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.printf("`%s`.afterCompletion", this.getClass().getName()).println();
    }
}

四、Spring MVC 执行流程

在这里插入图片描述


五、DispatchServlet 初始化流程


1. 创建 WebApplicationContext

  1. org.springframework.web.servlet.HttpServletBean#init
  2. org.springframework.web.servlet.FrameworkServlet#initServletBean
  3. org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
  4. org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)
    基于当前存在的 Spring 上下文做为 Root,创建 MVC 上下文。
  5. org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
  6. org.springframework.context.support.AbstractApplicationContext#refresh

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


六、RequestMapping 注解

<!-- 4、`RequestMapping`注解。-->
<!-- 4.1、包扫描。-->
<context:component-scan base-package="com.qs.springmvcfast.controller"/>
<!-- 4.2、注解驱动。-->
<mvc:annotation-driven/>

/**
 * http://localhost:8080/anno/hello.do
 * `org.springframework.core.ParameterNameDiscoverer`参数封装。
 */
@RequestMapping("/hello.do")
public ModelAndView hello(String name) {
    System.out.printf("`%s`.hello", this.getClass().getName()).println();
    ModelAndView modelAndView = new ModelAndView("user_view");
    modelAndView.addObject("name", "骑士");
    return modelAndView;
}

  • 为什么基于 <mvc:annotation-driven/> 配置就能实现 MVC 的整个配置了,之前所提到的 handlerMapping 与 handlerAdapter 组件都不适用了?
  1. 认识 NamespaceHandler 接口。
  2. 查看 MvcNamespaceHandler。
  3. 查看 AnnotationDrivenBeanDefinitionParser。
  • 结论:在 <mvc:annotation-driven /> 对应的解析器,自动向 IOC 里面注册了两个 BeanDefinition。
    分别是 RequestMappingHandlerMapping 与 BeanNameUrlHandlerMapping 组件。

1. 实现结构

在这里插入图片描述

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

2. 执行流程

  • 映射流程。
  1. org.springframework.web.servlet.DispatcherServlet#getHandler
    基于注解查找映射Api。
  2. org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
  3. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
  4. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl

  • 调用流程。
  1. org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
  2. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
  3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
  4. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
  5. org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑士梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值