1. SpringMVC简介

1.1 什么是MVC

  • MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分

    • M:Model,模型层,指工程中的JavaBean,作用是处理数据

      • JavaBean分为两类:

        • 一类称为实体类Bean:专门存储业务数据的,如Student、User等
        • 一类称为业务处理Bean:指Service和Dao对象,专门用于处理业务逻辑和数据访问
    • V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
    • C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
  • MVC的工作流程:用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

1.2 什么是SpringMVC

  • SpringMVC是Spring的一个后续产品,是Spring的一个子项目。SpringMVC是Spring为表述层开发提供的一整套完备的解决方案。在表述层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为JavaEE项目表述层开发的首选方案。三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

1.3 SpringMVC的特点

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

2. 入门案例HelloWorld

2.1 开发环境

  • IDE:idea2021.3
  • 构建工具:maven3.8.4
  • 服务器:tomcat8.0.42
  • SpringMVC版本:5.3.1

2.2 创建maven工程,引入依赖包

  • 添加web模块
  • 打包方式:war包
  • 引入依赖
  • 注:由于Maven的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。
<groupId>com.atguigu.mvc</groupId>
<artifactId>SpringMVC_demo01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式-->
<packaging>war</packaging>
<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies><!-- SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.1</version></dependency><!-- 日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!-- ServletAPI --><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>
</dependencies>

2.3 配置web.xml文件

2.3.1 注册SpringMVC的前端控制器DispatcherServlet

①. 默认配置方式

  • 此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为<servlet-name>-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml
<!--配置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的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求--><url-pattern>/</url-pattern>
</servlet-mapping>

②. 扩展配置方式

  • 可通过init-param标签设置SpringMVC配置文件的位置和名称,通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间
<!--配置SpringMVC的前端控制器DispatcherServlet,对浏览器发送的请求统一进行处理-->
<servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--通过初始化参数指定SpringMVC配置文件的位置和名称,目的是把配置文件放到resource里(默认的位置是WEB-INF下) --><init-param><!--contextConfigLocation为固定值--><param-name>contextConfigLocation</param-name><!--使用classpath:表示从类路径(src/main/resources)查找配置文件,例如maven工程中的src/main/resources --><param-value>classpath:springMVC.xml</param-value></init-param><!--作为框架的核心组件,在启动过程中有大量的初始化操作要做而这些操作放在第一次请求时才执行会严重影响访问速度因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时--><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>SpringMVC</servlet-name><!--"/"表示当前浏览器所发送的所有请求,但是不包括.jsp为后缀的请求路径的请求--><url-pattern>/</url-pattern>
</servlet-mapping>

③. 注:<url-pattern>标签中使用"/"和"/*"的区别

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

2.4 创建请求控制器

  • 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器。
  • 请求控制器中每一个处理请求的方法成为控制器方法
  • 因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IOC容器管理,此时SpringMVC才能够识别控制器的存在
@Controller
public class HelloController{
}

2.5 springMVC配置文件

<!--组件扫描,自动扫描包-->
<context:component-scan base-package="com.atguigu.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,为templateEngine赋值--><bean class="org.thymeleaf.spring5.SpringTemplateEngine"><property name="templateResolver"><!--内部bean,为templateResolver赋值--><bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"><!--视图前缀--><property name="prefix" value="/WEB-INF/templates/"/><!--视图后缀--><property name="suffix" value=".html"/><!--模板的模型:HTML5--><property name="templateMode" value="HTML5"/><property name="characterEncoding" value="UTF-8"/></bean></property></bean></property>
</bean>
<!--    &lt;!&ndash;-->
<!--        处理静态资源,例如html、js、css、jpg-->
<!--        若只设置该标签,则只能访问静态资源,其他请求则无法访问-->
<!--        此时必须设置<mvc:annotation-driven/>解决问题-->
<!--    &ndash;&gt;-->
<!--    <mvc:default-servlet-handler/>-->
<!--    &lt;!&ndash;开启mvc注解驱动&ndash;&gt;-->
<!--    <mvc:annotation-driven>-->
<!--        <mvc:message-converters>-->
<!--            &lt;!&ndash; 处理响应中文内容乱码 &ndash;&gt;-->
<!--            <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>-->

2.6 测试界面

  • 在请求控制器中创建处理请求的方法
  • 在主页index.html中设置超链接
  • 在在请求控制器中创建处理请求的方法
