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

<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 获取请求头

注意:

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

  2. 使用@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请求头中携带上登录时返回的字符串,重新请求获取参数类型中刚刚请求的资源:

 

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值