当前位置:   article > 正文

SpringMVC----全面详解

springmvc

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相关坐标

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>5.3.8</version>
  6. </dependency>
  7. </dependencies>

(2)在webapps下创建hello.jsp视图页面

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>Title</title>
  5. </head>
  6. <body>
  7. 你好,SpringMVC!
  8. </body>
  9. </html>

(3)在com.gcxy.controller包下创建HelloController控制器

 

  1. package com.gcxy.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. @Controller
  5. public class HelloController {
  6. @RequestMapping("/hello")
  7. public String hello(){
  8. System.out.println("hello···");
  9. return "hello.jsp";
  10. }
  11. }

(4)在resources文件夹下创建SpringMVC核心文件spring-mvc.xml,并添加注解扫描

 (5)web.xml中配置SpringMVC核心控制器DispathcerServlet

  1. <servlet>
  2. <servlet-name>dispatcherServlet</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:spring-mvc.xml</param-value>
  7. </init-param>
  8. <load-on-startup>1</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>dispatcherServlet</servlet-name>
  12. <url-pattern>/</url-pattern>
  13. </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包

  1. <dependency>
  2. <groupId>javax.servlet</groupId>
  3. <artifactId>javax.servlet-api</artifactId>
  4. <version>4.0.1</version>
  5. </dependency>

(2)在config包下创建配置类SpringMvcConfig替换spring-mvc.xml

  1. package com.gcxy.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @ComponentScan("com.cqgcxy.controller")
  6. public class SpringMvcConfig {
  7. }

(3)在config包下创建配置类ServletContainersInitConfig替换web.xml

  1. package com.gcxy.config;
  2. import org.springframework.web.context.WebApplicationContext;
  3. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
  4. import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
  5. public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
  6. @Override
  7. protected WebApplicationContext createServletApplicationContext() {
  8. AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
  9. ctx.register(SpringMvcConfig.class);
  10. return ctx;
  11. }
  12. @Override
  13. protected String[] getServletMappings() {
  14. return new String[]{"/"};
  15. }
  16. protected WebApplicationContext createRootApplicationContext() {
  17. return null;
  18. }
  19. }

