一万字完整总结 SpringMVC,确定不进来了解一下吗?

在这里插入图片描述

图片来源网络,如有侵权请联系删除


文章目录

Spring MVC 框架介绍

什么是 MVC

MVC 是一种软件架构的思想,将软件按照模型、视图、控制器来划分,主要的作用是降低了视图与业务逻辑的双向耦合。

  • Model(模型层):指工程中的JavaBean,作用是处理数据。
    JavaBean分为两类:
    1)实体类 Bean:专门存储业务数据的,如 StudentUser 等。
    2)业务处理 Bean:指 ServiceDao 对象,专门用于处理业务逻辑和数据访问。
  • View(视图层):指工程中的html或jsp等页面,作用是与用户进行交互,展示数据。
  • Controller(控制器层):指工程中的servlet,作用是接收请求和响应浏览器。

SpringMVC 的特点

三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 Servlet。

  • Spring 家族原生产品,与 IOC 容器等基础设施无缝对接。
  • 基于原生的 Servlet,通过了功能强大的前端控制器 DispatcherServlet,对请求和响应进行统一处理。
  • 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案。
  • 代码清新简洁,大幅度提升开发效率。
  • 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可。
  • 性能卓著,尤其适合现代大型、超大型互联网项目要求。

SpringMVC 架构原理解析

在这里插入图片描述

  1. 发起请求到中央调度器 DispatcherServlet。
  2. 中央调度器接收到请求后,调用 HandlerMapping 映射器查找 Handler。
  3. HandlerMapping 映射器根据xml配置、注解进行查找具体的处理器 Handler,生成处理器对象及连接器一并向中央调度器返回 Handler。
  4. 中央调度器调用 HandlerAdapter 设配器执行Handler。
  5. HandlerAdapter 适配器经过适配调用具体的处理器(Controller 也称:控制器)进行业务逻辑操作。
  6. Handler执行完成给适配器返回ModelAndView。
  7. HandlerAdapter 将 Handler 执行结果 ModelAndView 返回给中央调度器(ModelAndView是springmvc框架的一个底层对象,包括 Model和view)。
  8. 中央调度器将 ModelAndView 传给 ViewResolver 视图解析器进行视图解析,根据逻辑视图名解析成真正的视图(jsp)。
  9. ViewResolver 视图解析器向中央调度器返回View。
  10. 中央调度器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域。
  11. 前端控制器向用户响应结果。
  • 前端控制器 DispatcherServlet(不需要程序员开发)
    作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。
  • 处理器映射器 HandlerMapping(不需要程序员开发)
    作用:根据请求的url查找Handler。
  • 处理器适配器 HandlerAdapter
    作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
  • 处理器 Handler(需要程序员开发)
    注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
  • 视图解析器 ViewResolver(不需要程序员开发)
    作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
  • 视图View(需要程序员开发jsp) View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

SpringMVC 配置

pom.xml 文件引入依赖

<!-- SpringMVC 依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
<!-- servlet 依赖 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!-- Spring5 和 Thymeleaf整合包 -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.12.RELEASE</version>
</dependency>

web.xml 文件头部信息格式

注意此处的web-app版本是4.0。

<?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 中央调度器

1)在web.xml配置文件中注册 Spring MVC 框架的核心对象 DispatcherServlet

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- 设置 springmvc 的核心控制器所能处理的请求的请求路径 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