在请求控制器中创建处理请求的方法
//@RequestMapping注解:处理请求和控制器方法之间的映射关系
//@RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
//localhost:8080/springMVC/
@RequestMapping("/")
public String index() {//设置视图名称return "index";
}
在主页index.html中设置超链接
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1>首页</h1><!--th:href="@{/hello}"等同于/springMVC/hello --><a th:href="@{/hello}">HelloWorld</a>
</body>
</html>
在在请求控制器中创建处理请求的方法
@RequestMapping("/hello")
public String HelloWorld() {return "target";
}

2.7 总结

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

3. @RequestMapping注解

3.1 @RequestMapping注解的功能

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

3.2 @RequestMapping注解的位置

  • @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
  • @RequestMapping标识一个方法:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/test")
public class RequestMappingController {//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping@RequestMapping("/testRequestMapping")public String testRequestMapping(){return "success";}
}

3.3 @RequestMapping注解的value属性

  • @RequestMapping注解的value属性通过请求的请求地址匹配请求映射
  • @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
  • @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射
<a th:href="@{/testRequestMapping}">测试@RequestMapping的value属性-->/testRequestMapping</a><br>
<a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br>
@RequestMapping(value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){return "success";
}

3.4 @RequestMapping注解的method属性

  • @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
  • @RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
  • 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method 'POST' not supported
  • 对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
    • 处理get请求的映射-->@GetMapping
    • 处理post请求的映射-->@PostMapping
    • 处理put请求的映射-->@PutMapping
    • 处理delete请求的映射-->@DeleteMapping
  • 常用的请求方式有get、post、put、delete,但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理。若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到
<a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br>
<form th:action="@{/test}" method="post"><input type="submit">
</form>
@RequestMapping(value = {"/testRequestMapping", "/test"},method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){return "success";
}

3.5 @RequestMapping注解的params属性(了解)

  • @RequestMapping注解的params属性通过请求的请求参数匹配请求映射
  • @RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
    • "param":要求请求映射所匹配的请求必须携带param请求参数
    • "!param":要求请求映射所匹配的请求必须不能携带param请求参数
    • "param=value":要求请求映射所匹配的请求必须携带param请求参数且param=value
    • "param!=value":要求请求映射所匹配的请求必须携带param请求参数但是param!=value
<a th:href="@{/test(username='admin',password=123456)">测试@RequestMapping的params属性-->/test</a><br>
@RequestMapping(value = {"/testRequestMapping", "/test"},method = {RequestMethod.GET, RequestMethod.POST},params = {"username","password!=123456"}
)
public String testRequestMapping(){return "success";
}
注:
若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,
此时页面会报错400:Parameter conditions "username, password!=123456" not
met for actual request parameters: username={admin}, password={123456}

3.6 @RequestMapping注解的headers属性(了解)

  • @RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
  • @RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
    • "header":要求请求映射所匹配的请求必须携带header请求头信息
    • "!header":要求请求映射所匹配的请求必须不能携带header请求头信息
    • "header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
    • "header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value
  • 若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到

3.7 SpringMVC支持ant风格的路径

  • ?:表示任意的单个字符
  • *:表示任意的0个或多个字符
  • **:表示任意的一层或多层目录
  • 注意:在使用**时,只能使用/**/xxx的方式

3.8 SpringMVC支持路径中的占位符(重点)

  • 原始方式:/deleteUser?id=1
  • rest方式:/deleteUser/1
  • SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参
<a th:href="@{/testRest/1/admin}">测试路径中的占位符-->/testRest</a><br>
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id,@PathVariable("username") String username
){System.out.println("id:"+id+",username:"+username);return "success";
}
//最终输出的内容为-->id:1,username:admin

4. SpringMVC获取请求参数

4.1 通过ServletAPI获取

  • 将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){String username = request.getParameter("username");String password = request.getParameter("password");System.out.println("username:"+username+",password:"+password);return "success";
}

4.2 通过控制器方法的形参获取请求参数

  • 在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参
<a th:href="@{/testParam(username='admin',password=123456)}">测试获取请求参数-->/testParam</a><br>
@RequestMapping("/testParam")
public String testParam(String username, String password){System.out.println("username:"+username+",password:"+password);return "success";
}
注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串
数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