(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

 翻看该解析器源码,可以看到该解析器的默认设置,如下:

  1. public static final String REDIRECT_URL_PREFIX = "redirect:"; //重定向前缀
  2. public static final String FORWARD_URL_PREFIX = "forward:"; //请求转发前缀
  3. private String prefix = ""; //视图名称前缀
  4. private String suffix = ""; //视图名称后缀

 4.2 SpringMVC的数据响应方式

  • 页面跳转

    直接返回字符串

    通过ModelAndView对象返回

  • 回写数据

    直接返回字符串

    返回对象或集合

    • json转换

      • 通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:

  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  2. <property name="messageConverters">
  3. <list>
  4. <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
  5. </bean>
  6. </list>
  7. </property>
  8. </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方式:

  1. <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  2. <property name="prefix" value="/WEB-INF/views/"></property>
  3. <property name="suffix" value=".jsp"></property>
  4. </bean>
  • 注解方式:
  1. //在spring配置类中加入视图解析器的配置
  2. @Bean
  3. public InternalResourceViewResolver internalResourceViewResolver(){
  4. InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  5. viewResolver.setPrefix("/WEB-INF/views/");
  6. viewResolver.setSuffix(".jsp");
  7. return viewResolver;
  8. }
  •  编写控制器和处理请求的方法
  1. @Controller
  2. public class HelloController {
  3. @RequestMapping("/hello")
  4. public String hello(){
  5. System.out.println("hello···");
  6. return "hello";
  7. }
  8. }

(2)返回字符串

  • 直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转

    资源地址:/WEB-INF/views/hello.jsp

  • 返回带有前缀的字符串:

    转发:forward:/WEB-INF/views/hello.jsp

    重定向:redirect:/index.jsp

  1. @RequestMapping("/forward")
  2. public String forword(){
  3. System.out.println("forward···");
  4. return "forward:/WEB-INF/views/index.jsp";
  5. }
  6. @RequestMapping("/redirect")
  7. public String redirect(){
  8. System.out.println("redirect···");
  9. return "redirect:/login.jsp";
  10. }

(3)返回ModelAndView对象

  • 修改hello.jsp页面

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>Title</title>
  5. </head>
  6. <body>
  7. 你好,SpringMVC!${username}
  8. </body>
  9. </html>
  • 编写控制器中处理请求的方法
  1. @RequestMapping("/hello2")
  2. public ModelAndView hello2(){
  3. //Model:模型,用于封装数据
  4. //View:视图,用于展示数据
  5. ModelAndView modelAndView = new ModelAndView();
  6. modelAndView.addObject("username","pep7chiao");
  7. modelAndView.setViewName("hello");
  8. return modelAndView;
  9. }
  10. @RequestMapping("/hello3")
  11. public ModelAndView hello3(ModelAndView modelAndView){
  12. modelAndView.addObject("username","pep7chiao");
  13. modelAndView.setViewName("hello");
  14. return modelAndView;
  15. }
  16. @RequestMapping("/hello4")
  17. public String hello4(Model model){
  18. model.addAttribute("username","messi");
  19. return "hello";
  20. }
  21. @RequestMapping("/hello5")
  22. public String hello5(HttpServletRequest reqest){ //HttpServletRequest需要添加依赖
  23. reqest.setAttribute("username","ronaldo");
  24. return "hello";
  25. }

4.4 回写数据

(1)直接返回字符串

  • 通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void。

  1. @RequestMapping("/data1")
  2. public void data1(HttpServletResponse response) throws IOException {
  3. response.setContentType("text/html;charset=utf-8");
  4. response.getWriter().print("天天开心,拒绝焦虑");
  5. }
  • 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转,而是直接在http响应体中返回。
  1. @RequestMapping(value = "/data2",produces = "text/html;charset=utf-8")
  2. @ResponseBody
  3. public String data2(){
  4. return "拒绝焦虑";
  5. }

(2)返回对象或集合

  • 导入json相关依赖

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>2.9.0</version>
  5. </dependency>
  • 开启mvc的注解驱动
  • xml方式:spring核心配置文件中加以下配置

<mvc:annotation-driven/>
  •  注解方式:spring配置类上加以下注解
@EnableWebMvc
  •  在com.cqgcxy.entity包下创建实体类
  1. package com.gcxy.entity;
  2. public class Phone {
  3. // phone_id bigint
  4. private Long phoneId;
  5. // brand_id bigint
  6. private Long brandId;
  7. // model_number varchar
  8. private String modelNumber;
  9. // capacity int
  10. private Integer capacity;
  11. public Long getPhoneId() {
  12. return phoneId;
  13. }
  14. public void setPhoneId(Long phoneId) {
  15. this.phoneId = phoneId;
  16. }
  17. public Long getBrandId() {
  18. return brandId;
  19. }
  20. public void setBrandId(Long brandId) {
  21. this.brandId = brandId;
  22. }
  23. public String getModelNumber() {
  24. return modelNumber;
  25. }
  26. public void setModelNumber(String modelNumber) {
  27. this.modelNumber = modelNumber;
  28. }
  29. public Integer getCapacity() {
  30. return capacity;
  31. }
  32. public void setCapacity(Integer capacity) {
  33. this.capacity = capacity;
  34. }
  35. @Override
  36. public String toString() {
  37. return "Phone{" +
  38. "phoneId=" + phoneId +
  39. ", brandId=" + brandId +
  40. ", modelNumber='" + modelNumber + '\'' +
  41. ", capacity=" + capacity +
  42. '}';
  43. }
  44. }
  •  编写控制器中处理请求的方法
  1. @RequestMapping("/data3")
  2. @ResponseBody
  3. public Phone data3() {
  4. Phone phone = new Phone();
  5. phone.setPhoneId(1L);
  6. phone.setBrandId(1L);
  7. phone.setModelNumber("mate60");
  8. phone.setCapacity(256);
  9. return phone;
  10. }

 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

  1. @Controller
  2. @RequestMapping("/phone")
  3. public class PhoneController {
  4. @RequestMapping("/param1")
  5. @ResponseBody
  6. public void param1(int phoneId,String modelNumber){
  7. System.out.println(phoneId);
  8. System.out.println(modelNumber);
  9. }
  10. }

(2)当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。

http://localhost:8888/phone/param2?phoneId=1&mNumber=mate60

  1. @RequestMapping("/param2")
  2. @ResponseBody
  3. public void param2(int phoneId,@RequestParam("mNumber") String modelNumber){
  4. System.out.println(phoneId);
  5. System.out.println(modelNumber);
  6. }

(3)注解@RequestParam还有如下参数可以使用:

  • value::请求参数名称

  • required::指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错

  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值

http://localhost:8888/phone/param3?phoneId=1

  1. @RequestMapping("/param3")
  2. @ResponseBody
  3. public void param3(int phoneId,@RequestParam(value = "mNumber",required = false,defaultValue = "mate60") String modelNumber){
  4. System.out.println(phoneId);
  5. System.out.println(modelNumber);
  6. }

5.4 获取POJO类型参数

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。

http://localhost:8888/phone/param4?phoneId=1&modelNumber=mate60

  1. package com.cqgcxy.entity;
  2. public class Phone {
  3. private Long phoneId;
  4. private String modelNumber;
  5. //忽略getter和setter方法
  6. }
  1. @RequestMapping("/param4")
  2. @ResponseBody
  3. public void param4(Phone phone){
  4. System.out.println(phone.getPhoneId());
  5. System.out.println(phone.getModelNumber());
  6. }

5.5 获取数组类型参数

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。

http://localhost:8888/phone/param5?nets=4G&nets=5G

  1. @RequestMapping("/param5")
  2. @ResponseBody
  3. public void param5(String[] nets){
  4. System.out.println(Arrays.asList(nets));
  5. }

http://localhost:8888/phone/param6?nets=4G&nets=5G

  1. @RequestMapping("/param6")
  2. @ResponseBody
  3. public void param6(@RequestParam List<String> nets){
  4. System.out.println(nets);
  5. }

5.6 获取集合类型参数

获取集合参数时,要将集合参数包装到一个POJO中

  1. package com.gcxy.vo;
  2. import com.cqgcxy.entity.Phone;
  3. import java.util.List;
  4. public class PhoneStoreDTO {
  5. private String storeName;
  6. private List<Phone> phoneList;
  7. //getter和setter方法
  8. }
  1. <form action="${pageContext.request.contextPath}/phone/param7" method="post">
  2. <p>
  3. 店名:<input type="text" name="storeName">
  4. </p>
  5. <p>
  6. 手机编号1:<input type="text" name="phoneList[0].phoneId">
  7. </p>
  8. <p>
  9. 手机型号1:<input type="text" name="phoneList[0].modelNumber">
  10. </p>
  11. <p>
  12. 手机编号2:<input type="text" name="phoneList[1].phoneId">
  13. </p>
  14. <p>
  15. 手机型号2:<input type="text" name="phoneList[1].modelNumber">
  16. </p>
  17. <p>
  18. <input type="submit" value="提交">
  19. </p>
  20. </form>
  1. @RequestMapping(value = "/param7",method = RequestMethod.POST)
  2. @ResponseBody
  3. public void param7(PhoneStoreDTO phoneStoreDTO){
  4. System.out.println(phoneStoreDTO);
  5. }

乱码问题:当表单提交post请求时,数据会出现乱码,可以设置一个过滤器来进行编码的过滤。

  • xml方式:

  1. <filter>
  2. <filter-name>characterEncodingFilter</filter-name>
  3. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  4. <init-param>
  5. <param-name>encoding</param-name>
  6. <param-value>utf-8</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>characterEncodingFilter</filter-name>
  11. <servlet-name>dispatcherServlet</servlet-name>
  12. </filter-mapping>
  •  注解方式:
  1. //ServletContainersInitConfig配置类中加入
  2. @Override
  3. protected Filter[] getServletFilters() {
  4. CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
  5. characterEncodingFilter.setEncoding("UTF-8");
  6. return new Filter[]{characterEncodingFilter};
  7. }

当前端使用json格式传输数据时,方法参数位置使用@RequestBody可以直接接收集合数据,而无需使用POJO进行包装。

  • 使用Postman传输json格式数据

 

  • 添加json相关依赖  
  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>2.9.0</version>
  5. </dependency>
  • 启动web mvc配置

 

  • Controller中编写接收数据接口  
  1. @RequestMapping(value = "/param8",method = RequestMethod.POST)
  2. @ResponseBody
  3. public void param8(@RequestBody List<Phone> phoneList){
  4. System.out.println(phoneList);
  5. }

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

  1. @RequestMapping("/param9/{phoneId}")
  2. @ResponseBody
  3. public void param9(@PathVariable("phoneId") Integer phoneId){
  4. System.out.println(phoneId);
  5. }

5.8 自定义类型转换器

SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。

但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

注意:@DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。和@RequestParam注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。

自定义类型转换器的开发步骤:

(1)

  1. //定义转换器类实现Converter接口
  2. public class DateConverter implements Converter<String, Date> {
  3. public Date convert(String dateStr) {
  4. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  5. Date date = null;
  6. try {
  7. date = format.parse(dateStr);
  8. } catch (ParseException e) {
  9. e.printStackTrace();
  10. }
  11. return date;
  12. }
  13. }
  1. //在springmvc配置类中配置自定义类型转换器
  2. @Bean
  3. public WebMvcConfigurer webMvcConfigurer(){
  4. return new WebMvcConfigurer() {
  5. public void addFormatters(FormatterRegistry registry) {
  6. registry.addConverter(new DateConverter());
  7. }
  8. };
  9. }

http://localhost:8888/phone/param10/2022-10-10%202008:08:08

  1. @RequestMapping("/param10/{date}")
  2. @ResponseBody
  3. public void param10(@PathVariable("date") Date date){
  4. System.out.println(date);
  5. }

(2)Spring MVC 接收 LocalDate、LocalTime 和 LocalDateTime Java 8 时间类型参数

  1. org.springframework.web.bind.annotation.RequestParam
  2. org.springframework.format.annotation.DateTimeFormat
  • @RequestParam 比较常见,用于标注 Controller 中方法的参数;

  • @DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。两个注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。

示例如下:

  • 将 2020-01-29 转换成 LocalDate

  1. //http://localhost:8888/phone/param11/2022-02-02
  2. @RequestMapping("/param11/{date}")
  3. @ResponseBody
  4. public void param11(@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @PathVariable("date") LocalDate date){
  5. System.out.println(date);
  6. }
  7. //http://localhost:8888/phone/param12?date=2022-02-02
  8. @RequestMapping("/param12")
  9. @ResponseBody
  10. public void param12(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date){
  11. System.out.println(date);
  12. }
  •  将 12:25:30 转换成 LocalTime
  1. //http://localhost:8888/phone/param13?time=08:08:08
  2. @RequestMapping("/param13")
  3. @ResponseBody
  4. public void param13(@RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime time){
  5. System.out.println(time);
  6. }
  • 将 2020-01-29 12:25:30 转换成 LocalDateTime
  1. //http://localhost:8888/phone/param14?dateTime=2022-08-08 08:08:08
  2. @RequestMapping("/param14")
  3. @ResponseBody
  4. public void param14(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime){
  5. System.out.println(dateTime);
  6. }

5.9 获取请求头

注意:

  1. 使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)

  2. 使用@CookieValue可以获得指定Cookie的值