<url-pattern>标签中使用//*的区别:

  • /所匹配的请求可以是 /login.html.js.css方式的请求路径。但是/不能匹配.jsp请求路径的请求,因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面。
  • /*则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*的写法。

2)扩展配置方式:用<init-param>设置 SpringMVC 配置文件的位置和名称,通过load-on-startup标签设置 SpringMVC 前端控制器DispatcherServlet的初始化时间。

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 通过初始化参数指定 SpringMVC 配置文件的位置和名称 -->
    <init-param>
        <!-- contextConfigLocation为固定值 -->
        <param-name>contextConfigLocation</param-name>
        <!-- 使用classpath:表示从类路径查找配置文件,例如 maven 工程中的 src/main/resources -->
        <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <!-- 
 		作为框架的核心组件,在启动过程中有大量的初始化操作要做,而这些操作放在第一次请求时才执行会严重影响访问速度,
		因此需要通过此标签将启动控制 DispatcherServlet 的初始化时间提前到服务器启动时。
	-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

中央调度器 <url-pattern />

  • 在没有特殊要求的情况下,springmvc 的中央调度器DispatcherServlet< url-pattern/ >常使用后缀匹配方式,如写为*.do*.action或者*.mvc等。

  • 也可以使用/代替。当我们发起静态资源请求时,DispatcherServlet会将此请求最为普通的 Controller。中央调度器会调用控制器映射器为其查找相应的控制器。在这种情况下所有静态资源请求会报错404。

  • 一般服务器所返回的静态资源都是由 Tomcat 自身处理的。

    • Tomcat 的 web.xml 配置文件里有一个名为 default 的 Servlet,当运行 Tomcat 时会创建 DefaultServlet 对象,用于处理静态资源与为映射的请求。

      <servlet>
          <servlet-name>default</servlet-name> 
          <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
          <init-param>
              <param-name>debug</param-name>
              <param-value>0</param-value>
          </init-param>
          <init-param>
              <param-name>listings</param-name>
              <param-value>false</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>default</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      

创建请求控制器

由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器(Controller)。

SpringMVC 的控制器是由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件。由 Spring 的 IoC 容器管理,此时 SpringMVC 才能将相应的请求交给对应的控制器(Controller)进行处理。

@Controller
public class HelloController {
    
}

创建 springmvc 的配置文件

resources文件中创建 Spring 配置文件applicationContext.xml

  1. 配置 JSP 视图解析器:用于处理 jsp 文件。
<!-- 开启组件扫描:如果扫描多个包,多个包之间使用逗号隔开或者直接写这些包共有的上层目录 -->
<context:component-scan base-package="com.springmvc.controller" />

<!-- 配置 JSP 视图解析器,设置视图文件的路径 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 文件路径前缀 -->
    <property name="prefix" value="/WEB-INF/view/"/>
    <!-- 文件后缀 -->
    <property name="suffix" value=".jsp"/>
</bean>

<!--处理静态资源-->
<mvc:default-servlet-handler/>

<!--mvc注释驱动开启-->
<mvc:annotation-driven/>

2)配置 Thymeleaf 视图解析器:用于处理 html 模板文件。

<!-- 开启组件扫描:如果扫描多个包,多个包之间使用逗号隔开或者直接写这些包共有的上层目录 -->
<context:component-scan base-package="com.springmvc.controller" />

<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <!-- 视图解析器优先级 -->
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine">
        <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
            <property name="templateResolver">
                <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                    <!-- 视图前缀 -->
                    <property name="prefix" value="/WEB-INF/templates/"/>
                    <!-- 视图后缀 -->
                    <property name="suffix" value=".html"/>
                    <property name="templateMode" value="HTML5"/>
                    <property name="characterEncoding" value="UTF-8" />
                </bean>
            </property>
        </bean>
    </property>
</bean>

<!--处理静态资源-->
<mvc:default-servlet-handler/>

<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
    <mvc:message-converters>
        <!-- 处理响应中文内容乱码 -->
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="defaultCharset" value="UTF-8" />
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html</value>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

浏览器发送请求,若请求地址符合前端控制器的<url-pattern>,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取 SpringMVC 的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的 value 属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过 Thymeleaf 对视图进行渲染,最终转发到视图所对应页面。

如何解决访问静态资源

1)第一种:在 springmvc 配置文件里加入<mvc:default-servlet-handler>标签:框架创建控制器对象,把接受到的请求转发给 Tomcat。

<mvc:default-servlet-handler/>

2)第二种:在 springmvc 配置文件里加入<mvc:resources>标签:框架创建 ResourceHttpRequestHandler 控制器对象,让这个对象处理静态资源的访问,不依赖 Tomcat 服务器。

  • location属性:静态资源在你的项目中的目录位置。
  • mapping属性:访问静态资源的 URL 地址,使用通配符**

在这里插入图片描述

<mvc:resources location="/static/" mapping="/static/**"/>

注解

@Controller 注解

创建一个 MyController 类并使用@Controller注解标识此类为控制器对象,由 Spring 的 IoC 容器管理。

@Controller
public class MyController {
    @RequestMapping("/doSome")
    public ModelAndView doSome() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","smg数据");
        modelAndView.setViewName("show");
        return modelAndView;
    }
}

@RequestMapping 注解

  • @RequestMapping注解:可以设置在方法上或是一个类中。

  • @RequestMapping注解:请求映射,作用是把请求地址与方法绑定,一个请求指定一个方法处理。

  • @RequestMapping注解:被修饰的方法叫做处理器方法或者控制器方法可以处理请求,类似servlet中的doGet、doPost。

1)@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
2)SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

  • 在类上定义注解称父级接口。

    @RequestMapping("/test")
    public class MyController {
    }
    
  • 在方法上定义注解称子级级接口。

    @RequestMapping(value = "/doSome")
    public ModelAndView doSome() {
    }
    

    接口访问方式:http://xxxx.xxxx/test/doSome

  • 在注解中定义请求方式,method属性表示请求方式,它的值是RequestMethod类枚举值。

    @RequestMapping("/test")
    public class MyController {
        
        @RequestMapping(value = "/doSome", method = RequestMethod.GET)
        public ModelAndView doSome() {
            ModelAndView model = new ModelAndView();
            model.setViewName("index");
            return model;
        }
    }
    

@RequestMapping 派生注解

对于处理指定请求方式的控制器方法,SpringMVC 中提供了@RequestMapping的派生注解:

  • @GetMapping:处理GET请求的映射。
  • @PostMapping:处理POST请求的映射。
  • @PutMapping:处理PUT请求的映射。
  • @DeleteMapping:处理DELETE请求的映射。

SpringMVC 支持 ANT 风格的路径。

通配符说明
?匹配任何字符。
*匹配 0 或者任意数量的字符。
**匹配 0 或者更多的目录。

注意:在使用 **时,只能以/**/list这种方式使用。

SpringMVC 支持路径中的占位符。

SpringMVC 路径中的占位符常用于 RESTful 风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的 value 属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参。

