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是一种基于Java实现MVC模型的轻量级Web框架
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC 通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
1.3 SpringMVC的特点
- Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
- 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一 处理
- 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
- 代码清新简洁,大幅度提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
- 性能卓著,尤其适合现代大型、超大型互联网项目要求
1.4 SpringMVC的执行流程
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet调用HandlerAdapter处理器适配器。
(5)HandlerAdapter经过适配调用具体的Handler处理器(Controller,也叫后端控制器)。Controller执行完成返回ModelAndView。
(6)HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
(7)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
(8)ViewReslover解析后返回具体View视图。
(9)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
(10)DispatcherServlet响应用户。
2. SpringMVC入门案例【重点】
2.1 使用maven搭建web项目
(方式有多种,这里只是其中一种)
(1)创建普通maven项目
(2)在项目的main文件夹下新建文件夹webapps
(3)选择File--->Project Structure
(4)选择Facets--->+--->web
(5)选择项目中的对应模块
(6)修改Deployment Descriptors下web.xml路径
(7)修改Web Resource Directory路径
2.2 运用xml的方式
(1)导入SpringMVC相关坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
</dependencies>
(2)在webapps下创建hello.jsp视图页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
你好,SpringMVC!
</body>
</html>
(3)在com.gcxy.controller包下创建HelloController控制器
package com.gcxy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("hello···");
return "hello.jsp";
}
}
(4)在resources文件夹下创建SpringMVC核心文件spring-mvc.xml,并添加注解扫描
(5)web.xml中配置SpringMVC核心控制器DispathcerServlet
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
(6)配置Tomcat并启动
-
选择Run--->Edit Configurations
- 选择+--->Tomcat Server--->Local
- 选择本地tomcat所在的地址【tomcat的bin目录上一层路径】,点击Fix
- 选择Artifacts--->Web Application:Exploded--->From Modules
- 选择项目中的对应模块,选择OK--->继续选择OK
- 启动Tomcat
(7)客户端向路径 http://localhost:8080/mvc_demo_war_exploded/hello发起请求测试
2.3 运用注解的方式
(1)添加servlet依赖,并新建com.gcxy.config包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
(2)在config包下创建配置类SpringMvcConfig替换spring-mvc.xml
package com.gcxy.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.cqgcxy.controller")
public class SpringMvcConfig {
}
(3)在config包下创建配置类ServletContainersInitConfig替换web.xml
package com.gcxy.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
(4)启动Tomcat,并测试
测试结果如下:
3. @RequestMapping注解
3.1 @RequestMapping注解的功能
用于建立请求URL和处理请求方法之间的对应关系
3.2 @RequestMapping注解的位置
-
类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录
-
方法上,请求URL的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
3.3 @RequestMapping注解的属性
-
value:用于指定请求的URL。它和path属性的作用是一样的
-
method:用于指定请求的方式
-
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
-
例如:
-
params = {"accountName"},表示请求参数必须有accountName
-
-
4.SpringMVC多种类型数据响应
4.1 视图解析器
SpringMVC有默认组件配置,默认组件都是Dispatcherservlet.properties配置文件中配置的,该配置文件地址
org/springframework/web/ servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceviewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
public static final String REDIRECT_URL_PREFIX = "redirect:"; //重定向前缀
public static final String FORWARD_URL_PREFIX = "forward:"; //请求转发前缀
private String prefix = ""; //视图名称前缀
private String suffix = ""; //视图名称后缀
4.2 SpringMVC的数据响应方式
-
页面跳转
直接返回字符串
通过ModelAndView对象返回
-
回写数据
直接返回字符串
返回对象或集合
-
json转换
-
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:
-
-
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</list>
</property>
</bean>
- 在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用<mvc:annotation-driven>会自动加载RequestMappingHandlerMapping (处理映射器)和 RequestMappingHandlerAdapter(处理适配器),可用在spring-mvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。
-
同时使用<mvc:annotation-driven>默认底层就会集成jackson进行对象或集合的json格式字符串的转换。因此,我们可以使用mvc的注解驱动代替上述为处理器适配器配置消息转换参数的配置。
<mvc:annotation-driven/>
4.3 实现页面跳转
在SpringMVC项目搭建的基础上完成下面步骤
(1)配置视图解析器
-
xml方式:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
- 注解方式:
//在spring配置类中加入视图解析器的配置
@Bean
public InternalResourceViewResolver internalResourceViewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
- 编写控制器和处理请求的方法
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("hello···");
return "hello";
}
}
(2)返回字符串
-
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
资源地址:/WEB-INF/views/hello.jsp
-
返回带有前缀的字符串:
转发:forward:/WEB-INF/views/hello.jsp
重定向:redirect:/index.jsp
@RequestMapping("/forward")
public String forword(){
System.out.println("forward···");
return "forward:/WEB-INF/views/index.jsp";
}
@RequestMapping("/redirect")
public String redirect(){
System.out.println("redirect···");
return "redirect:/login.jsp";
}
(3)返回ModelAndView对象
-
修改hello.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
你好,SpringMVC!${username}
</body>
</html>
- 编写控制器中处理请求的方法
@RequestMapping("/hello2")
public ModelAndView hello2(){
//Model:模型,用于封装数据
//View:视图,用于展示数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username","pep7chiao");
modelAndView.setViewName("hello");
return modelAndView;
}
@RequestMapping("/hello3")
public ModelAndView hello3(ModelAndView modelAndView){
modelAndView.addObject("username","pep7chiao");
modelAndView.setViewName("hello");
return modelAndView;
}
@RequestMapping("/hello4")
public String hello4(Model model){
model.addAttribute("username","messi");
return "hello";
}
@RequestMapping("/hello5")
public String hello5(HttpServletRequest reqest){ //HttpServletRequest需要添加依赖
reqest.setAttribute("username","ronaldo");
return "hello";
}
4.4 回写数据
(1)直接返回字符串
-
通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void。
@RequestMapping("/data1")
public void data1(HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("天天开心,拒绝焦虑");
}
- 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转,而是直接在http响应体中返回。
@RequestMapping(value = "/data2",produces = "text/html;charset=utf-8")
@ResponseBody
public String data2(){
return "拒绝焦虑";
}
(2)返回对象或集合
-
导入json相关依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- 开启mvc的注解驱动
-
xml方式:spring核心配置文件中加以下配置
<mvc:annotation-driven/>
- 注解方式:spring配置类上加以下注解
@EnableWebMvc
- 在com.cqgcxy.entity包下创建实体类
package com.gcxy.entity;
public class Phone {
// phone_id bigint
private Long phoneId;
// brand_id bigint
private Long brandId;
// model_number varchar
private String modelNumber;
// capacity int
private Integer capacity;
public Long getPhoneId() {
return phoneId;
}
public void setPhoneId(Long phoneId) {
this.phoneId = phoneId;
}
public Long getBrandId() {
return brandId;
}
public void setBrandId(Long brandId) {
this.brandId = brandId;
}
public String getModelNumber() {
return modelNumber;
}
public void setModelNumber(String modelNumber) {
this.modelNumber = modelNumber;
}
public Integer getCapacity() {
return capacity;
}
public void setCapacity(Integer capacity) {
this.capacity = capacity;
}
@Override
public String toString() {
return "Phone{" +
"phoneId=" + phoneId +
", brandId=" + brandId +
", modelNumber='" + modelNumber + '\'' +
", capacity=" + capacity +
'}';
}
}
- 编写控制器中处理请求的方法
@RequestMapping("/data3")
@ResponseBody
public Phone data3() {
Phone phone = new Phone();
phone.setPhoneId(1L);
phone.setBrandId(1L);
phone.setModelNumber("mate60");
phone.setCapacity(256);
return phone;
}
5.SpringMVC获取请求参数
5.1 客户端请求参数的格式
name=value&name=value.....
5.2 SpringMVC可以接收的参数类型
-
基本类型参数
-
POJO类型参数
-
数组类型参数
-
集合类型参数
注意:当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定
5.3 获取基本类型参数
在SpringMVC多种数据类型响应的基础上完成下面步骤
(1)Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param1?phoneId=1&modelNumber=mate60
@Controller
@RequestMapping("/phone")
public class PhoneController {
@RequestMapping("/param1")
@ResponseBody
public void param1(int phoneId,String modelNumber){
System.out.println(phoneId);
System.out.println(modelNumber);
}
}
(2)当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。
http://localhost:8888/phone/param2?phoneId=1&mNumber=mate60
@RequestMapping("/param2")
@ResponseBody
public void param2(int phoneId,@RequestParam("mNumber") String modelNumber){
System.out.println(phoneId);
System.out.println(modelNumber);
}
(3)注解@RequestParam还有如下参数可以使用:
-
value::请求参数名称
-
required::指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
-
defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
http://localhost:8888/phone/param3?phoneId=1
@RequestMapping("/param3")
@ResponseBody
public void param3(int phoneId,@RequestParam(value = "mNumber",required = false,defaultValue = "mate60") String modelNumber){
System.out.println(phoneId);
System.out.println(modelNumber);
}
5.4 获取POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param4?phoneId=1&modelNumber=mate60
package com.cqgcxy.entity;
public class Phone {
private Long phoneId;
private String modelNumber;
//忽略getter和setter方法
}
@RequestMapping("/param4")
@ResponseBody
public void param4(Phone phone){
System.out.println(phone.getPhoneId());
System.out.println(phone.getModelNumber());
}
5.5 获取数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param5?nets=4G&nets=5G
@RequestMapping("/param5")
@ResponseBody
public void param5(String[] nets){
System.out.println(Arrays.asList(nets));
}
http://localhost:8888/phone/param6?nets=4G&nets=5G
@RequestMapping("/param6")
@ResponseBody
public void param6(@RequestParam List<String> nets){
System.out.println(nets);
}
5.6 获取集合类型参数
获取集合参数时,要将集合参数包装到一个POJO中
package com.gcxy.vo;
import com.cqgcxy.entity.Phone;
import java.util.List;
public class PhoneStoreDTO {
private String storeName;
private List<Phone> phoneList;
//getter和setter方法
}
<form action="${pageContext.request.contextPath}/phone/param7" method="post">
<p>
店名:<input type="text" name="storeName">
</p>
<p>
手机编号1:<input type="text" name="phoneList[0].phoneId">
</p>
<p>
手机型号1:<input type="text" name="phoneList[0].modelNumber">
</p>
<p>
手机编号2:<input type="text" name="phoneList[1].phoneId">
</p>
<p>
手机型号2:<input type="text" name="phoneList[1].modelNumber">
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
@RequestMapping(value = "/param7",method = RequestMethod.POST)
@ResponseBody
public void param7(PhoneStoreDTO phoneStoreDTO){
System.out.println(phoneStoreDTO);
}
乱码问题:当表单提交post请求时,数据会出现乱码,可以设置一个过滤器来进行编码的过滤。
-
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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<servlet-name>dispatcherServlet</servlet-name>
</filter-mapping>
- 注解方式:
//ServletContainersInitConfig配置类中加入
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
当前端使用json格式传输数据时,方法参数位置使用@RequestBody可以直接接收集合数据,而无需使用POJO进行包装。
-
使用Postman传输json格式数据
- 添加json相关依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- 启动web mvc配置
- Controller中编写接收数据接口
@RequestMapping(value = "/param8",method = RequestMethod.POST)
@ResponseBody
public void param8(@RequestBody List<Phone> phoneList){
System.out.println(phoneList);
}
5.7 获得Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用“url+请求方式”表示一次请求目的,HTTP协议里面四个表示操作方式的动词如下:
-
GET:用于获取资源
-
POST:用于新建资源
-
PUT:用于更新资源
-
DELETE:用于删除资源
例如:
-
/user/1 GET :得到id =1的user
-
/user/1 DELETE:删除id = 1的user
-
/user/1 PUT:更新id = 1的user
-
/user POST:新增user
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。
http://localhost:8888/phone/param9/7
@RequestMapping("/param9/{phoneId}")
@ResponseBody
public void param9(@PathVariable("phoneId") Integer phoneId){
System.out.println(phoneId);
}
5.8 自定义类型转换器
SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
注意:@DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。和@RequestParam注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。
自定义类型转换器的开发步骤:
(1)
//定义转换器类实现Converter接口
public class DateConverter implements Converter<String, Date> {
public Date convert(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
//在springmvc配置类中配置自定义类型转换器
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter());
}
};
}
http://localhost:8888/phone/param10/2022-10-10%202008:08:08
@RequestMapping("/param10/{date}")
@ResponseBody
public void param10(@PathVariable("date") Date date){
System.out.println(date);
}
(2)Spring MVC 接收 LocalDate、LocalTime 和 LocalDateTime Java 8 时间类型参数
org.springframework.web.bind.annotation.RequestParam
org.springframework.format.annotation.DateTimeFormat
-
@RequestParam 比较常见,用于标注 Controller 中方法的参数;
-
@DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。两个注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。
示例如下:
-
将 2020-01-29 转换成 LocalDate
//http://localhost:8888/phone/param11/2022-02-02
@RequestMapping("/param11/{date}")
@ResponseBody
public void param11(@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @PathVariable("date") LocalDate date){
System.out.println(date);
}
//http://localhost:8888/phone/param12?date=2022-02-02
@RequestMapping("/param12")
@ResponseBody
public void param12(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date){
System.out.println(date);
}
- 将 12:25:30 转换成 LocalTime
//http://localhost:8888/phone/param13?time=08:08:08
@RequestMapping("/param13")
@ResponseBody
public void param13(@RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime time){
System.out.println(time);
}
- 将 2020-01-29 12:25:30 转换成 LocalDateTime
//http://localhost:8888/phone/param14?dateTime=2022-08-08 08:08:08
@RequestMapping("/param14")
@ResponseBody
public void param14(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime){
System.out.println(dateTime);
}
5.9 获取请求头
注意:
-
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
-
使用@CookieValue可以获得指定Cookie的值
(1)@RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
-
value:请求头的名称
-
required:是否必须携带此请求头
@RequestMapping("/param15")
@ResponseBody
public void param15(@RequestHeader(value = "user-Agent",required = false) String userAgent){
System.out.println(userAgent);
}
(2)@CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
-
value:指定cookie的名称
-
required:是否必须携带此cookie
@RequestMapping("/param16")
@ResponseBody
public void param16(@CookieValue("JSESSIONID") String jSessionId){
System.out.println(jSessionId);
}
6.实现文件上传和下载
6.1 文件上传
(1)导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
(2)配置文件上传解析器
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(5242880);
commonsMultipartResolver.setMaxUploadSizePerFile(5242880);
commonsMultipartResolver.setDefaultEncoding("UTF-8");
return commonsMultipartResolver;
}
(3)编写文件上传代码
@RequestMapping("/upload")
@ResponseBody
public void upload(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
System.out.println("文件名称:"+originalFilename);
file.transferTo(new File("D:\\upload\\"+originalFilename));
}
测试结果如下:
6.2 文件下载
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
使用ResponseEntity实现下载文件的功能
@RequestMapping("/down")
public ResponseEntity<byte[]> testDown(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("img");
realPath = realPath + File.separator + "测试.xlsx";
//创建输入流
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=测试.xlsx");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
7.拦截器
7.1 拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置
7.2 拦截器的作用
Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
7.3 拦截器的方法说明
方法名 | 说明 |
---|---|
preHandle | 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法 |
postHandle | 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 |
afterCompletion | 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用 |
7.3 拦截器和过滤器的区别
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何Java Web工程都可以使用 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用 |
拦截范围 | 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js 是不会进行拦截的 |
7.4 多个拦截器的执行顺序
①若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
②若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false 的拦截器之前的拦截器的afterCompletion()会执行
7.5 实现使用拦截器验证用户是否登录
在SpringMVC获取请求参数的基础上完成下面步骤
(1)创建拦截器
package com.gcxy.config.intercepter;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginIntercepter implements HandlerInterceptor {
/**
* 预处理:可以进行编码、安全控制、权限校验等处理
* 在业务处理器处理请求之前被调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authorization = request.getHeader("Authorization");
if (ObjectUtils.isEmpty(authorization)) {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json; charset=utf-8");
response.getWriter().write("权限不足");
return false;
}
HttpSession session = request.getSession();
Object obj = session.getAttribute(authorization);
if(ObjectUtils.isEmpty(obj)){
response.getWriter().write("token已过期");
return false;
}
return true;
}
/**
* 后处理(调用了Service并返回ModelAndView,但未进行页面渲染)
* 在业务处理器处理请求执行完成后,生成视图之前执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
* 返回处理(已经渲染了页面)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
(2)配置拦截器
@Configuration
@ComponentScan("com.cqgcxy.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginIntercepter()).addPathPatterns(new String[]{"/**"}).excludePathPatterns(new String[]{"/login"});
}
}
(3)测试拦截器
- 使用Lombok创建实体类
-
导入Lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
- 创建实体类
package com.gcxy.entity;
import lombok.Data;
@Data
public class Account {
private String username;
private String password;
}
- 创建控制器
package com.gcxy.controller;
import com.cqgcxy.entity.Account;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.UUID;
@RestController
public class AccountController {
@RequestMapping(value = "/login",produces = "text/html;charset=utf-8")
public String login(@RequestBody Account account, HttpSession session) {
//模拟登录验证
if("admin".equals(account.getUsername())&&"123456".equals(account.getPassword())){
UUID uuid = UUID.randomUUID();
session.setAttribute(uuid.toString(),account);
return uuid.toString();
}
return "账号或密码错误";
}
}
测试结果如下:
1.客户端使用Postman向获取参数类型的资源发起请求:
2.客户端使用Postman向/login发起请求 :
3. 客户端使用Postman请求头中携带上登录时返回的字符串,重新请求获取参数类型中刚刚请求的资源: