SpringMVC简介
我们首先要了解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视图,渲染数据后最终响应给浏览器
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
-
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。
-
在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet
SpringMVC的特点
-
Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
-
基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
-
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
-
代码清新简洁,大幅度提升开发效率
-
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
-
性能卓著,尤其适合现代大型、超大型互联网项目要求
SpringMVC的配置
在项目中我们需要配置的核心文件有web.xml
和与SpringMVC的配置文件
①web.xml
在该配置文件中我们可以配置:
-
编码过滤器请求方式的过滤器
-
前端控制器DispatcherServlet
-
SpringMVC和Spring配置文件自定义的位置和名称
-
DispatcherServlet的初始化时间
-
Spring的监听器,在服务器启动时加载Spring的配置文件
<?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">
<display-name>Archetype Created Web Application</display-name>
<!--配置Spring的编码过滤器-->
<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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置处理请求方式的过滤器-->
<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的前端控制器DispatcherServlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置SpringMVC配置文件自定义的位置和名称-->
<init-param>
<param-name>contextConfigLocation</param-name>
<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>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置Spring的监听器,在服务器启动时加载Spring的配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--设置Spring配置文件自定义的位置和名称-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
</web-app>
②SpringMVC配置文件
在该配置文件中我们需要配置的核心内容为视图解析器,我这里用的是Thymeleaf视图解析器
我们也可以在里面配置与注解相关的内容,如:
-
context:component-scan
:用于扫描组件 -
<mvc:default-servlet-handler/>
:配置默认的servlet处理静态资源,用于html,css,js静态资源的正常使用 -
<mvc:annotation-driven/>
:是视图控制器和<mvc:default-servlet-handler/>
所必备的 -
mvc:view-controller
:视图控制器,对于简单的控制器方法可以直接使用试图控制器替代
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描控制层组件-->
<context:component-scan base-package="com.zrb.controller"></context:component-scan>
<!-- 配置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>
<!--
配置默认的servlet处理静态资源
当前工程的web.xml配置的前端控制器DispatcherServlet的url-pattern是/
tomcat的web.xml配置的DefaultServlet的url-pattern也是/
此时,浏览器发送的请求会优先被DispatcherServlet进行处理,但是DispatcherServlet无法处理静态资源
若配置了<mvc:default-servlet-handler />,此时浏览器发送的所有请求都会被DefaultServlet处理
若配置了<mvc:default-servlet-handler />和<mvc:annotation-driven />
浏览器发送的请求会先被DispatcherServlet处理,无法处理在交给DefaultServlet处理
-->
<mvc:default-servlet-handler/>
<!--开启mvc的注解驱动-->
<mvc:annotation-driven/>
<!--
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404
此时必须在配置一个标签:<mvc:annotation-driven />
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
</beans>
基本使用
①在请求控制器中创建处理请求的方法
// @RequestMapping注解:处理请求和控制器方法之间的映射关系
// @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径,我也理解为虚拟目录
@RequestMapping("/")
public String index() {
//设置视图名称
return "index";
}
上述代码中返回值是一个字符串index
,其实他是一个静态资源页面,为其页面的逻辑视图,经过Thymeleaf视图解析器渲染后,加上<property name="prefix" value="/WEB-INF/templates/"/>
中values所表示的视图前缀和<property name="suffix" value=".html"/>
中所表示的试图后缀,就变成了物理视图,即该资源的完整路径
对于静态资源中的超链接标签(a标签),在不经过处理的情况下,如
<a href="hello">HelloWorld</a>
上述点击跳转后的链接是不带上下文路径的,这时我们可以用Thymeleaf视图解析器对其渲染后访问,就加上上下文路径了,如下
<a th:href="@{/hello}">HelloWorld</a>
上述大概过程流程为:
-
浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。
-
前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中 @RequestMapping 注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。
-
处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面
@RequestMapping注解
功能
-
@RequestMapping注解的作用是将请求和处理请求的控制器方法关联起来,建立映射关系。
-
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
位置
-
@RequestMapping标识一个类:设置映射请求的请求路径的初始信息
-
@RequestMapping标识一个方法:设置映射请求请求路径的具体信息
@RequestMapping注解的value属性
-
@RequestMapping注解的value属性通过请求的请求地址匹配请求映射
-
@RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
-
@RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射
@RequestMapping注解的method属性
-
@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
-
@RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错:
405:Request method ‘XXX’ not supported
注意
对于处理指定请求方式的控制器方法,SpringMVC中提供@RequestMapping的派生注解
-
处理get请求的映射–>@GetMapping
-
处理post请求的映射–>@PostMapping
-
处理put请求的映射–>@PutMapping
-
处理delete请求的映射–>@DeleteMapping
用这些派生注解后,只需设置该注解的value属性即可,其中默认设置了对应的method属性
常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理,若要发送put和delete请求,则需要通过spring提供的过滤:HiddenHttpMethodFilter,后面会说到
@RequestMapping注解的params属性
-
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射
-
@RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
-
“param”:表示当前所匹配请求的请求参数中必须携带param参数
-
“!param”:表示当前所匹配请求的请求参数中一定不能携带param参数
-
“param=value”:表示当前所匹配请求的请求参数中必须携带param参数且值必须为value
-
“param!=value”:表示当前所匹配请求的请求参数中可以不携带param,若携带值一定不能是value
-
用Thymeleaf对url进行参数加入可以用下面两种方式
<a th:href="@{/hello?username=admin}"></a>
<a th:href="@{/hello(username='admin')}"></a>
注意:
-
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求参数不匹配
-
此时页面报错:400 - Parameter conditions “xxx” not met for actual request parameters
@RequestMapping注解的headers属性(和params属性一样也有四种表达式设置请求参数和请求映射的匹配关系)
- 作用:通过请求的请求头信息匹配请求,即浏览器发送的请求的请求头信息必须满足headers属性的设置
- 若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求头信息不匹配,此时页面报错:404
SpringMVC支持ant风格的路径
在@RequestMapping注解的value属性值中设置一些特殊字符
?
:表示任意的单个字符(不包括?和/)*
:表示任意的0个或多个字符(不包括?和/)**
:表示任意层数的任意目录
注意:在使用**
时,只能使用/**/xxx的方式
如:
@RequestMapping("/**/test/ant")
SpringMVC支持路径中的占位符(重点)
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的 @RequestMapping 注解的value属性中通过占位符{xxx}表示传输的数据,在通过 @PathVariable 注解,将占位符所表示的数据赋值给控制器方法的形参
-
以前我们在路径中的参数样式:/deleteUser?id=1
-
用rest方式:/user/delete/1,把参数也作为路径的一部分
比如我们发送的请求的路径中的参数携带id=1&username=admin,用restful风格为:
<a th:href="@{/testRest/1/admin}"></a>
我们想要获得id和username的值就可以通过占位符来获取
@RequestMapping("/testRest/{id}/{username}")
在RequestMapping的value值中用{}
表示占位符,里面的内容代表所传来的请求参数,我们在请求方法中
public String testRest(@PathVariable("id") Integer id, @PathVariable("username") String username){}
使请求方法中的参数与在RequestMapping的value值中{}
的内容相同即可,如果相同,则不需要加其他操作,如果不相同,则需要在方法参数前加上 @PathVariable 注解,其中的value的值与在RequestMapping的value值中{}
的内容相同即可
SpringMVC获取请求参数
通过ServletAPI获取
因为我们处理请求的DispatcherServlet本身继承了HttpServlet,所以我们也可以用传统的servlet获取请求参数
我们只需将将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象,即SpringMVC会自动对控制器方法中的形参赋值或创造对象
如:
@RequestMapping("/test1")
public String testParam(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
return "success";
}
通过控制器方法的形参获取请求参数
SpringMVC中我们有更简单的方法获取请求参数
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参
如
<a th:href="@{/testParam(username='admin',password=123456)}"></a><br>
@RequestMapping("/testParam")
public String testParam(String username, String password){
return "success";
}
注意:
-
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
-
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
-
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
@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所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值
@RequestHeader和@CookieValue
@RequestHeader
-
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
-
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
@CookieValue
-
@CookieValue是将cookie数据和控制器方法的形参创建映射关系
-
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
注意:
-
在不使用注解的情况下,形参默认获取的是请求参数的数据
-
若想要获取请求头信息和cookie数据必须加入 @RequestHeader 和 @CookieValue 注解
使用
@RequestMapping("/param")
public String getParam(
@RequestParam(value = "userName", required = true, defaultValue = "hello") String username,
String password,
@RequestHeader("referer") String referer,
@CookieValue("JSESSIONID") String jsessionId
){
System.out.println("jsessionId:"+jsessionId);
System.out.println("referer:"+referer);
System.out.println("username:"+username+",password:"+password);
return "success";
}
通过POJO获取请求参数
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
注意:
-
需要在控制器方法的形参位置设置实体类类型的形参,要保证实体类中的属性的属性名和请求参数的名字一致
-
可以通过实体类类型的形参获取请求参数
解决获取请求参数的乱码问题
解决获取请求参数的乱码问题,在web.xml中配置Spring的编码过滤器CharacterEncodingFilter
<!--配置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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:
-
SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
-
即在除处理编码前不能获取任何参数
域对象共享数据
向request域对象共享数据
①使用ServletAPI向request域对象共享数据
因为我们处理请求的DispatcherServlet本身继承了HttpServlet,所以我们也可以用传统的servlet向request域对象共享数据
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("hello", "hello");
return "success";
}
②使用ModelAndView向request域对象共享数据
通过ModelAndView向请求域共享数据
-
使用ModelAndView时,可以使用其Model功能向请求域共享数据
-
使用View功能设置逻辑视图,但是控制器方法一定要将ModelAndView作为方法的返回值
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testScope", "hello,ModelAndView");
//设置视图,实现页面跳转
mav.setViewName("success");
return mav;
}
③使用Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("hello", "hello");
return "success";
}
④使用map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
map.put("hello", "hello");
return "success";
}
⑤使用ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("hello", "hello");
return "success";
}
Model、ModelMap、Map的关系
- 在底层中,这些类型的形参最终都是通过BindingAwareModelMap创建
向session域共享数据
用老方法即可
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("hello", "hello");
return "success";
}
向application域共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("hello", "hello");
return "success";
}
Thymeleaf获取域中数据
<p th:text="${hello}"></p>
<p th:text="${session.hello}"></p>
<p th:text="${application.hello}"></p>
只写域中数据的键的话,默认是获取request域中的数据,要想获取session和application域中的数据,则需要加上session.
和application.
前缀
SpringMVC的视图
-
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
-
SpringMVC视图的种类很多,默认有转发视图和重定向视图
-
当工程引入jstl的依赖,转发视图会自动转换为JstlView
-
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
ThymeleafView
-
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析
-
视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
转发视图
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况:
-
当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图
-
此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
-
例如"forward:/",“forward:/employee”
重定向视图
SpringMVC中默认的重定向视图是RedirectView
SpringMVC中创建重定向视图的情况:
-
当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图
-
此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
-
例如"redirect:/",“redirect:/employee”
视图控制器view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示,即对于简单的控制器方法可以直接使用试图控制器替代
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称(逻辑视图)
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
注意:
-
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效
-
此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />
RESTful
概述
- REST:Representational State Transfer,表述(表征)性状态转移。
RESTful是一种架构的规范与约束、原则,符合下述规范的架构就是RESTful架构
-
对网络上所有的资源都有一个资源标志符。
-
对资源的操作不会改变标识符。
-
同一资源有多种表现形式(xml、json)
-
所有操作都是无状态的(Stateless)
RESTful的实现
-
其实就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
-
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。
-
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
操作 | 传统方式 | REST风格 |
---|---|---|
查询操作 | getUserById?id=1 | user/1–>get请求方式 |
保存操作 | saveUser | user–>post请求方式 |
删除操作 | deleteUser?id=1 | user/1–>delete请求方式 |
更新操作 | updateUser | user–>put请求方式 |
HiddenHttpMethodFilter
HiddenHttpMethodFilter简介
-
由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?
-
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求
HiddenHttpMethodFilter 处理put和delete请求的条件:
-
当前请求的请求方式必须为post
-
当前请求必须传输请求参数_method
满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式
在web.xml中注册HiddenHttpMethodFilter
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass>
</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);
SpringMVC处理ajax请求
@RequestBody
@RequestBody可以获取请求体信息,使用@RequestBody注解标识控制器方法的形参,当前请求的请求体就会为当前注解所标识的形参赋值
<!--此时必须使用post请求方式,因为get请求没有请求体-->
<form th:action="@{/test}" method="post">
<input type="submit">
</form>
@RequestMapping("/test")
public String testRequestBody(@RequestBody String requestBody){
return "success";
}
@RequestBody获取json格式的请求参数
在使用了axios发送ajax请求之后,浏览器发送到服务器的请求参数有两种格式:
- ①
params
的形式:name=value&name=value…,此时的请求参数可以通过request.getParameter()获取,对应SpringMVC中,可以直接通过控制器方法的形参获取此类请求参数
-
②
data
的形式:{key:value,key:value,…},此时无法通过request.getParameter()获取,之前我们使用操作json的相关jar包gson或jackson处理此类请求参数,可以将其转换为指定的实体类对象或map集合。- 而在SpringMVC中,直接使用@RequestBody注解标识控制器方法的形参即可将此类请求参数转换为java对象
使用@RequestBody获取json格式的请求参数的条件:
- 导入jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
- SpringMVC的配置文件中设置开启mvc的注解驱动
<!--开启mvc的注解驱动-->
<mvc:annotation-driven />
- 在控制器方法的形参位置,设置json格式的请求参数要转换成的java类型(实体类或map)的参数,并使用@RequestBody注解标识
@ResponseBody
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器。
不加@ResponseBody的情况
@RequestMapping("/testResponseBody")
public String testResponseBody(){
//此时会跳转到逻辑视图success所对应的页面
return "success";
}
加@ResponseBody的情况
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
//此时响应浏览器数据success
return "success";
}
@ResponseBody响应浏览器json数据
-
服务器处理ajax请求之后,大多数情况都需要向浏览器响应一个java对象,此时必须将java对象转换为json字符串才可以响应到浏览器,之前我们使用操作json数据的jar包gson或jackson将java对象转换为json字符串。
-
在SpringMVC中,我们可以直接使用@ResponseBody注解实现此功能
@ResponseBody响应浏览器json数据的条件:
-
导入jackson的依赖
-
SpringMVC的配置文件中设置开启mvc的注解驱动
-
使用@ResponseBody注解标识控制器方法,在方法中,将需要转换为json字符串并响应到浏览器的java对象作为控制器方法的返回值,此时SpringMVC就可以将此对象直接转换为json字符串并响应到浏览器
@RestController注解
@RestController注解是SpringMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解
文件上传和下载
文件下载
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
使用ResponseEntity实现下载文件的功能
@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("img");
realPath = realPath + File.separator + "1.jpg";
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组,is.available()获取输入流所对应文件的字节数
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<byte[]>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
我们把它当作一个模板就行,不用记,我们只需要知道
和
这部分是可以自定义修改的就行
文件上传
-
文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data”
-
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
<form enctype="multipart/form-data" method="post">
</form>
上传步骤:
①添加依赖:
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --
>
<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>
③控制器方法:
@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("photo");
File file = new File(photoPath);
if(!file.exists()){
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//实现上传功能
photo.transferTo(new File(finalPath));
return "success";
}
拦截器
拦截器的配置
- SpringMVC中的拦截器用于拦截控制器方法的执行
- SpringMVC中的拦截器需要实现HandlerInterceptor
我们先创建一个拦截器FirstInterceptor ,并重写其中的一些方法
@Component
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<mvc:interceptors>
<!-- bean和ref标签所配置的拦截器默认对DispatcherServlet处理的所有的请求进行拦截-->
<bean class="com.zrb.FirstInterceptor"/>
<ref bean="firstInterceptor" />
<!-- <mvc:interceptor>
<!–配置需要拦截的请求的请求路径,/**表示所有请求–>
<mvc:mapping path="/**"/>
<!–配置需要排除拦截的请求的请求路径–>
<mvc:exclude-mapping path="/abc"/>
<!–配置拦截器–>
<ref bean="firstInterceptor" />
</mvc:interceptor>-->
</mvc:interceptors>
拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
-
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
-
postHandle:控制器方法执行之后执行postHandle()
-
afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
多个拦截器的执行顺序
①若每个拦截器的preHandle()都返回true
-
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关
-
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
②若某个拦截器的preHandle()返回了false
- preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterCompletion()会执行
异常处理器
基于配置的异常处理
-
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
-
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
基于注解的异常处理
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}
注解配置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 {
@Override
//设置一个配置类代替Spring的配置文件
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
//设置一个配置类代替SpringMVC的配置文件
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
//设置SpringMVC的前端控制器DispatcherServlet的url-pattern
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
//设置当前的过滤器
protected Filter[] getServletFilters() {
//创建编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
//创建处理请求方式的过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
创建SpringConfig配置类,代替spring的配置文件
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}
创建WebConfig配置类,代替SpringMVC的配置文件
//将类标识为配置类
@Configuration
//扫描组件
@ComponentScan("com.atguigu.controller")
//开启mvc的注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
//默认的servlet处理静态资源
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
//配置视图控制器
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
//@Bean注解可以将标识的方法的返回值作为bean进行管理,bean的id为方法的方法名
@Bean
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
@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;
}
}