// 请求:http://192.168.1.111/doSome/12/admin
@RequestMapping(value = "/doSome/{id}/{name}")
public String doSome(@PathVariable("id") Integer id, @PathVariable("name") String name) {
    return "success";
}

控制器方法请求参数获取

@RequestMapping注解所表示的方法有三种参数:请求、响应、会话。这些参数在系统被调用时由系统自动赋值。

@RequestMapping(value = "/doSome",method = RequestMethod.GET)
public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    
}

逐个参数接收

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参。

@RequestMapping(value = "/login",method = RequestMethod.POST)
public ModelAndView otherSome(String username, String password) {
    
}

形参注解 @RequestParam

@RequestParam是将请求的参数与控制器方法的形参创建映射关系。
@RequestParam注解一共有三个参数:

  • value:指定为形参负责的请求参数名称。
  • required:设置是否必传参数。默认:true
  • defaultValue:不管required属性值是truefalse,当value所指定的请求参数没有传输或者为空时,则使用默认为形参赋予。
// 请求:http://192.168.1.111/doSome/12/admin?id=1&name=admin
@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@RequestParam("id") Integer id, @RequestParam("name") String name) {
    
}

形参注解 @RequestHeader

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系。
@RequestHeader属性与@RequestParam相同。

@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@RequestHeader("token") String token) {
    
}

形参注解 @CookieValue

@CookieValue是将cookie数据和控制器方法的形参创建映射关系。
@CookieValue属性与@RequestParam相同。

@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@CookieValue("token") String token) {
    
}

POJO 获取请求参数

@RequestMapping(value = "/index",method = RequestMethod.POST)
public ModelAndView otherSome(User user) {
    
}

CharacterEncodingFilter 解决中文乱码

  • web.xml配置文件中注册过滤器,过滤所有接口解决中文乱码。

    <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>
        <!-- 强制请求对象(HttpServletRequest)使用encoding编码的值,默认值为false -->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- 强制响应对象(HttpServletResponse)使用encoding编码的值,默认值为false -->
        <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>
    

域对象共享数据

使用 ServletApi 向域对象共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpServletRequest request) {
    request.setAttribute("name","admin");
    return "success";
}

使用 ModelAndView 向 Request 域对象共享数据

ModelAndView对象有 Model 和 View 的功能:
1)Model 主要用于向请求域共享数据。
2)View 主要用于设置视图,实现页面跳转功能。

ModelAndView对象方法:

  • addObject(String attributeName, @Nullable Object attributeValue):添加数据,框架在请求的最后把数据放入到request作用域中。
  • setViewName(@Nullable String viewName):指定视图,框架对视图执行request.getRequestDispatcher("/show.jsp").forward(req,resp)操作。

框架会使用视图解析器拼接路径:前缀 + 文件名 + 后缀。

@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome() {
    ModelAndView model = new ModelAndView();
    // 共享数据
    model.addObject("name","admin");
    // 设置视图
    model.setViewName("success")
    return model;
}

使用 Model 向 Request 域对象共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(Model model) {
    model.addAttribute("name","admin");
    return "success";
}

使用 Map 向 Request 域对象共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(Map<String, Object> map) {
    map.put("name","admin");
    return "success";
}

使用 ModelMap 向 Request 域对象共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(ModelMap modelMap) {
    modelMap.addAttribute("name","admin");
    return "success";
}

Model、ModelMap、Map的关系

Model、ModelMap、Map 类型的参数其实本质上都是 BindingAwareModelMap 类型的

public interface Model {}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}

Session 域共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpSession session) {
    session.setAttribute("name","admin");
    return "success";
}

application 域共享数据

@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpSession session) {
    ServletContext application = session.getServletContext();
    application.setAttribute("name","admin");
    return "success";
}

控制器方法返回值

String 返回值

直接return 要跳转页面的名称,框架会用视图解析器作拼接。
数据的返回可以在形参中加入 HttpServletRequest 对象,并设置对象域在页面接收即可。

@RequestMapping(value = "/returnString-view",method = RequestMethod.POST)
public String doReturnStringView(HttpServletRequest req,String username, String password) {
    req.setAttribute("username",username);
    req.setAttribute("password",password);
    return "show";
}

void 返回值:Ajax 响应

在处理 Ajax 的时候,可以使用 void 返回值。
Ajax 请求服务器端放回的就是数据和视图无关。
使用 HttpServletResponse 输出数据响应Ajax请求,Ajax 请求所接受的数据个格式为 JSON,需要使用 Jackson 依赖使对象转换为 JSON 字符串,而 Ajax 会把 JSON 字符串解析为 JSON。

@RequestMapping(value = "/returnVoid-ajax")
public void doReturnAjax(HttpServletResponse resp, String username, String password) throws IOException {
    Student student = new Student();
    student.setUsername(username);
    student.setPassword(password);
    //对象转换为json结果
    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(student);
    //响应ajax请求
    resp.setContentType("application/json; charset=utf-8");
    resp.getWriter().write(json);
}
$(function(){
    $("#btn").click(function() {
        $.post("user/returnVoid-ajax",{username:"tuoyingtao",password:"123456"},
               function fun(response) {
            alert(JSON.stringify(response))
        },"json")
    })
})

