lukazan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

SpringMVC技术总结

1、SpringMVC执行流程

  1. 浏览器提交请求到中央调度器
  2. 中央调度器直接将请求转给处理器映射器。
  3. 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
  4. 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
  5. 处理器适配器调用执行处理器。
  6. 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
  7. 处理器适配器直接将结果返回给中央调度器。
  8. 中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。
  9. 视图解析器将封装了的视图对象返回给中央调度器
  10. 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
  11. 中央调度器响应浏览器。

2、依赖,需要添加servlet和spring-webmvc依赖

<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>javax.servlet-apiartifactId>
    <version>3.1.0version>
    <scope>providedscope>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.2.5.RELEASEversion>
dependency>

3、web.xml配置文件

(1) 注册中央调度器

<servlet>
    <servlet-name>dispatcherServletservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    <init-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:spring-mvc.xmlparam-value>
    </init-param>
    <load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
    <servlet-name>dispatcherServletservlet-name>
    <url-pattern>*.htmlurl-pattern>
    <url-pattern>*.jsonurl-pattern>
servlet-mapping>
  1. 全限定性类名

该中央调度器为一个 Servlet,名称为 DispatcherServlet。 中央调度器的全限定性类名在导入的 Jar 文件 spring-webmvc-5.2.5.RELEASE.jar 的第一个包 org.springframework.web.servlet下可找到。
  1. <load-on-startup/>

        在<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时调用执行该 Servlet 的 init()方
法,而不是在真正访问时才创建。它的值必须是一个整数。
    • 当值大于等于 0 时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该 Servlet的优先级就越高, 其被创建的也就越早;
    • 当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
    • 当值相同时,容器会自己选择创建顺序。
  1. 配置文件位置与名称

默认要从项目根下的 WEB-INF 目录下找名称为 "Servlet 名称-servlet.xml "的配置文件。这里的“Servlet 名称”指的是注册中央调度器<servlet-name/>标签中指定的 Servlet 的 name 值。本例配置文件名为dispatcherServlet-servlet.xml。我们通过<param-value>classpath:spring-mvc.xml</param-value>指定配置文件地址。
  1. <url-pattern/>

(2) 配置字符集过滤器