4.3 @RequestParam

  • @RequestParam是将请求参数和控制器方法的形参创建映射关系
  • @RequestParam注解一共有三个属性:
    • value:指定为形参赋值的请求参数的参数名
    • required:设置是否必须传输此请求参数,默认值为true。若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
    • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值
<form th:action="@{/testParam}" method="post">用户名:<input type="text" name="user_name"><br>密码:<input type="password" name="password"><br>爱好:<input type="checkbox" name="hobby" value="a">a<input type="checkbox" name="hobby" value="b">b<input type="checkbox" name="hobby" value="c">c<br><input type="submit" value="测试使用控制器的形参获取请求参数">
</form>
@RequestMapping("/testParam")
public String testParam(@RequestParam(value="user_name",required=false,defaultValue="hehe") String username,String password,String[] hobby
){System.out.println("username:"+username+",password:"+password+",hobby:"+Arrays.toString(hobby));return "success";
}

4.4 @RequestHeader

  • @RequestHeader是将请求头信息和控制器方法的形参创建映射关系
  • @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.5 @CookieValue

  • @CookieValue是将cookie数据和控制器方法的形参创建映射关系
  • @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.6通过POJO获取请求参数

  • 可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
<form th:action="@{/testPojo}" method="post">用户名:<input type="text" name="username"><br>密  码:<input type="password" name="password"><br>性  别:<input type="radio" name="sex" value="男">男<input type="radio" name="sex" value="女">女<br>年  龄:<input type="text" name="age"><br>邮  箱:<input type="text" name="email"><br><input type="submit" value="使用实体类接收请求参数">
</form>
@RequestMapping("/testPojo")
public String param(User user){System.out.println(user);return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男',email='123@qq.com'}

4.7 解决获取请求参数的乱码问题

  • 解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册,且SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
<!--配置springMVC的编码过滤器-->
<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>forceResponseEncoding</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

5. 域对象共享数据

5.1 使用ServletAPI向request域对象共享数据

//使用servletAPI向Request域对象共享数据
@RequestMapping("/testRequestByServletAPI")
public String testServletAPI(HttpServletRequest request){request.setAttribute("testRequestScope","hello,servletAPI");return "success";
}
//获得Request域中的数据
<!--如果是获得request域中的数据,直接写共享数据的键如果是获得session域中的数据,session.共享数据的键如果是获得ServletContext中的数据,application.共享数据的键
-->
<p th:text="${testRequestScope}"></p>
<p th:text="${session.testSessionScope}"></p>
<p th:text="${application.testApplicationScope}"></p>

5.2 使用ModelAndView向request域对象共享数据

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){/** ModelAndView有Model和View的功能* Model主要用于向请求域共享数据* View主要用于设置视图,实现页面跳转*/ModelAndView mav = new ModelAndView();//处理模型数据:即向请求域request共享数据mav.addObject("testRequestScope","hello,ModelAndView");//设置视图名称,实现页面跳转mav.setViewName("success");return mav;
}

5.3 使用Model向request域对象共享数据

@RequestMapping("/testModel")
public String testModel(Model model){model.addAttribute("testRequestScope","hello,Model");return "success";
}

5.4 使用map向request域对象共享数据

@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){map.put("testRequestScope", "hello,Map");return "success";
}

5.5 使用ModelMap向request域对象共享数据

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){modelMap.addAttribute("testRequestScope", "hello,ModelMap");return "success";
}

5.6 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 {}

5.7 向session域共享数据

//向session域共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){session.setAttribute("testSessionScope", "hello,session");return "success";
}
//在html页面获得共享的数据
<!--如果是获得request域中的数据,直接写共享数据的键如果是获得session域中的数据,session.共享数据的键如果是获得ServletContext中的数据,application.共享数据的键
-->
<p th:text="${testRequestScope}"></p>
<p th:text="${session.testSessionScope}"></p>
<p th:text="${application.testApplicationScope}"></p>

5.8 向application域共享数据

//向application域共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){ServletContext application = session.getServletContext();application.setAttribute("testApplicationScope", "hello,application");return "success";
}
//在html页面获得共享的数据
<!--如果是获得request域中的数据,直接写共享数据的键如果是获得session域中的数据,session.共享数据的键如果是获得ServletContext中的数据,application.共享数据的键
-->
<p th:text="${testRequestScope}"></p>
<p th:text="${session.testSessionScope}"></p>
<p th:text="${application.testApplicationScope}"></p>