(1)@RequestHeader

使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)

@RequestHeader注解的属性如下:

  • value:请求头的名称

  • required:是否必须携带此请求头

  1. @RequestMapping("/param15")
  2. @ResponseBody
  3. public void param15(@RequestHeader(value = "user-Agent",required = false) String userAgent){
  4. System.out.println(userAgent);
  5. }

(2)@CookieValue

使用@CookieValue可以获得指定Cookie的值

@CookieValue注解的属性如下:

  • value:指定cookie的名称

  • required:是否必须携带此cookie

  1. @RequestMapping("/param16")
  2. @ResponseBody
  3. public void param16(@CookieValue("JSESSIONID") String jSessionId){
  4. System.out.println(jSessionId);
  5. }

 6.实现文件上传和下载

6.1 文件上传

 

 (1)导入fileupload和io坐标

  1. <dependency>
  2. <groupId>commons-fileupload</groupId>
  3. <artifactId>commons-fileupload</artifactId>
  4. <version>1.2.2</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-io</groupId>
  8. <artifactId>commons-io</artifactId>
  9. <version>2.4</version>
  10. </dependency>

(2)配置文件上传解析器

  1. @Bean
  2. public MultipartResolver multipartResolver(){
  3. CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
  4. commonsMultipartResolver.setMaxUploadSize(5242880);
  5. commonsMultipartResolver.setMaxUploadSizePerFile(5242880);
  6. commonsMultipartResolver.setDefaultEncoding("UTF-8");
  7. return commonsMultipartResolver;
  8. }

