Spring MVC 介绍

Spring MVC 介绍

为什么不用 HttpServlet

  1. 只能一个类处理一个请求
  2. 请求处理比较繁琐
  3. 视图响应不统一

SpringMVC流程

springmvc

响应视图,JSP视图解析器是基于转发的机制

文字版介绍

核心请求总控器(DispatcherServlet 中央调度器)

SpringMVC的核心控制器 负责拦截所有请求,并把请求转发给 HandlerMapping 去找到对应的 Controller,但是不能直接调用所以使用 HandleAdaptor 调用 Controller,Controller 执行后返回 ModelAndView 对象给 ViewReslver 解析成 View 对象,最后把 View 返回给浏览器。

DispatcherServlet 中的模板方法,负责创建 servlet

/**
 * 初始化servlet使用的策略对象。
 * <p>可能在子类中被重写,以便初始化更多的策略对象。
 */
protected void initStrategies(ApplicationContext context) {
    // 初始化该类使用的MultipartResolver。
    // 如果在这个命名空间的BeanFactory中没有使用给定的名称定义bean,则不提供多部分处理
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

HandlerMapping 和 HandlerAdapter

HandlerMapping:负责解析带着 @ReqeustMapping 注解的方法以及类信息
接收请求时解析并判断是否有对应的 @ReqeustMapping 注解的方法,如果有返回到 DispatcherServlet 由它

当xml配置了或者配置类中配置了 @EnableWebMvc 注解时,spring会自动装配RequestMappingHandlerMapping(请求映射处理器)RequestMappingHandlerAdapter(请求处理适配器)这两个类。

RequestMappingHandlerMapping:负责解析带有@ReqeustMapping注解的方法以及类信息,并在请求到达时找到相应的HandlerMethod(一个JavaBean,封装了请求处理方法、参数信息、类信息以及IOC容器等重要的内容)。当找到相应的HandlerMethod后,如果程序中有定义拦截器,那么就会将这个HandlerMethod封装到HandlerExecutionChain的类中,这个类包含了一个拦截器的集合和一个HandlerMethod的对象。最后将这个chain返回给DispatcherServlet。DispatcherServlet从这个HandlerExecutionChain中取出HandlerMethod来匹配相应的HandlerAdapter,找到合适的可以调用HandlerMathod的请求处理适配器。接着DispatcherServlet负责调用HandlerExecutionChain中的所有拦截器中的预处理方法,如果预处理方法没有任何问题,那么就将HandlerMethod交给HandlerAdapter去调用。

RequestMappingHandlerAdapter:DispatcherServlet将HandlerMethod传递给HandlerAdapter,由它负责调用HandlerMethod(也就是目标控制器的方法)。调用时还会使用具体的MethodArgumentResolver(方法参数解析器,RequestMappingHandlerAdapter内部会初始化一系列默认的HandlerMethodArgumentResolver)将请求中的参数解析为请求处理方法所需要的具体类型参数。最后将Controller方法返回的ModelAndView一并返回到DispatcherServlet中。接着DispatcherServlet会继续执行所有拦截器中的后置处理方法。

ViewResolver

springmvc内部提供了许多视图解析器用于解析不同的视图对象,最长见的有InternalResourceViewResolver(内部资源视图解析器)、FreeMarkerViewResolver(模板引擎视图解析器)等。

InternalResourceViewResolver:在DispatcherServlet接收到HandlerAdapter返回的ModelAndView之后,DispatcherServlet将这个ModelAndView交给指定InternalResourceViewResolver来进行视图解析,InternalResourceViewResolver会根据ModelAndView的视图名称来创建一个InternalResourceView的视图对象返回到DispatcherServlet。由DispatcherServlet去调用视图对象的渲染方法来响应视图。在渲染完视图之后,DispatcherServlet会执行所有拦截器中的after方法。

View

视图对象是由相应的视图解析器解析出来的,Spring也提供了不同的视图对象来完成不同的视图响应工作,常见的有的InternalResourceView(内部资源转发视图)等。

InternalResourceView:这个视图对象会将ModeAndView中而外带的数据放入请求作用域,以及获取到拼接好的转发地址。并提供一个renderMergedOutputModel渲染方法由DispatcherServlet调用,这个方法就是负责具体的url转发工作。

配置

maven依赖

<dependencies>
    <!-- springmvc依赖,会将spring的核心包一并依赖进来 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- war插件,用于导出war包 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.2</version>
            <!-- 设置web.xml的位置 -->
            <configuration>
                <warSourceDirectory>web</warSourceDirectory>
                <webXml>web/WEB-INF/web.xml</webXml>
            </configuration>
        </plugin>
    </plugins>
</build>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 核心请求总控器(DispatcherServlet 中央调度器),负责接收所有的请求,并根据映射的url地址将请求分发给具体控制器的方法来处理-->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- springmvc默认会从WEB-INF目录下查找
             名为[servletName(自定义)]-servlet.xml的
             配置文件,这是spring官方默认的约定命名。
             如果想要自定义文件名并且想存放在其他目录下
             则需要通过contextConfigLocation初始化参数来配置,
             例如:自定义一个springmvc.xml配置文件放在resources
             目录下
        -->
        <!--<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

[servletName(自定义)]-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 启用注解扫描-->
    <context:component-scan base-package="项目包"/>
    <!-- 启用mvc注解处理器。这个注解驱动注册了RequestMappingHandlerMapping(请求映射处理器)和一个RequestMappingHandlerAdapter(请求处理适配器),同时提供了@ReqeustBody、@ResponseBody注解支持、数据绑定等支持 -->
    <mvc:annotation-driven/>

    <!-- 配置视图解析器,springmvc支持多种视图,不同的视图由不同的视图解析器来解析。
         例如:想要使用JSP作为视图,那么就需要配置 InternalResourceViewResolver这个试图解析器,用于解析内部的JSP资源-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 设置jsp资源的前缀,用于指定jsp存放的目录 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 设置jsp资源的后缀名,以".jsp"结尾的文件-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

请求注解

@RequestMapping

使用的前提是处理器被 Spring容器 管理

在方法上

使用方式:
@RequestMapping("/请求")
value 属性为请求,method 属性为请求方式 支持数组
@RequestMapping(value = "/请求",method = RequestMethod.请求方式)
@RequestMapping(value = "/请求",method = {RequestMethod.POST,RequestMethod.GET})

在类上

@RequestMapping("/命名空间")
用法是在有多个相同的子请求是用于区分不同的请求,请求格式为(/命名空间/请求)

接收数据

@RequestParam("")
vaule属性 指定接收数据名字

SpringMVC 4.0提供的子注解(@GetMapping和@PostMapping)

从springmvc4.0开始,提供了新的子注解来替换@ReqeustMapping
不能指定请求方式,可以指定请求路径
因为里面指定了请求方式
file

响应注解

@ResponseBody

表示将方法返回值,以输出流的方式写回客户端,这样 springmvc 就会将序列化好的 json 数据放入响应体中并写回

@RestController

这个注解是在 spring4.0 后新加入的一个注解,同样用于标注为控制器的组件,如果当前 Controller 中所有请求方法都需要使用 @RestController 注解来响应,那么就可以使用它标注在类上,而不需要在每一个方法上标注 @ResponseBody

静态资源的处理(访问jsp只能转发)

Servlet容器(Tomcat):
DefavltServlet(默认):静态文件的处理
JspServlet:JSP的引擎
自定义Servlet(继承HttpServlet):处理 Http 请求

为什么需要配置静态资源,因为核心控制器(使用/或者/*的情况)对所有请求(静态资源)进行了拦截
而核心控制器并没有对静态资源进行解析,对 DefavltServlet 进行了覆盖
自定义Servlet 优先级最高,自定义Servlet 又没有处理静态资源

两种解决方式

  1. 将静态资源交由给容器的默认Servlet来处理,SpringMVC不参与解析,常见的 Servlet 的容器如Tomcat、Jetty等都会有一个自带的DefaultServlet了处理这些静态资源
    <mvc:default-servlet-handler/>
  2. 静态资源由 SpringMVC 自己来处理
    mapping 属性:用于映射静态资源的虚拟url
    location 属性:用于指定静态资源的本地相对路径
    例如:下面的配置中,当以page为开头的所有请求,都会映射到 static 这个目录中去查找相应的静态资源文件
    <mvc:resources mapping="/page/**" location="/static/"/>

笔记

视图响应