6. SpringMVC的视图

  • SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
  • SpringMVC视图的种类很多,默认有转发视图和重定向视图
  • 当工程引入jstl的依赖,转发视图会自动转换为JstlView
  • 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

6.1 ThymeleafView

  • 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/testHello")
public String testHello(){return "hello";
}
SpringMVC.xml文件中的视图解析器配置:
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
返回的解析结果是:
/WEB-INF/templates/hello.html
然后把这个路径下的html页面通过转发的方式实现跳转
最终的结果是在浏览器上呈现出hello.html页面

6.2 转发和重定向

  • 重定向访问服务器两次,转发只访问服务器一次
  • 转发页面的URL不会改变,而重定向地址会改变
  • 转发只能转发到自己的web应用内,重定向可以重定义到任意资源路径。
  • 转发相当于服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,两个文件(当前文件和目标文件)属于同一次请求,前后页共用一个request,可以通过此来传递一些数据或者session信息,request.setAttribute()和 request.getAttribute()。而重定向会产生一个新的request,不能共享request域信息与请求参数
  • 由于转发相当于服务器内部方法调用,所以转发后面的代码仍然会执行(转发之后记得return);重定向代码执行之后是方法执行完成之后进行重定向操作,也就是访问第二个请求,如果是方法的最后一行进行重定向那就会马上进行重定向(重定向也需要return)。
  • 无论是RequestDispatcher.forward方法,还是HttpServletResponse.sendRedirect方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中移除。

6.3 转发视图

  • SpringMVC中默认的转发视图是InternalResourceView
  • SpringMVC中创建转发视图的情况:
    • 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
@RequestMapping("/testForward")
public String testForward(){return "forward:/testHello";
}
//创建InternalResourceView视图,将前缀"forward:"去掉,
//然后把"/testHello"请求进行转发,最终的结果是访问下面的控制器方法,是同一个请求
@RequestMapping("/testHello")
public String testHello(){
}

6.4 重定向视图

  • SpringMVC中默认的重定向视图是RedirectView
  • 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
@RequestMapping("/testRedirect")
public String testRedirect(){return "redirect:/testHello";
}
//创建RedirectView视图,将前缀"redirect:"去掉,
//然后把"/testHello"请求进行重定向,就是客户端重新发一次请求:/testHello
@RequestMapping("/testHello")
public String testHello(){
}

6.5 视图控制器view-controller

  • 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示
SpringMVC.xml配置文件中配置view-controller:
<!--path:设置处理的请求地址view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/" view-name="index"/>
<mvc:view-controller path="/test_rest" view-name="test_rest"/>
<!--当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
-->
<!--开启mvc的注解驱动-->
<mvc:annotation-driven/>

7. RESTful

7.1 RESTful简介

REST:Representation State Transfer,表现层资源状态转移

7.1.1 资源

  • 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。

7.1.2 资源的表述

  • 资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。

7.1.3 状态转移

  • 状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

7.2 RESTful实现

  • 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
  • 它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
  • REST风格提倡URL地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为URL地址的一部分,以保证整体风格的一致性。

7.3 HiddenHttpMethodFilter

  • 由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?
  • SpringMVC提供HiddenHttpMethodFilter帮助我们将POST请求转换为DELETE或PUT请求
  • HiddenHttpMethodFilter处理put和delete请求的条件:
    • 当前请求的请求方式必须为post
    • 当前请求必须传输请求参数_method
  • 满足以上条件,HiddenHttpMethodFilter过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式
  • 在web.xml中注册HiddenHttpMethodFilter
<!--配置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>
注:
目前为止,SpringMVC中提供了两个过滤器:
CharacterEncodingFilter和HiddenHttpMethodFilter
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter
原因:
在CharacterEncodingFilter中通过request.setCharacterEncoding(encoding)方法设置字符集
request.setCharacterEncoding(encoding)方法要求前面不能有任何获取请求参数的操作
而HiddenHttpMethodFilter恰恰有一个获取请求方式的操作:
String paramValue = request.getParameter(this.methodParam);

8. RESTful案例

8.1 准备工作

和传统CRUD一样,实现对员工信息的增删改查操作
1>准备实体类
public class Employee {private Integer id;private String lastName;private String email;//1 male, 0 femaleprivate Integer gender;...
}
2>准备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);}
}

8.2 功能清单

8.3 具体功能:访问首页

1>配置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>

8.4 具体功能:查询所有员工数据