(3)编写文件上传代码

  1. @RequestMapping("/upload")
  2. @ResponseBody
  3. public void upload(MultipartFile file) throws IOException {
  4. String originalFilename = file.getOriginalFilename();
  5. System.out.println("文件名称:"+originalFilename);
  6. file.transferTo(new File("D:\\upload\\"+originalFilename));
  7. }

测试结果如下:

 

6.2 文件下载 

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

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

  1. @RequestMapping("/down")
  2. public ResponseEntity<byte[]> testDown(HttpSession session) throws IOException {
  3. //获取ServletContext对象
  4. ServletContext servletContext = session.getServletContext();
  5. //获取服务器中文件的真实路径
  6. String realPath = servletContext.getRealPath("img");
  7. realPath = realPath + File.separator + "测试.xlsx";
  8. //创建输入流
  9. InputStream is = new FileInputStream(realPath);
  10. //创建字节数组,is.available()获取输入流所对应文件的字节数
  11. byte[] bytes = new byte[is.available()];
  12. //将流读到字节数组中
  13. is.read(bytes);
  14. //创建HttpHeaders对象设置响应头信息
  15. MultiValueMap<String, String> headers = new HttpHeaders();
  16. //设置要下载方式以及下载文件的名字
  17. headers.add("Content-Disposition", "attachment;filename=测试.xlsx");
  18. //设置响应状态码
  19. HttpStatus statusCode = HttpStatus.OK;
  20. //创建ResponseEntity对象
  21. ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
  22. //关闭输入流
  23. is.close();
  24. return responseEntity;
  25. }

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)创建拦截器

  1. package com.gcxy.config.intercepter;
  2. import org.springframework.util.ObjectUtils;
  3. import org.springframework.util.StringUtils;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import javax.servlet.http.HttpSession;
  9. public class LoginIntercepter implements HandlerInterceptor {
  10. /**
  11. * 预处理:可以进行编码、安全控制、权限校验等处理
  12. * 在业务处理器处理请求之前被调用
  13. */
  14. @Override
  15. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  16. String authorization = request.getHeader("Authorization");
  17. if (ObjectUtils.isEmpty(authorization)) {
  18. response.setCharacterEncoding("utf-8");
  19. response.setContentType("application/json; charset=utf-8");
  20. response.getWriter().write("权限不足");
  21. return false;
  22. }
  23. HttpSession session = request.getSession();
  24. Object obj = session.getAttribute(authorization);
  25. if(ObjectUtils.isEmpty(obj)){
  26. response.getWriter().write("token已过期");
  27. return false;
  28. }
  29. return true;
  30. }
  31. /**
  32. * 后处理(调用了Service并返回ModelAndView,但未进行页面渲染)
  33. * 在业务处理器处理请求执行完成后,生成视图之前执行
  34. */
  35. @Override
  36. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  37. }
  38. /**
  39. * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
  40. * 返回处理(已经渲染了页面)
  41. */
  42. @Override
  43. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  44. }
  45. }