Object 返回值:@ResponseBody注解 Ajax 响应

1)springmvc 控制器方法返回 Object,转换 JSON 输出到浏览器,响应原理:

  • <mvc:annotaction-driven> 注解驱动:完成 Java 对象到 JOSN、xml、text、二进制等数据格式的转换。
  • <mvc:annotaction-driven>在加入到 springmvc 配置后会自动创建 HttpMessageConverter 接口的实现类,包括MappingJackson2HttpMessageConverter。 (内部使用 Jackson 工具库中的 ObjectMapper 实现 Java 对象转为 JSON 字符串)

2)HttpMessageConverter 接口两个方法:

  • canWrite(Class<?> clazz, @Nullable MediaType mediaType):检查控制器方法返回值,是否可以转换为 MediaType 的数据格式。
  • write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage):把控制器方法的返回对象,调用 Jackson 中的 ObjectMapper 转为 JSON 格式。

3)通过@ResponseBody把数据结果输出到浏览器,Ajax 处理响应。

  • 把处理器返回的对象转换为 JSON 后,通过 HttpServletResponse 输出给浏览器。
  1. 引入 Jackson 依赖:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
  1. 在 spring 配置文件注册 annotation-driver 驱动:(注意:包路径是 mvc 下的)
<mvc:annotation-driven/>
  1. 使用 @ResponseBody 注解将数据响应给 Ajax 请求:
//返回对象
@ResponseBody
@RequestMapping(value = "/returnStudentJson",method = RequestMethod.POST)
public Student doReturnStudentJson(String username, String password) {
    Student student = new Student();
    student.setUsername("托马斯·克里斯特");
    student.setPassword("123456");
    return student;
}
//返回List数组
@ResponseBody
@RequestMapping(value = "/returnStudentList",method = RequestMethod.POST)
public List<Student> doReturnStudentList(String username, String password) {
    List<Student> list = new ArrayList<Student>();
    Student student = new Student();
    student.setUsername("托马斯·克里斯特");
    student.setPassword("123456");
    Student student1 = new Student();
    student1.setUsername("玛丽·莫利亚");
    student1.setPassword("123456");
    list.add(student);
    list.add(student1);
    return list;
}

注解 Ajax 响应文本

1)控制器方法返回 String 的数据,不是视图。
2)如果加上@ResponseBody注解,则表示返回的就是文本数据,反之就是视图。
3)返回文本数据时默认使用 text/plain; charset=ISO-8859-1 作为Content-Type。可能出现中文乱码。

解决:在 @RequestMapping 中添加 produces 属性来指定Content-Type类型。

@ResponseBody
@RequestMapping(value = "/returnStringData", produces = "text/plain;charset=utf-8")
public String doStringData(String username,String password) {
    return "杰克·罗伯塔";
}

SpringMVC 视图

转发视图

SpringMVC 中默认的转发视图是InternalResourceView

SpringMVC 中创建转发视图的情况:
当控制器方法中所设置的视图名称以forward:为前缀时,创建InternalResourceView视图,此图的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀forward:去掉,剩余部分分作为最终路径通过转发的方式实现跳转。

如:forward:/forward:/employee

@RequestMapping(value = "/doForward")
public String doForward() {
    return "forward:/testHello";
}

重定向视图

SpringMVC 中默认的重定向视图时 RedirectView

当控制器方法中所设置的视图名称以redirect:为前缀时,创建RedirectView视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀redirect:去掉,剩余部分作为最终路径通过重定向的方式实现跳转。

注意:重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径

如:redirect:/redirect:/employee

@RequestMapping(value = "/doRedirect")
public String doRedirect() {
    return "redirect:/testHello";
}

RESTful API

什么是 API

  • API 是应用程序接口,可理解为程序与程序之间通信的桥梁,其本质就是一个函数而已。
  • API 把 web app 的功能全部封装了,所以,通过 API 操作数据,可以极大的把前端和后端的代码分离,使得后端代码易于测试,前端代码编写更简单。

什么是 RESTful

RESTful 是一种设计 API 的模式。REST 的全称是 Representational State Transfer,翻译过来就是 “资源”在网络传输中以某种“表现形式”进行“状态转移” 。

  • 资源(Resource):资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个 URI 来标识。URI 既是资源的名称,也是资源在 Web 上的地址。对某个资源感兴趣的客户端应用,可以通过资源的 URI 与其进行交互。
  • 资源表述(Representational):资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTMLXMLJSON、纯文本、图片、视频、音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
  • 状态转移(State Transfer):在客户端和服务器端之间转移(Transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

RESTful 的实现

具体说就是 HTTP 协议里面,四个表示操作方式的动词:GETPOSTPUTDELETE
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

操作传统方式REST风格
查询操作getUserById?id=1user/1–>get请求方式
保存操作saveUseruser–>post请求方式
删除操作deleteUser?id=1user/1–>delete请求方式
更新操作updateUseruser–>put请求方式

HiddenHttpMethodFilter

1)由于浏览器只支持发送GETPOST方式的请求,那么该如何发送PUTDELETE请求呢?
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