1>控制器方法
@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getEmployeeList(Model model){Collection<Employee> employeeList = employeeDao.getAll();model.addAttribute("employeeList", employeeList);return "employee_list";
}
2>页面
<!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>

8.5 具体功能:删除

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>
3>控制器方法
@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){employeeDao.delete(id);return "redirect:/employee";
}

8.6 具体功能:跳转到添加数据页面

<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-
controller>
<!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>

8.7 具体功能:执行保存

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

8.8 具体功能:跳转到更新数据页面

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>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>

8.9 具体功能:执行更新

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

9. HttpMessageConverter

  • HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
  • HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity

9.1 @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";
}
输出结果:
requestBody:username=admin&password=123456

9.2 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";
}
输出结果:
requestHeader:[host:"localhost:8080",connection:"keep-alive",content-
length:"27",cache-control:"max-age=0",sec-ch-ua:""Not A;Brand";v="99",
"Chromium";v="90","GoogleChrome";v="90"",sec-ch-ua-mobile:"?0",upgrade-
insecure-requests:"1",origin:"http://localhost:8080",user-
agent:"Mozilla/5.0(Windows NT 10.0; Win64; x64)AppleWebKit/537.36(KHTML,
likeGecko)Chrome/90.0.4430.93 Safari/537.36"]
requestBody:username=admin&password=123

9.3 @ResponseBody

  • @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){return "success";
}
结果:浏览器页面显示success
//也可以使用传统的方式
//响应浏览器数据
@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse response) throws IOException {response.getWriter().print("hello,response");
}

9.4 SpringMVC处理json

  • @ResponseBody处理json的步骤:

    • 导入jackson的依赖
    • 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
    • 在处理器方法上使用@ResponseBody注解进行标识
    • 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.1</version>
</dependency>
<mvc:annotation-driven />
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){return new User(1001,"admin","123456",23,"男");
}
浏览器的页面中展示的结果:
{"id":1001,"username":"admin","password":"123456","age":23,"sex":"男"}

9.5 SpringMVC处理ajax