(2)配置拦截器

  1. @Configuration
  2. @ComponentScan("com.cqgcxy.controller")
  3. @EnableWebMvc
  4. public class SpringMvcConfig implements WebMvcConfigurer{
  5. @Override
  6. public void addInterceptors(InterceptorRegistry registry) {
  7. registry.addInterceptor(new LoginIntercepter()).addPathPatterns(new String[]{"/**"}).excludePathPatterns(new String[]{"/login"});
  8. }
  9. }

(3)测试拦截器

  • 使用Lombok创建实体类
  • 导入Lombok依赖

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <version>1.18.20</version>
  5. </dependency>
  • 创建实体类
  1. package com.gcxy.entity;
  2. import lombok.Data;
  3. @Data
  4. public class Account {
  5. private String username;
  6. private String password;
  7. }
  • 创建控制器
  1. package com.gcxy.controller;
  2. import com.cqgcxy.entity.Account;
  3. import org.springframework.web.bind.annotation.RequestBody;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import javax.servlet.http.HttpSession;
  7. import java.util.UUID;
  8. @RestController
  9. public class AccountController {
  10. @RequestMapping(value = "/login",produces = "text/html;charset=utf-8")
  11. public String login(@RequestBody Account account, HttpSession session) {
  12. //模拟登录验证
  13. if("admin".equals(account.getUsername())&&"123456".equals(account.getPassword())){
  14. UUID uuid = UUID.randomUUID();
  15. session.setAttribute(uuid.toString(),account);
  16. return uuid.toString();
  17. }
  18. return "账号或密码错误";
  19. }
  20. }

测试结果如下:

1.客户端使用Postman向获取参数类型的资源发起请求:

 

2.客户端使用Postman向/login发起请求 :

 

3. 客户端使用Postman请求头中携带上登录时返回的字符串,重新请求获取参数类型中刚刚请求的资源:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/586190
推荐阅读
  

闽ICP备14008679号