2)HiddenHttpMethodFilter 处理PUTDELETE请求的条件:

  • 请求方式必须为POST

  • 请求必须传输请求参数_method

    满足以上条件,HiddenHttpMethodFilter过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式。

  • web.xml中注册HiddenHttpMethodFilter

    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

CharacterEncodingFilter

1)目前为止,SpringMVC 中提供了两个过滤器:CharacterEncodingFilterHiddenHttpMethodFilter

2)在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

  • CharacterEncodingFilter中通过request.setCharacterEncoding(encoding)方法设置字符集的。

  • request.setCharacterEncoding(encoding)方法要求前面不能有任何获取请求参数的操作。
    HiddenHttpMethodFilter恰恰有一个获取请求方式的操作:

    String paramValue = request.getParameter(this.methodParam);
    
<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>
    <!-- 强制请求对象(HttpServletRequest)使用encoding编码的值,默认值为false -->
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!-- 强制响应对象(HttpServletResponse)使用encoding编码的值,默认值为false -->
    <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>

RESTful 案例

实体类:

public class Employee {
   private Integer id;
   private String lastName;
   private String email;
   private Integer gender;
   // 省略get、set等方法
}

模拟 DAO 数据:

@Repository
public class EmployeeDao {
   private static Map<Integer, Employee> employees = null;
   static{
      employees = new HashMap<Integer, Employee>();
      employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
      employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
      employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
      employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
      employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
   }
   private static Integer initId = 1006;
   // 保存
   public void save(Employee employee){
      if(employee.getId() == null){
         employee.setId(initId++);
      }
      employees.put(employee.getId(), employee);
   }
   // 列表
   public Collection<Employee> getAll(){
      return employees.values();
   }
   // 查询
   public Employee get(Integer id){
      return employees.get(id);
   }
   // 删除
   public void delete(Integer id){
      employees.remove(id);
   }
}

功能清单

功能URL 地址请求方式
访问首页√/GET
查询全部数据√/employeeGET
删除√/employee/2DELETE
跳转到添加数据页面√/toAddGET
执行保存√/employeePOST
跳转到更新数据页面√/employee/2GET
执行更新√/employeePUT

访问首页

  1. 在 Spring 配置文件中配置view-controller标签。

    <mvc:view-controller path="/" view-name="index"/>
    
  2. 创建页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8" >
            <title>Title</title>
        </head>
        <body>
            <h1>首页</h1>
            <a th:href="@{/employee}">访问员工信息</a>
        </body>
    </html>
    

查询所有员工数据

  1. Controller 层。

    @RequestMapping(value = "/employee", method = RequestMethod.GET)
    public String getEmployeeList(Model model){
        Collection<Employee> employeeList = employeeDao.getAll();
        model.addAttribute("employeeList", employeeList);
        return "employee_list";
    }
    
  2. 创建employee_list.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <title>Employee Info</title>
            <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
        </head>
        <body>
            <table border="1" cellpadding="0" cellspacing="0" style="text-align: center;" id="dataTable">
                <tr>
                    <th colspan="5">Employee Info</th>
                </tr>
                <tr>
                    <th>id</th>
                    <th>lastName</th>
                    <th>email</th>
                    <th>gender</th>
                    <th>options(<a th:href="@{/toAdd}">add</a>)</th>
                </tr>
                <tr th:each="employee : ${employeeList}">
                    <td th:text="${employee.id}"></td>
                    <td th:text="${employee.lastName}"></td>
                    <td th:text="${employee.email}"></td>
                    <td th:text="${employee.gender}"></td>
                    <td>
                        <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
                        <a th:href="@{'/employee/'+${employee.id}}">update</a>
                    </td>
                </tr>
            </table>
        </body>
    </html>
    

删除功能

  1. 创建处理DELETE请求方式的表单。

    <!-- 作用:通过超链接控制表单的提交,将POST请求转换为DELETE请求 -->
    <form id="delete_form" method="post">
        <!-- HiddenHttpMethodFilter 要求:必须传输 _method 请求参数,并且值为最终的请求方式 -->
        <input type="hidden" name="_method" value="delete"/>
    </form>
    
  2. 删除超链接绑定点击事件

    • 引入vue.js

      <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
      
    • 删除超链接

      <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
      
    • 通过 Vue 处理点击事件

      <script type="text/javascript">
          var vue = new Vue({
              el:"#dataTable",
              methods:{
                  //event表示当前事件
                  deleteEmployee:function (event) {
                      //通过id获取表单标签
                      var delete_form = document.getElementById("delete_form");
                      //将触发事件的超链接的href属性为表单的action属性赋值
                      delete_form.action = event.target.href;
                      //提交表单
                      delete_form.submit();
                      //阻止超链接的默认跳转行为
                      event.preventDefault();
                  }
              }
          });
      </script>
      
    • Controller 层

      @RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
      public String deleteEmployee(@PathVariable("id") Integer id){
          employeeDao.delete(id);
          return "redirect:/employee";
      }
      