1>请求超链接
<div id="app"><a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>
2>通过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>
<script type="text/javascript">var vue = new Vue({el:"#app",methods:{testAjax:function (event) {axios({method:"post",//将触发点击事件的超链接的href属性赋值给urlurl:event.target.href,params:{username:"admin",password:"123456"}}).then(function (response) {//response.data:response对象中响应的数据alert(response.data);});//取消超链接的默认行为(跳转),event表示当前的事件event.preventDefault();}}});
</script>
3>控制器方法
@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){System.out.println("username:"+username+",password:"+password);return "hello,ajax";
}

9.6 @RestController注解

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

9.7 ResponseEntity

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

10. 文件上传和下载

  • 使用ResponseEntity实现下载文件的功能
  • 文件上传要求form表单的请求方式必须为post,并且添加属性enctype="multipart/form-data"
  • SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
#########文件上传需要添加依赖
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency>
#########在SpringMVC的配置文件中添加配置
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
1>html文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>测试文件上传和下载</title>
</head>
<body><h1>文件下载(当前工程的环境):从服务器将文件下载到客户端、浏览器端</h1><br><a th:href="@{/testDown}">测试下载图片1.jpg</a><br><h1>文件上传(当前工程的环境):从客户端、浏览器端将文件上传到服务器</h1><br><h1>二者底层用的都是文件复制的过程。</h1><!--设置了enctype="multipart/form-data"之后,当前form表单中的数据不再是通过name=value&name=value的方式传输到服务器,而是通过二进制的方式传输到服务器--><form th:action="@{/testUp}" method="post" enctype="multipart/form-data"><!--type="file",file是文件域,浏览器解析之后会显示一个框,点了这个框之后,可以选择一个文件-->头像:<input type="file" name="photo"><br><input type="submit" value="上传"></form>
</body>
</html>
2>请求控制器
@Controller
public class FileUpAndDownController {//ResponseEntity自定义一个响应报文去响应浏览器//文件的下载@RequestMapping("/testDown")public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {//获取ServletContext(表示的是当前的整个工程)对象ServletContext servletContext = session.getServletContext();//获取服务器中文件的真实路径,只有知道文件在服务器中的位置,才可以通过io流进行文件的复制//servletContext.getRealPath:该方法是获取当前服务器的部署路径(就是工程部署到tomcat服务器的路径)//如果里面有字符串,获取的就是当前字符串所对应的文件在服务器中的路径String realPath = servletContext.getRealPath("/static/img/1.jpg");System.out.println(realPath);//创建输入流InputStream is = new FileInputStream(realPath);//创建字节数组//is.available():获取当前的输入流对应的文件的所有字节数byte[] bytes = new byte[is.available()];//将流读到字节数组中is.read(bytes);//创建HttpHeaders对象设置响应头信息MultiValueMap<String, String> headers = new HttpHeaders();//键是固定的,值是设置要下载的方式以及为下载的文件所设置的默认的名字(固定结构,除了1.jpg),attachment代表是以附件的方式下载文件headers.add("Content-Disposition", "attachment;filename=1.jpg");//设置响应状态码HttpStatus statusCode = HttpStatus.OK;//创建ResponseEntity对象,bytes就是响应体,headers就是响应头ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,statusCode);//关闭输入流is.close();return responseEntity;}//文件的上传@RequestMapping("/testUp")public String testUp(MultipartFile photo, HttpSession session) throws IOException {//1.获取上传的文件的文件名String fileName = photo.getOriginalFilename();//2.处理文件重名问题//获取上传的文件的后缀(通过lastIndexOf获取最后一个.的索引位置,然后通过subString从索引位置开始截取)String suffixName = fileName.substring(fileName.lastIndexOf("."));//UUID:32位的随机序列,防止文件重名导致文件覆盖fileName = UUID.randomUUID().toString() + suffixName;//3.获取服务器中photo文件目录的路径ServletContext servletContext = session.getServletContext();//上传的位置(photo文件目录下)String photoPath = servletContext.getRealPath("photo");File file = new File(photoPath);//判断photopath所对应的文件路径是否存在if(!file.exists()) {//如果不存在,则创建目录file.mkdir();}//File.separator:文件分隔符/String finalPath = photoPath + File.separator + fileName;//实现上传功能photo.transferTo(new File(finalPath));return "success";}
}

11. 拦截器

11.1 拦截器的配置

  • SpringMVC中的拦截器用于拦截控制器方法的执行
  • SpringMVC中的拦截器需要实现HandlerInterceptor
  • SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<!--配置单个拦截器-->
<mvc:interceptors><!--<bean class="com.atguigu.mvc.interceptors.FirstInterceptor"/>--><!--<ref bean="firstInterceptor"/>--><!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截--><mvc:interceptor><mvc:mapping path="/**"/><mvc:exclude-mapping path="/index1"/><ref bean="firstInterceptor"/></mvc:interceptor><!--以上配置方式可以通过ref或bean标签设置拦截器通过mvc:mapping设置需要拦截的请求通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求-->
</mvc:interceptors>
<!--配置多个拦截器-->
<mvc:interceptors><ref bean="firstInterceptor"/><ref bean="secondInterceptor"/>
</mvc:interceptors>@Component
public class FirstInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("FirstInterceptor-->preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("FirstInterceptor-->postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("FirstInterceptor-->afterCompletion");}
}
@Component
public class SecondInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("SecondInterceptor-->preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("SecondInterceptor-->postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("SecondInterceptor-->afterCompletion");}
}

11.2 拦截器的三个抽象方法

  • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
  • postHandle:控制器方法执行之后执行postHandle()
  • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

11.3 多个拦截器的执行顺序

  • 若每个拦截器的preHandle()都返回true,此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:

    • preHandle()会按照配置的顺序执行
    • 而postHandle()和afterComplation()会按照配置的反序执行
  • 若某个拦截器的preHandle()返回了false,preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

12. 异常处理器

12.1 基于配置的异常处理

  • SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
  • HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
  • SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<!--配置异常处理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><!--key里面写出现的异常,value写出现异常跳转的页面--><!--properties的键表示处理器方法执行过程中出现的异常properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面--><prop key="java.lang.ArithmeticException">error</prop></props></property><!--exceptionAttribute属性设置一个属性名,将出现的异常信息存储到请求域中,键为ex,异常信息为值--><property name="exceptionAttribute" value="ex"/>
</bean>

12.2 基于注解的异常处理

//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {//@ExceptionHandler用于设置所标识方法处理的异常@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})public String testException(Exception exception, Model model){//ex表示当前请求处理中出现的异常对象model.addAttribute("ex",exception);return "error";}
}
//error.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>出现错误!</h1><p th:text="${ex}"></p>
</body>
</html>

13. 注解配置SpringMVC

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

13.1 创建初始化类,代替web.xml

  • 在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。
//web工程的初始化类,用来设置servlet上下文,等同于web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {/*** 指定spring的配置类* @return*/@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*** 指定SpringMVC的配置类* @return*/@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}/*** 指定DispatcherServlet的映射规则,即url-pattern* @return*/@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}/*** 添加过滤器* @return*/@Overrideprotected Filter[] getServletFilters() {//编码过滤器CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();encodingFilter.setEncoding("UTF-8");encodingFilter.setForceResponseEncoding(true);//<!--配置处理请求方式put和delete的HiddenHttpMethodFilter过滤器-->HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();return new Filter[]{encodingFilter, hiddenHttpMethodFilter};}
}

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

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

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

//用来代替SpringMVC的配置文件:SpringMVC.xml
@Configuration
//1.扫描组件
@ComponentScan("com.atguigu.mvc")
//2.开启MVC的注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {//3.配置Thymeleaf的视图解析器//3.1配置生成模板解析器//@Bean:加上此注解的方法的返回值可以作为IOC容器中的一个Bean@Beanpublic 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;}//3.2生成模板引擎并为模板引擎注入模板解析器@Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {SpringTemplateEngine templateEngine = new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}//3.3生成视图解析器并为解析器注入模板引擎@Beanpublic ViewResolver viewResolver(SpringTemplateEngine templateEngine) {ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();viewResolver.setOrder(1);viewResolver.setCharacterEncoding("UTF-8");viewResolver.setTemplateEngine(templateEngine);return viewResolver;}//4.default-servlet-handler:配置默认的servlet,用于开放对静态资源的访问:静态资源不能被SpringMVC处理,所以需要开放default-servlet,对静态资源进行处理@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {//代表默认的servlet可用configurer.enable();}//5.配置视图控制器@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/hello").setViewName("hello");}//6.配置文件上传解析器@Beanpublic MultipartResolver multipartResolver(){CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();return multipartResolver;}//7.配置拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {//创建拦截器对象TestInterceptor testInterceptor = new TestInterceptor();registry.addInterceptor(testInterceptor).addPathPatterns("/**").excludePathPatterns("/");}//8.配置异常处理解析器@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties properties = new Properties();properties.setProperty("java.lang.ArithmeticException","error");exceptionResolver.setExceptionMappings(properties);exceptionResolver.setExceptionAttribute("exception");resolvers.add(exceptionResolver);}
}