<!--配置字符集过滤器-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--指定字符集-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--强制请求设置字符集-->
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!--强制相应设置字符集-->
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4、SpringMVC配置文件详解

 <!--配置自动扫描的包-->
    <context:component-scan base-package="com.atguigu.crowd.mvc"/>

    <!--配置SpringMVC注解驱动-->
    <mvc:annotation-driven/>

    <!--配置视图解析器,为controller层函数返回视图添加前后缀-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置基于XML的异常映射--> <!--已使用注解方式-->
    <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--配置异常的类型和具体视图页面的对应关系-->
        <property name="exceptionMappings">
            <props>
                <!--key属性指定异常全类名,标签体中写对应的视图名(这个值要拼前后缀得到具体路径)-->
                <prop key="java.lang.Exception">system-error</prop>
                <prop key="com.atguigu.crowd.exception.AccessForbiddenException">admin-login</prop>
            </props>
        </property>
    </bean>

    <!--配置view-controller,直接把请求地址和视图名称关联起来,不必写handler方法了
        @RequestMapping("/admmin/to/login/page.html")
        public String toLoginPage(){
            return "admin-admin";
        }
    -->
    <mvc:view-controller path="/admin/to/login/page.html" view-name="admin-login"/>

    <!--注册拦截器: 使用 SpringSecurity 后当前自定义的登录拦截器不再使用了-->
    <!--注册拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--mvc:mapping配置要拦截的资源-->
            <!-- /* 对应一层路径,比如:/aaa -->
            <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc -->
            <mvc:mapping path="/**"/>

            <!-- mvc:exclude-mapping 配置不拦截的资源 -->
            <mvc:exclude-mapping path="/admin/to/login/page.html"/>
            <mvc:exclude-mapping path="/admin/do/login.html"/>
            <mvc:exclude-mapping path="/admin/do.logout.html"/>

            <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

(1) 视图解析器

  SpringMVC 框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器InternalResouceViewResolver 中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。

(2)注解驱动

  将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由来完成。

5、处理器简介

(1) @Controller:表示当前类为处理器
(2) @RequestMapping:表示当前方法为处理器方法。
  • 属性value:对 value 属性所指定的 URI进行处理与响应。若有多个请求路径均可匹配该处理器方法的执行,value 属性中可以写成一个数组。
  • 属性method: 可以设置为get或post提交。不设置则get和post都可
                                  
(3) 实现处理器的老方式:1.处理器类实现Controller接口;2.配置name对应请求路径,class对应处理请求的类

6、处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • 请求中所携带的请求参数
    • 逐个接收:保证请求参数名与该请求处理方法的参数名相同即可。
    • @RequestParam:指定请求 URL 所携带参数的名称。
    • 对象参数: 将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
    • @PathVariable:获取请求路径中的变量

(1) @RequestParam:

  请求URL所携带的参数名称与处理方法中指定的参数名不相同时,在参数前添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。
  用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。提交方式为get或post。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-orm-urlencoded类型)
    RequestParam实质是将Request.getParameter() 中的Key-Value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段。
  • 属性value:请求参数名
  • 属性required:true表示请求中必须有此参数,false表示可以没有此参数

(2) @RequestBody:

  可以接收json格式的数据,并将其转换成对应的数据类型(对象,map)。将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。
  处理HttpEntity传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据(但是也可以处理)。application/json、application/xml等格式的数据,必须使用@RequestBody来处理。
  GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
  POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
  这里涉及到使用@RequestBody接收不同的对象
  1. 创建一个新的entity,将两个entity都进去。这是最简单的,但是不够“优雅”。
  2. 用Map<String, Object>接受request body,自己反序列化到各个entity中。
  3. 类似方法2,不过更为generic,实现自己的HandlerMethodArgumentResolver。参考https://sadique.io/blog/2016/01/30/using-custom-arguments-in-spring-mvc-controllers/

(3) @ModelAttribute:将参数绑定到Model对象

    使用@ModelAttribute注解的实体类接收前端发来的数据格式需要为"x-www-form-urlencoded"。若是使用@ModelAttribute接收application/json格式,虽然不会报错,但是值并不会自动填入。
    当前台界面使用GET或POST方式提交数据时,数据编码格式由请求头的ContentType指定。分为以下几种情况:
  • application/x-www-form-urlencoded,这种情况的数据@RequestParam、@ModelAttribute可以处理,@RequestBody也可以处理。
  • multipart/form-data,@RequestBody不能处理这种格式的数据。(form表单里面有文件上传时,必须要指定enctype属性值为multipart/form-data,以二进制流的形式传输文件。)
  • application/json、application/xml等格式的数据,必须使用@RequestBody来处理。
TODO:1.研究Content-Type: application/x-www-form-urlencoded等编码格式;2. 消息转换器 HttpMessageConverter

(4) RestFul风格

@PathVariable获取请求路径变量,也可以加载类头的@RequestMapping()参数里(RestFul风格)
可使用组合注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
@RequestMapping("/pets/{petId}")
public void findPet(PathVariable String petId, Model model) {     
    // implementation omitted
}

7、注解驱动

  将Object数据转化为JSON数据,需要由消息转换器HttpMessageConverter完成。而转换器的开启,需要由来完成。
  当 Spring 容器进行初始化过程中,在处创建注解驱动时,默认创建了七个 HttpMessageConverter 对象。
HttpMessageConverter 接口 : HttpMessageConverter是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为T)将对象(类型为T)输出为响应信息
HttpMessageConverter接口定义的方法: boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为clazz类型对象,指定支持MIME类型(text/html,applaiction/json 等) boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在 MediaType 中定义。 List getSupportMediaTypes():该转换器支持的媒体类型。 T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。 void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将 T 类型的对象写到响应流中,同时指定相应的媒体类型为 contentType

  主要关注接口实现类MappingJackson2HttpMessageConverter:负责读取和写入json格式的数据。 利用Jackson的ObjectMapper读写json数据,操作Object 类型数据,可读取application/json,响应媒体类型为 application/json

使用场景:
  • Ajax请求时
  • 解决静态资源访问的时候
  • 异常处理使用注解方式的时候

9、处理器方法的返回值

  • ModelAndView:处理器方法处理完后,需要跳转到其它资源,又要传递数据, 选择返回ModelAndView 比较好
  • String:返回内部资源逻辑视图名,与视图解析器结合使用
  • 无返回值void:
  • 返回自定义类型对象Object:
    • 这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
    • 返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。
    • 一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。需要导入Jackson 的相关Jar包。
    • 需要声明注解驱动
    • 返回中文字符串,需要在@RequestMapping 的 produces 属性指定字符集。如:produces="text/plain;charset=utf-8"
注意:
  1. Model、ModelMap(参数传入)和ModelAndView(自己创建)都可以用于返回数据
  2. @Responsebody 表示该方法的返回结果直接写入 HTTP response body 中。一般在异步获取数据时使用,在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。比如异步获取json数据,加上 @Responsebody 后,会直接返回json数据。
总结:
  • 普通请求
    • 需要返回数据:ModelAndView、String+ModelMap
    • 不需返回数据:String、ModelAndView(不传数据)
  • Ajax请求:加@ResponseBody注解,返回Object

9、静态资源的访问

(1) *.do方式

  SpringMVC 的中央调度器 DispatcherServlet 的常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。图片会正常显示。

(2) / 方式

  DispatcherServlet 向静态资源的获取请求(.css、 .js、 .jpg、 .png)当作是一个普通的 Controller 请求。中央调度器调用处理器映射器为其查找相应的处理器。找不到会报 404 错误。图片无法显示。

(3) 无法访问静态资源的解决方法

  • 表示使用 DefaultServletHttpRequestHandler 处理器对象。
    • 对进入 DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet 处理。
    • 一般的服务器都有默认的 Servlet。而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。
  • 使用:在Spring3.0版本后, Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。需要在springmvc 配置文件中添加如下配置:
<mvc:resources location="/images/" mapping="/images/**" />
location:表示静态资源所在目录。 当然, 目录不要使用/WEB-INF/及其子目录。
mapping:表示对该资源的请求(以/images/开始的请求,如/image/beauty.jpg,/images/car.png等)。注意后面是两个星号**。
  • 解决动态资源和静态资源冲突的问题,还需要在 springmvc 配置文件声明注解驱动

10、请求重定向和转发

  • 对于请求转发的页面,可以是WEB-INF中页面;
  • 重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问WEB-INF中资源的。
  • 转发和重定向都不需要视图解析器

(1) 请求转发

  • 处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:
  • 处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。

(2) 重定向

  • 在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转

11、异常处理

    SpringMVC提供了基于XML和基于注解两种异常映射机制。这两种异常映射不能够只使用一个,他们需要一起使用。因为有些异常是基于注解异常映射捕获不到的。
  • 请求是由处理的,抛出了异常就得用基于xml的异常映射来捕获处理这个异常。
  • 请求是由@RequestMapping这个注解来处理的,发生了异常就得使用基于注解的异常映射来捕获并处理这个异常。没找到基于注解的异常映射就会找xml的异常映射

(1) 基于xml的异常映射


    <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.Exception">system-errorprop>
            props>
        property>
    bean>
  • exceptionMappings是SimpleMappingExceptionResolver的一个成员属性。
  • prop中的system-error会经过视图解析器处理,所以如果发生java.lang.Exception异常就会跳转到system-error.jsp页面

(2) 基于注解的异常映射

  • 使用注解@ExceptionHandler指定异常处理方法。该注解只有一个可选属性value,为一个Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
  • 使用注解@ControllerAdvice(控制器增强)定义全局异常处理类。使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。
  • 当使用@RequestMapping注解修饰的方法抛出异常时,会执行@ControllerAdvice修饰的类中的异常处理方法。
  • @ControllerAdvice 是使用@Component 注解修饰的,可以扫描到@ControllerAdvice 所在的类路径(包名), 创建对象。
  • 若@ExceptionHandler使用在@RequestMapping注解的方法上,异常处理方法去xml异常映射中找

12、拦截器

(1) 定义拦截器

  需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:
  • preHandle(request,response, Object handler):该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。
  • postHandle(request,response, Object handler,modelAndView):该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
  • afterCompletion(request,response, Object handler, Exception ex):当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。 即该方法是在中央调度器渲染(数据填充) 了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。afterCompletion 最后执行的方法,清除资源,例如在 Controller 方法中加入数据

(2) 注册拦截器

<!--注册拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--mvc:mapping配置要拦截的资源-->
        <!-- /* 对应一层路径,比如:/aaa -->
        <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc -->
        <mvc:mapping path="/**"/>

        <!-- mvc:exclude-mapping 配置不拦截的资源 -->
        <mvc:exclude-mapping path="/admin/to/login/page.html"/>
        <mvc:exclude-mapping path="/admin/do/login.html"/>
        <mvc:exclude-mapping path="/admin/do.logout.html"/>

        <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

(3) 多拦截器执行顺序

 

13、SpringMVC配置文件总结

  • 组件扫描器:<context:component-scan base-package>
  • 视图解析器:<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  • 声明注解驱动:<mvc:annotation-driven />
  • 静态资源访问
    • <mvc:default-servlet-handler/>:创建DefaultServletHttpRequestHandler处理器对象
    • <mvc:resources/>:加入静态资源
    • <mvc:annotation-driven />声明注解驱动
  • 异常处理
    • xml方式:<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    • 注解方式:
      • 组件扫描器扫描@ControllerAdvice注解所在的包
      • 声明注解驱动
  • 注册拦截器:<mvc:interceptors>
<?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="com.luca.security.config.controller"/>
 
    <!--声明 springmvc框架中的视图解析器,帮助开发人员设置视图文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp"/>
    </bean>
 
    <!--声明注解驱动-->
    <mvc:annotation-driven/>
 
    <!--静态资源访问-->
    <!--方式一-->
    <mvc:default-servlet-handler/>
    <!--方式二:使用一个配置语句,指定多种静态资源的访问-->
    <mvc:resources mapping="/static/**" location="/static/"/>
 
    <!--异常处理-->
    <!--配置基于xml的异常映射-->
    <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.Exception">system-error</prop>
            </props>
        </property>
    </bean>
    <!--基于注解的异常映射-->
    <context:component-scan base-package="com.luca.controller"/>
 
    <!--注册拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--mvc:mapping配置要拦截的资源-->
            <!-- /* 对应一层路径,比如:/aaa -->
            <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc -->
            <mvc:mapping path="/**"/>
 
            <!-- mvc:exclude-mapping 配置不拦截的资源 -->
            <mvc:exclude-mapping path="/admin/to/login/page.html"/>
            <mvc:exclude-mapping path="/admin/do/login.html"/>
            <mvc:exclude-mapping path="/admin/do.logout.html"/>
 
            <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

 

posted on 2021-04-16 21:02  lukazan  阅读(202)  评论(0)    收藏  举报