跳转到添加数据页面

  1. 在 Spring 配置文件中配置view-controller

    <mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
    
  2. 创建employee_add.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <title>Add Employee</title>
        </head>
        <body>
            <form th:action="@{/employee}" method="post">
                lastName:<input type="text" name="lastName"><br>
                email:<input type="text" name="email"><br>
                gender:<input type="radio" name="gender" value="1">male
                <input type="radio" name="gender" value="0">female<br>
                <input type="submit" value="add"><br>
            </form>
        </body>
    </html>
    

执行保存

  1. Controller 层

    @RequestMapping(value = "/employee", method = RequestMethod.POST)
    public String addEmployee(Employee employee){
        employeeDao.save(employee);
        return "redirect:/employee";
    }
    

跳转到更新数据页面

  1. 修改超链接

    <a th:href="@{'/employee/'+${employee.id}}">update</a>
    
  2. 控制器方法

    @RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
    public String getEmployeeById(@PathVariable("id") Integer id, Model model){
        Employee employee = employeeDao.get(id);
        model.addAttribute("employee", employee);
        return "employee_update";
    }
    
  3. 创建employee_update.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <title>Update Employee</title>
        </head>
        <body>
            <form th:action="@{/employee}" method="post">
                <input type="hidden" name="_method" value="put">
                <input type="hidden" name="id" th:value="${employee.id}">
                lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
                email:<input type="text" name="email" th:value="${employee.email}"><br>
                <!--
    			th:field="${employee.gender}"可用于单选框或复选框的回显
    			若单选框的value和employee.gender的值一致,则添加checked="checked"属性
    		   -->
                gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
                <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
                <input type="submit" value="update"><br>
            </form>
        </body>
    </html>
    

执行更新

  1. Controller 层

    @RequestMapping(value = "/employee", method = RequestMethod.PUT)
    public String updateEmployee(Employee employee){
        employeeDao.save(employee);
        return "redirect:/employee";
    }
    

HttpMessageConverter 文本转换器

HttpMessageConverter 文本转换器,将请求报文转换为 JAVA 对象,或将 JAVA 对象转换为响应文本。
httpMessageConverter提供两个注解和两个类型:@RequestBody@ResponseBodyRequestEntity@ResponseEntity

@RequestBody

@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。

<form th:action="@{/testRequestBody}" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
    System.out.println("requestBody:"+requestBody);
    return "success";
}

@ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器。

@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
    return "success";
}

RequestEntity

RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息。

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
    System.out.println("requestHeader:"+requestEntity.getHeaders());
    System.out.println("requestBody:"+requestEntity.getBody());
    return "success";
}

请求响应处理 JSON

@ResponseBody 处理 JSON 的步骤:

  1. 导入 jackson 的依赖。

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.1</version>
    </dependency>
    
  2. 在 SpringMVC 的核心配置文件中开启<mvc:annotation-driven/>的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的 JAVA 对象转换为 JSON 格式的字符串。

  3. 在处理器方法上使用@ResponseBody注解进行标识。

  4. 将 JAVA 对象直接作为控制器方法的返回值返回,就会自动转换为 JSON 格式的字符串。

    @RequestMapping("/testResponseUser")
    @ResponseBody
    public User testResponseUser(){
        return new User(1001,"admin","123456",23,"男");
    }
    

请求响应处理 Ajax

  1. 通过 Vue 点击事件来处理 axios 请求。

    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
    <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
    
    <div id="app">
        <a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
    </div>
    
    <script type="text/javascript">
        var vue = new Vue({
            el:"#app",
            methods:{
                testAjax:function (event) {
                    axios({
                        method:"post",
                        url:event.target.href,
                        params:{
                            username:"admin",
                            password:"123456"
                        }
                    }).then(function (response) {
                        alert(response.data);
                    });
                    event.preventDefault();
                }
            }
        });
    </script>
    
  2. 控制器方法

    @RequestMapping("/testAjax")
    @ResponseBody
    public String testAjax(String username, String password){
        System.out.println("username:"+username+",password:"+password);
        return "hello,ajax";
    }
    

@RestController 注解

@RestController注解是 SpringMVC 提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解。

ResponseEntity

ResponseEntity 用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应文本。


文件上传和下载

文件下载

使用 ResponseEntity 实现下载文件的功能。

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/static/images/1.jpg");
    //创建输入流
    InputStream is = new FileInputStream(realPath);
    //创建字节数组
    byte[] bytes = new byte[is.available()];
    //将流读到字节数组中
    is.read(bytes);
    //创建HttpHeaders对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    //设置要下载方式以及下载文件的名字
    headers.add("Content-Disposition", "attachment;filename=1.jpg");
    //设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    //创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

文件上传

文件上传要求 Form 表单的请求方式必须为 POST,并且添加属性enctype="multipart/form-data",SpringMVC 中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息。

  1. 添加依赖

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    
  2. 在 SpringMVC 的配置文件中添加配置

    <!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    
  3. 控制器方法

    @RequestMapping("/testUp")
    public String testUp(MultipartFile photo, HttpSession session) throws IOException {
        //获取上传的文件的文件名
        String fileName = photo.getOriginalFilename();
        //处理文件重名问题
        String hzName = fileName.substring(fileName.lastIndexOf("."));
        fileName = UUID.randomUUID().toString() + hzName;
        //获取服务器中photo目录的路径
        ServletContext servletContext = session.getServletContext();
        String photoPath = servletContext.getRealPath("static/images");
        File file = new File(photoPath);
        if(!file.exists()){
            file.mkdir();
        }
        String finalPath = photoPath + File.separator + fileName;
        //实现上传功能
        photo.transferTo(new File(finalPath));
        return "success";
    }
    