14. SpringMVC执行流程

14.1 SpringMVC常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

    • 作用:统一处理请求和响应,整个流程控制的中心,由它调用其他组件处理用户的请求
  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
    • 作用:根据请求的url,method信息查找Handler,即控制器方法
  • Handler:处理器,需要工程师开发
    • 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
  • HandlerAdapter:处理器适配器,由框架提供
    • 作用:通过HandlerAdapter对处理器(控制器方法)进行执行
  • ViewResolver:视图解析器,由框架提供
    • 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
  • View:视图
    • 作用:将模型数据通过页面展示给用户

14.2 SpringMVC的执行流程

  • ①.用户向服务器发送请求,请求被SpringMVC的前端控制器DispatcherServlet捕获
  • ②.DispatcherServlet对请求URL进行解析,得到请求资源定位符(URI),判断请求URI对应的映射:
    • i>不存在
    • 再判断是否配置了mvc:default-servlet-handler
    • 如果没配置,则控制台报映射查找不到,客户端展示404错误
    • 如果有配置,则访问目标资源(一般为静态资源,如:JS、CSS、HTML),找不到客户端也会展示404
    • ii>存在则执行下面的流程
  • ③.根据URI,调用HandlerMapping获得该Handler配置的所有相关的信息(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的方式返回
  • ④.DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter
  • ⑤.如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
  • ⑥.提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    • a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    • b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    • c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    • d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  • ⑦.Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象。
  • ⑧.此时将开始执行拦截器的postHandle(...)方法【逆向】。
  • ⑨.根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
  • ⑩.渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
  • ⑪.将渲染结果返回给客户端

【框架】SpringMVC相关推荐

  1. java获取作用域的值_Java-springMVC框架:springMVC取参数值、把值放入作用域方法

    Java-springMVC框架:springMVC取参数值.把值放入作用域方法 package com.zp.upload; import java.io.IOException; import j ...

  2. 框架 - SpringMVC框架

    框架 - SpringMVC框架 第一章 SpringMVC概述 1.1 SpringMVC简介 1.2 SpringMVC 优点 1.3 第一个注解的 SpringMVC程序 1.4 SpringM ...

  3. 常用的mvc框架 java_常用框架SpringMVC

    常用框架SpringMVC 2020年12月30日 | 萬仟网IT编程 | 我要评论 与SpringMVC相关面试题SpringMVC的流程(1)用户发送请求至前端控制器 DispatcherServ ...

  4. SSM三大框架+SpringMVC的工作原理及其流程

    SSM三大框架+SpringMVC的工作原理及其流程 一.SSM中各层作用及关系 1.持久层:DAO层(mapper层)(属于mybatis模块)     DAO层(Mapper层):主要负责与数据库 ...

  5. 框架SpringMVC笔记系列 二 传值

    主题:SpringMVC(第一节中再回顾复习一次) 学习资料参考网址: 1.http://www.icoolxue.com 2.http://haohaoxuexi.iteye.com/blog/13 ...

  6. SpringMVC框架----SpringMVC的自定义类型转换器

    上一段我们介绍了请求参数的绑定 SpringMVC请求参数的绑定 在请求参数绑定的时候,有时候会遇到一些问题,如自定义类型转换器异常 1.自定义类型转换器演示异常 现在有一个页面,想给服务器提交数据, ...

  7. SpringMVC框架----SpringMVC入门程序中的组件介绍

    springmvc框架:底层基于组件方式执行流程. 前端控制器和处理器映射器都称为组件 客户端先发送request请求,请求先到前端控制器,前端控制器是一个指挥中心.它找到了处理器映射器.(之前案例中 ...

  8. SpringMVC框架----SpringMVC的入门程序

    1.入门程序的需求 在这里插入图片描述 编写一个页面,叫index.jsp.页面上写一个超链接的标签,点击超链接,会发出一个请求,如果把请求发出去的话.后台编写类,接收请求,编写方法.发送请求,方法就 ...

  9. SpringMVC框架----SpringMVC 概述

    1.SpringMVC 是什么 SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 ...

  10. treegrid 与java交互_EXTJS实现的TREEGRID(后台java,框架SpringMVC)

    一.一些废话 近日来,有几个项目用到了EXTJS作为Web前端.也看到了一些童鞋苦苦在网上寻觅树形列表控件,碰巧项目中用到了一种,现在给大家分享一下. 二.前端 前端代码,用的是JS.主要就是指定数据 ...

最新文章

  1. GitHub上传代码、更新代码、token设置
  2. think in Java 第五章之垃圾回收类型
  3. 遗传算法(GA)中的编码方式-二进制编码、格雷编码、实数编码
  4. 2021年,投身自然语言处理是否明智?
  5. 视角不平衡立体匹配研究
  6. java的递归算法_如果要用Java实现算法,一定慎用递归
  7. python编写年金终值函数_看零件图的标题栏可了解()
  8. 查看进程动态:top
  9. kubernetes视频教程笔记 (24)-存储-PV和PVC
  10. 和foreach的区别和应用场景_介绍下Set、Map、WeakSet 和 WeakMap 的区别?
  11. c语言常用例子,C语言经典例子100个
  12. python中if语句格式_Python入门学习系列——Python中的if语句
  13. 交换机配置软件crt安装_模拟器安装使用
  14. 等保二级和等保三级的区别
  15. word页眉的横线怎么居中
  16. 案例:世界500强如何打造汽车后市场智慧门店
  17. 环境数据采集系统——中期总结
  18. QuickCam Gev 2.0 开发
  19. Unity可用的热更新方案
  20. copy() 与 clone() 的区别

热门文章

  1. 2022年100道最新软件测试面试题,常见面试题及答案汇总
  2. 2019 全球 AI 安防市场十大事件丨年终盘点
  3. python curl工具_python版本的curl工具pycurl学习
  4. 原子数组AtomicIntegerArray实现原理简析
  5. Android关闭软键盘的方法
  6. html的marquee标签,HTML Marquee标签
  7. 妙用PPT 2003刻录多媒体光盘
  8. Android 超简单音乐播放器(六)播放暂停音乐,更换播放模式,上下歌曲切换(service)
  9. Springboot整合layui之一个页面显示两张表格
  10. 【目录】办公技能集合