异常处理器

SpringMVC 框架采用的是统一,全局的异常处理。把 Controller 中的所有异常处理都集中到一个地方,采用 AOP 思想,把业务逻辑和异常处理代码分开。也称解耦合。

使用两个注解:

ControllerAdvice:控制器增强 ,必须让框架知道这个注解所在的包名,需要在 SpringMVC 配置文件声明组件扫描器指定 @ControllerAdvice 所在的全局异常处理类包名。

ExceptionHandler:表示异常的类型,当发生此类型的异常时,由当前方法处理。处理异常的方法和控制器方法的定义一样,可以有多个参数。可以有ModelAndViewStringvoid对象类型的返回值, 形参:Exception,表示 Controller 中抛出的异常对象,通过形参可以获取发送的异常信息。

在这里插入图片描述

  1. 自定义异常类MyUserException.java,继承Exception类。

    public class MyUserException extends Exception{
        public MyUserException() {
            super();
        }
        public MyUserException(String message) {
            super(message);
        }
    }
    
  2. 创建MyUserException的子类NameException.javaPasswordException.java

    public class UsernameException extends MyUserException{
        public UsernameException() {
            super();
        }
        public UsernameException(String message) {
            super(message);
        }
    }
    public class PasswordException extends MyUserException {
        public PasswordException() {
            super();
        }
        public PasswordException(String message) {
            super(message);
        }
    }
    
  3. 在控制器方法中,捕获NameExceptionPasswordException异常,并抛出父类异常MyUserException

    @Controller
    public class MyController {
        @ResponseBody
        @RequestMapping(value = "/errorData",method = RequestMethod.POST)
        public ModelAndView doErrorData(String username,String password) throws MyUserException {
            System.out.println(username + password);
            ModelAndView mv = new ModelAndView();
            //更具请求参数抛出异常
            if (!"托马斯·克里斯特".equals(username)) {
                throw new UsernameException("姓名有误!!!");
            }
            if (!"123456".equals(password)) {
                throw new PasswordException("密码有误!!!");
            }
            mv.addObject("username",username);
            mv.addObject("password",password);
            mv.setViewName("show");
            return mv;
        }
    }
    
  4. 创建GlobalExceptionHandler类,使用@ControllerAdvice注解来标识这个类为全局异常处理类。在方法上标注@ExceptionHandler注解表示所有的异常都会有当前方法进行处理。若给定 value 值,则指针对某一种类型进行处理。

    @ControllerAdvice
    public class GlobalExceptionHandler {
        //定义处理异常方法
        @ExceptionHandler(value = UsernameException.class)
        public ModelAndView doUsernameException(Exception e) {
            //处理账号异常
            ModelAndView mv = new ModelAndView();
            mv.addObject("message","姓名必须为托马斯·克里斯特!!!");
            mv.addObject("error",e);
            mv.setViewName("uesrnameError");
            return mv;
        }
        @ExceptionHandler(value = PasswordException.class)
        public ModelAndView doPasswordException(Exception e) {
            //处理账号异常
            ModelAndView mv = new ModelAndView();
            mv.addObject("message","密码必须为123456!!!");
            mv.addObject("error",e);
            mv.setViewName("passwordError");
            return mv;
        }
        //处理未知异常
        @ExceptionHandler
        public ModelAndView doOtherException(Exception e) {
            ModelAndView mv= new ModelAndView();
            mv.addObject("message","用户或密码错误我!");
            mv.addObject("error",e);java
            mv.setViewName("defaultError");
            return mv;
        }
    }
    
  5. SpringMVC 配置文件扫描异常处理程序与注册驱动。

    <context:component-scan base-package="com.springmvc.handler"/>
    <!-- 添加注解驱动 -->
    <mvc:annotation-driven/>
    

拦截器

什么是拦截器

1)拦截器是 SpringMVC 中的一种,需要实现 HandlerInterceptor 接口。
2)拦截器与过滤器类似,处理的应用场景不同。过滤器是用来过滤请求参数,设置字符集编码等,而拦截器则是对用户的请求做拦截,对请求做判断处理。
3)拦截器是全局的,可以对多个 Controller 做拦截。一个项目可以有多个拦截器。常用在:用户登录处理、权限检查、记录日志等。

拦截器的执行

定义一个拦截器类实现 HandlerInterceptor 接口,重写以下三个方法:preHandle()、postHandle()、afterCompletion()

1)preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler):在请求处理之前,也就是 Controller 类中的方法之前先被拦截,如果返回值为true就继续执行下一步,如果为false就终止,在这个方法中可以获取用户请求信息,验证请求是否符合要求。

2)preHandle():返回true执行 Controller 类中的方法。

3)postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView mv):在控制器方法执行之后执行拦截器,能够获取控制器方法的返回值 ModelAndView,可以修改ModelAndView中的数据和视图,可以影响最后的执行结果,主要对原来的执行结果做二次修改。

4)afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex):在请求处理完后执行拦截器,在框架中规定是当你的视图处理完成后,对视图执行了forward,就认为请求处理完成,一般做资源回收工作的,程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。

拦截器的使用

  1. 定义类实现HandlerInterceptor接口。

    public class MyInterceptor implements HandlerInterceptor {
        /**
         * @param request
         * @param response
         * @param handler 被拦截的控制器对象
         * @return Boolean
         * @throws Exception
         */
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器MyInterceptor的preHandle方法");
            return true;
        }
        /**
         * @param request
         * @param response
         * @param handler 被拦截的控制器对象
         * @param modelAndView 处理器方法的返回值
         * @throws Exception
         */
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器MyInterceptor的postHandle方法");
        }
        /**
         * @param request
         * @param response
         * @param handler 被拦截的控制器对象
         * @param ex 程序中发生的异常
         * @throws Exception
         */
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器MyInterceptor的afterCompletion方法");
        }
    }
    
  2. 在 SpringMVC 配置文件中声明拦截器,在框架中多个拦截器是按照顺序存放到 ArrayList 中的。

    <mvc:interceptors>
        <!-- 声明第一个拦截器 -->
        <mvc:interceptor>
            <!-- 指定拦截器的请求url地址 拦截/**所有接口 -->
            <mvc:mapping path="/**"/>
            <!-- 声明拦截器对象 -->
            <bean class="com.springmvc.handler.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

执行顺序

在这里插入图片描述

多个拦截器的执行

  1. MyInterceptor 拦截器类一

    public class MyInterceptor implements HandlerInterceptor {
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器MyInterceptor的preHandle方法");
            return true;
        }
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器MyInterceptor的postHandle方法");
        }
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器MyInterceptor的afterCompletion方法");
        }
    }
    
  2. MyInterceptor2拦截器类二

    public class MyInterceptor2 implements HandlerInterceptor {
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器MyInterceptor的preHandle方法22222222");
            return true;
        }
    
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器MyInterceptor的postHandle方法2222222222");
        }
    
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器MyInterceptor的afterCompletion方法2222222");
        }
    }
    
  3. SpringMVC 配置文件

    <mvc:interceptors>
        <!-- 声明第一个拦截器 -->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.springmvc.handler.MyInterceptor"/>
        </mvc:interceptor>
        <!-- 声明第二个拦截器 -->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.springmvc.handler.MyInterceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

情况一:当MyInterceptorMyInterceptor2拦截器中的preHandle()返回 都为true时执行结果。

在这里插入图片描述

情况二:当MyInterceptor2拦截器中的preHandle()返回false时执行结果。

在这里插入图片描述

情况三:当MyInterceptor拦截器中的preHandle()返回false时执行结果。

在这里插入图片描述

拦截器与过滤去的区别

  1. 过滤器是 Servlet 中的对象。
    拦截器是框架中的对象。
  2. 过滤器是实现 Filte 接口的对象。
    拦截器是实现 Handlerinterceptor 接口。
  3. 过滤器是用来设置 Request、Response 的参数,是对数据的过滤。
    拦截器是用来验证请求的,能截断请求。
  4. 过滤器是在拦截器之前先执行的。
    拦截器是 SpringMVC 容器中的对象。
  5. 过滤器是一个执行时间点。
    拦截器有三个执行时间点。
  6. 过滤器可以处理 jsp、js、html 等。
    拦截器是对 Controller 的对象拦截,如果你的请求不能被 DispatcherServlet 接受,这个请求不会执行拦截器。
  7. 过滤器过滤 Servlet 请求响应。
    拦截器拦截普通方法的执行。

注解配置 SpringMVC

使用配置类和注解代替web.xml和 SpringMVC 配置文件的功能。

创建初始化类来代替 web.xml

在 Servlet3.0 环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置 Servlet 容器。

Spring 提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2 引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到 Servlet3.0 容器的时候,容器会自动发现它,并用它来配置 Servlet 上下文。

public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     * 指定spring的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }
    /**
     * 指定SpringMVC的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    /**
     * 指定DispatcherServlet的映射规则,即url-pattern
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    /**
     * 添加过滤器
     * @return
     */
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceRequestEncoding(true);
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
    }
}

创建 SpringConfig 配置类,代替 Spring 的配置文件

@Configuration
public class SpringConfig {
	//ssm整合之后,spring的配置信息写在此类中
}

创建 WebConfig 配置类,代替 SpringMVC 的配置文件

@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    //使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    //配置文件上传解析器
    @Bean
    public CommonsMultipartResolver multipartResolver(){
        return new CommonsMultipartResolver();
    }
    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        FirstInterceptor firstInterceptor = new FirstInterceptor();
        registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
    }
    /**
    //配置视图控制
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }
    */
    /**
    //配置异常映射
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.ArithmeticException", "error");
        //设置异常映射
        exceptionResolver.setExceptionMappings(prop);
        //设置共享异常信息的键
        exceptionResolver.setExceptionAttribute("ex");
        resolvers.add(exceptionResolver);
    }
    */
    //配置生成模板解析器
    @Bean
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }
    //生成模板引擎并为模板引擎注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }
    //生成视图解析器并未解析器注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值