SpringMVC从入门到精通一文帮你学会

SpringMVC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GoRKIAzC-1678070111788)(笔记图片/image-20230213145340880.png)]

1. SpringMVC概述

概述:

Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架,最热门的开发技能。一个好的框架要减轻开发者处理复杂问题的负担,内部有良好的扩展,并且有一个支持它的强大用户群体,恰恰 Spring MVC 都做到了。、

1.MVC设计模式概述

MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。

比如:

在 Web 项目的开发中,能够及时、正确地响应用户的请求是非常重要的。用户在网页上单击一个 URL 路径,这对 Web 服务器来说,相当于用户发送了一个请求。而获取请求后如何解析用户的输入,并执行相关处理逻辑,最终跳转至正确的页面显示反馈结果,这些工作往往是控制层(Controller)来完成的。在请求的过程中,用户的信息被封装在 User 实体类中,该实体类在 Web 项目中属于数据模型层(Model)。在请求显示阶段,跳转的结果网页就属于视图层(View)。

视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能

控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。

数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。

2.SpringMVC的优缺点

任何一件事都有利有弊,下面来了解一下 MVC 的优缺点。

优点

1.多视图共享一个模型,大大提高了代码的可重用性

2.MVC 三个模块相互独立,松耦合架构

3.控制器提高了应用程序的灵活性和可配置性

4.有利于软件工程化管理

  总之,我们通过 MVC 设计模式最终可以打造出一个松耦合+高可重用性+高可适用性的完美架构。

缺点

1.原理复杂

2.增加了系统结构和实现的复杂性

3.视图对模型数据的低效率访问

​ MVC 并不适合小型甚至中型规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序,通常得不偿失,所以对于 MVC 设计模式的使用要根据具体的应用场景来决定。

3.Spring的MVC模式概述

  1. 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。
  2. 是结构最清晰的 Servlet+JSP+JavaBean 的实现,是一个典型的教科书式的 MVC 构架,不像 Struts 等其它框架都是变种或者不是完全基于 MVC 系统的框架。
  3. 角色划分清晰,分工明细,并且和 Spring 框架无缝结合。Spring MVC 是当今业界最主流的 Web 开发框架,以及最热门的开发技能。
  4. Controller 替换 Servlet 来担负控制器的职责,用于接收请求,调用相应的 Model 进行处理,处理器完成业务处理后返回处理结果。Controller 调用相应的 View 并对处理结果进行视图渲染,最终客户端得到响应信息。
  5. 框架采用松耦合可插拔的组件结构,具有高度可配置性,比起其它 MVC 框架更具有扩展性和灵活性
  6. Spring MVC 的注解驱动和对 REST 风格的支持,与 Spring 框架是无缝集成,性能方面具有先天的优越性,对于开发者来说,开发效率也高于其它的 Web 框架。

2. 构建SpringMVC

1.构建好一个完整的Spring框架

2. 引入相关SpringMVC的依赖

<!-- Spring-web开发和集成MVC开发的相关依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>

<!-- spring集成junit的单元测试包 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>

<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>

<!--J2EE-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

<!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

<!-- 文件上传和下载 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>



3.在spring项目下创建web配置文件

在这里插入图片描述

4.添加配置tomcat

在这里插入图片描述
在这里插入图片描述

1. 如果没有war expload包添加到tomcat选项要配置打包

在这里插入图片描述

以上步骤完成一定要从新配置tomcat

5. 配置前端控制器(DispatcherServlet )

在开发 Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet

<?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">
    <!-- 项目名称(最好可以和tomcat中配置的上下文名称一致) -->
    <display-name>demoMVC</display-name>
    <!-- 部署 DispatcherServlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <!-- 框架的前端控制器类 -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 表示容器再启动时立即加载servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!-- 处理所有URL -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>


</web-app>

6. 创建SpringMVC配置文件(Spring的配置项目也可以写)

名字命名为:springmvc-servlet.xml,放置在resouce目录下

web.xml配置

 <!-- 部署 DispatcherServlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <!-- 框架的前端控制器类 -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置一个初始化参数 -->
        <init-param>
            <!-- 加载本地的springMVC配置文件!!!!!!!!! -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>

        <!-- 表示容器再启动时立即加载servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>

1.做一个简单尝试,设置一个controller去请求响应跳转页面

controller类

package com.huawei.SpringDemo2023.classtest.controller;

//import org.springframework.stereotype.Controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 模块名称:登录模块
 * 模块类型:控制器(C)
 * 编码人:高靖博
 * 创建时间:2023/2/13
 * 联系电话:18587388612
 */
public class LoginController implements Controller {
    
    

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    
    
        System.out.println("----接收到请求");

        return new ModelAndView("/WEB-INF/views/index.jsp");//将响应转发到视图
    }

}

springmvc配置文件配置

<!-- name设置的是请求controller的url路径,且不要和其他的bean的name设置重复 -->
        <bean name="/login" class="com.huawei.SpringDemo2023.classtest.controller.LoginController" />

3. 视图解析器

视图解析器(ViewResolver)是 Spring MVC 的重要组成部分,负责将逻辑视图名解析为具体的视图对象。

1. 视图解析器类型

1. InternalResourceViewResolver

springmvc配置文件中配置

<!-- 视图解析器 -->
       <!-- 内部资源视图解析器 -->
       <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <!-- 视图路径前缀 -->
              <property name="prefix" value="/WEB-INF/views/" />
              <!-- 视图路径后缀 -->
              <property name="suffix" value=".jsp" />
       </bean>

如果以后jsp页面中使用的jstl表达式,上面配置的视图解析器就会报错

解决方案:直接引入jstl相关的依赖

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

4. SpringMVC执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gThatj88-1678070111801)(笔记图片/image-20230214110129798.png)]

SpringMVC 的执行流程如下:

  1. 用户点击某个请求路径,发起一个 HTTP request 请求,该请求会被提交到 DispatcherServlet(前端控制器);
  2. 由 DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一个执行链(HandlerExecutionChain)。
  3. DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
  4. HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller);
  5. Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息);
  6. HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
  7. DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
  8. ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet;
  9. DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
  10. 视图负责将结果显示到浏览器(客户端)。

5.@Controller注解和@RequestMapping注解

1. @Controller注解

作用: 将一个controller类实现通过扫描的方式将实例添加到IOC容器中

使用该注解需要开启Spring自动包扫描

<!-- 自动包扫描实例注入 -->
       <context:component-scan base-package="com.huawei.mvcdemo2023" />

Controller类案例

// 不需要实现Controller接口
@Controller
public class LoginController {
    
    

}

2.@RequestMapping注解

@RequestMapping 就负责将请求映射到对应的控制器方法上。

在基于注解的控制器类中可以为每个请求编写对应的处理方法。

使用 @RequestMapping 注解将请求与处理方法一 一对应即可。

@RequestMapping 注解可用于方法上。用于类上,表示类中的所有响应请求的方法都以该地址作为父路径。

简单案例:

@Controller
public class LoginController {
    
    
    // 可以通过http://localhost:8080/demo/doLogin访问 且请求类型要求为GET请求
    @RequestMapping(value = "/doLogin",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }
}

1. @RequestMapping注解参数

1.value参数

value 属性是 @RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名,如果有其它属性,则必须写上 value 属性名称。

显式声明:

	// http://localhost:8080/demo/doLogin能访问
    @RequestMapping(value = "/doLogin")
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

value属性可以隐式声明

// http://localhost:8080/demo/doLogin能访问
@RequestMapping("/doLogin")
public void aa(){
    
    
    System.out.println("doLogin被调用!!!!!");
}

通配符的使用

// http://localhost:8080/demo/doLogin/aa/bb/cc 能访问
    @RequestMapping(value = "/doLogin/*")
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

2.path 参数

其功能和value完全一致,唯一不同:path不可隐式声明,隐式声明的值将赋值给value属性

// http://localhost:8080/demo/doLogin/aa/bb/cc 能访问
    @RequestMapping(path = "/doLogin/*")
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

3.name参数

功能:描述请求功能

多参数写法,逗号分隔

@RequestMapping(path = "/doLogin/*",name="测试使用")
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

4.method参数

作用:规定允许访问的请求类型,不合格则抛出405错误

类型写法:

GET请求:RequestMethod.GET

POST请求:RequestMethod.POST

PUT请求;RequestMethod.PUT

DELETE请求:RequestMethod.DELETE

一般一定要规定请求类型,不然则代表所有请求类型都能访问

// http://localhost:8080/demo/doLogin,且为GET请求能访问
    @RequestMapping(value = "/doLogin",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

5.params参数

作用:规定请求中必须要包含的参数,甚至可以规定参数必须要携带的值是什么

用法1:规定必须携带某个指定参数

// http://localhost:8080/demo/doLogin?type=1,必须要有type参数,且为GET请求能访问
    @RequestMapping(value = "/doLogin",params = "type",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

用法2:规定必须要携带某个参数且值是多少

// http://localhost:8080/demo/doLogin?type=1,必须要有type且值为1,且为GET请求能访问
    @RequestMapping(value = "/doLogin",params = "type=1",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

6.header参数

控制请求头必须携带的参数和值

@RequestMapping(value = "/doLogin",headers = "Referer=http://www.xxx.com",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

7.consumers参数

控制请求content-Type的值

// 描述请求的数据必须为json,注意是!!!请求!!!!
@RequestMapping(value = "/doLogin",consumes = "application/json",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

3.使用注解化配置实现逻辑视图的跳转

当编写controller方法没有返回任何逻辑视图名称时,会以url映射路径理解为逻辑视图名,如:

@RequestMapping(value = "/doLogin",method = RequestMethod.GET)
    public void aa(){
    
    
        System.out.println("doLogin被调用!!!!!");
    }

// 则会跳转到 /WEB-INF/views/doLogin.jsp

规范写法:

/**
     * 返回值为String的方法,是用于跳转逻辑视图的
     * @return String返回值:就是返回逻辑视图名称即可
     */
    @RequestMapping(value = "/testclass/toLogin")
    public String toLogin(){
    
    
        System.out.println("--- 跳转到登录页面");
        return "login";//返回逻辑视图名
    }

功能模块父路径写法

@Controller
@RequestMapping("/testclass")
public class LoginController {
    
    

    /**
     * 返回值为String的方法,是用于跳转逻辑视图的
     * @return String返回值:就是返回逻辑视图名称即可
     访问该方法:/testclass/toLogin
     */
    @RequestMapping(value = "/toLogin")
    public String toLogin(){
    
    
        System.out.println("--- 跳转到登录页面");
        return "login";//返回逻辑视图名
    }

    @RequestMapping(value = "/toRegister")
    public String toRegister(){
    
    
        System.out.println("--- 跳转到登录页面");
        return "login";//返回逻辑视图名
    }

}

4.Controller方法参数

1.方法中的形参是用于接收请求传递的参数

/**
     * 返回值为String的方法,是用于跳转逻辑视图的
     * @return String返回值:就是返回逻辑视图名称即可
     */
    @RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(String userName,String pwd){
    
    
        System.out.println("--- 跳转到登录页面");
        System.out.println("用户名:"+userName);
        System.out.println("密码:"+pwd);
        return "login";//返回逻辑视图名
    }

测试请求:

http://localhost:8080/mvc/testclass/toLogin?userName=admin&pwd=123

2.可以注入ServletAPI中的对象

请求、响应、session

@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(HttpSession session, HttpServletRequest request){
    
    
        System.out.println("--- 跳转到登录页面");
        System.out.println(session.getId());
        System.out.println(request.getParameter("userName"));
        return "login";//返回逻辑视图名
    }

3. 方法形参为Model(数据模型)

作用:会将model的数据与视图一起返回到view层进行渲染

controller:

// Model:相当于一个数据包
    @RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(Model model){
    
    
        System.out.println("--- 跳转到登录页面");
        // 在jsp页面中可以使用${键的名称}取出值
        model.addAttribute("name","高靖博");
        return "login";//返回逻辑视图名
    }

jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    登录页面
    ${name}
</body>
</html>

5.Controller 方法的返回值

  1. ModelAndView(返回数据和视图)
  2. Model(在参数中定义Model)
  3. 包含模型属性的 Map
  4. 代表逻辑视图名的 String(跳转页面)
  5. void
  6. 其它任意Java类型

1. ModelAndView

@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public ModelAndView toLogin(){
    
    
        System.out.println("--- 跳转到登录页面");

        ModelAndView mv = new ModelAndView();
        mv.setViewName("login");//设置逻辑视图名称
        mv.addObject("name","高启强");//给model数据包添加数据返回到视图层渲染

        return mv;
    }

2.Model返回数据(在参数中定义Model)

// Model:相当于一个数据包
    @RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(Model model){
    
    
        System.out.println("--- 跳转到登录页面");
        // 在jsp页面中可以使用${键的名称}取出值
        model.addAttribute("name","高靖博");
        return "login";//返回逻辑视图名
    }

4. 返回值为String代表页面跳转

@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(String userName,String pwd){
    
    
        System.out.println("--- 跳转到登录页面");
        
        return "login";//返回逻辑视图名
    }

5.void无返回值

跳转的视图就是以url映射路径同名的视图

@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public void toLogin(String userName,String pwd){
    
    
        System.out.println("--- 跳转到登录页面");
    }
//跳转到 toLogin.jsp

6.Spring MVC传递参数(Controller 方法接收参数-关联5-4-1内容)

Spring MVC Controller 接收请求参数的方式有很多种,有的适合 get 请求方式,有的适合 post 请求方式,有的两者都适合。主要有以下几种方式: 通过实体 Bean 接收请求参数 通过处理方法的形参接收请求参数 通过 HttpServletRequest 接收请求参数(返祖-servlet) 通过 @PathVariable 接收 URL 中的请求参数 通过 @RequestParam 接收请求参数 通过 @ModelAttribute 接收请求参数(可以不掌握)

1. 使用实体Bean来接收

使用范围:GET、POST请求都可以使用(PUT、DELETE)

@RequestMapping(value = "/test",method = RequestMethod.POST)
    // 形参处使用自定义对象(VO对象)
    public String test(UserDto user){
    
    
        System.out.println("-- 测试方法");
        System.out.println(user);
        return "main";
    }

2.通过列举形参的方式接收请求参数

使用范围:GET、POST请求都可以使用(PUT、DELETE)

@RequestMapping(value = "/test",method = RequestMethod.POST)
    // 形参处使用自定义对象(VO对象)
    public String test(String name,int age){
    
    
        System.out.println("-- 测试方法");
        System.out.println(user);
        return "main";
    }

4. @PathVariable接收URL中的请求参数

如:http://localhost:8080/mvc/test/admin

我需要把路径中的“admin”切割出来映射到参数中

优点:将参数名称保密

使用范围:GET、POST请求都可以使用(PUT、DELETE)

注意:形参只能用String和其他基础数据类型以及包装器类型

@RequestMapping(value = "/test/{name}/{pwd}",method = RequestMethod.POST)
    public String test(@PathVariable String name,@PathVariable String pwd){
    
    
        System.out.println("-- 测试方法");

        System.out.println(name);
        System.out.println(pwd);

        return "main";
    }

5.@RequestParam接收请求参数

@RequestParam使用:

value属性:实现不同名参数关联

required:是否必须,默认为 true,表示请求中必须包含对应的参数名,若不存在将抛出异常

defaultValue:参数默认值

使用范围:GET、POST请求都可以使用(PUT、DELETE)

注意:@RequestParam 形参只能用String和其他基础数据类型以及包装器类型

@RequestMapping(value = "/test",method = RequestMethod.POST)
    public String test(@RequestParam(value = "userName",defaultValue = "妈耶!") String name, String pwd){
    
    
        System.out.println("-- 测试方法");

        System.out.println(name);
        System.out.println(pwd);

        return "main";
    }

7. SpringMVC请求转发、重定向

1.请求转发

1.简单案例:

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(){
    
    
        System.out.println("-- 测试方法");
        
        return "forward:/testclass/test2";// forward:开头加上另一个controller方法的路径
    }

2. 转发过程参数的传递

转发过程中,参数也会一并传递

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(String name){
    
    // 假设接收name=123
        System.out.println("-- 测试方法");
        System.out.println(name); // 123
        return "forward:/testclass/test2";
    }

    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String test2(String name){
    
    
        System.out.println("-- 测试方法2");
        System.out.println(name); //123
        return "main";
    }

3.转发过程中传递ServletAPI对象

转发过程中,相关API对象也会一并传递

注意:转发时,发送转发的方法和接收转发的方法,约束的请求类型必须一直,否则会405

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(HttpServletRequest request){
    
    
        System.out.println("-- 测试方法");
        request.setAttribute("name","123");
        return "forward:/testclass/test2";
    }

    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String test2(HttpServletRequest request){
    
    
        System.out.println("-- 测试方法2");
        request.getAttribute("name");
        return "main";
    }

2.请求重定向

请求、响应对象无法共享,因为是客户端会从新请求

注意:请求重定向发送的是一个get请求,所以无法重定向到post请求

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(String name){
    
    
        System.out.println("-- 测试方法");

        return "redirect:/testclass/test2";
    }

3.转发或访问非视图解析器资源(静态资源)

配置加载静态资源

xmlns:mvc="http://www.springframework.org/schema/mvc"

<!-- 静态资源目录 -->
       <mvc:annotation-driven />
       <mvc:resources location="/static/" mapping="/static/**" /><!-- 可以写多个 -->

测试转发到静态页面

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(){
    
    
        System.out.println("-- 测试方法");
        return "forward:/static/404.html";// 转发、重定向静态资源时只能是GET请求
    }

8.SpringMVC-@Service注解与@Autowired 、@Resource注解

使用@Service注解的意义在于要实现项目的三层架构模型:C(控制器Controller)、S(Service业务层)、D(Dao 持久层)

目的是实现三层解耦,且使用面向接口编程的方法

controller层案例

@Controller
@RequestMapping("/testclass")
public class LoginController {
    
    

    // 推荐使用resource注解根据实现类的name注入
    // 实现控制层与业务层的解耦
    @Resource(name = "testClassServiceImpl")
    private TestClassService testClassService;
    
    @RequestMapping(value="/doLogin",method = RequestMethod.POST)
    public String doLogin(UserDto dto,HttpSession session){
    
    
        System.out.println("--- 进行登录验证");

        // 控制器层直接面向接口调用即可
        MyResult result = testClassService.doLogin(dto);

        //忽略登录验证
        session.setMaxInactiveInterval(60 * 30);
        session.setAttribute("userName",dto.getUserName());
        
        return "main";
    }
    
}

Service接口

// 业务接口层定义业务实现的方法,建议统一返回相同结果类型,如我们课程中定义的MyResult类型
public interface TestClassService {
    
    

    MyResult doLogin(UserDto dto);

}

service 实现

// 描述-这是一个业务实现层
    // <bean id="testClassServiceImpl" class="xxxx.TestClassServiceImpl">
// service注解可添加参数(""),参数字符串描述的就是实例bean的name,提供给注入时连线使用
@Service
public class TestClassServiceImpl implements TestClassService {
    
    

    public MyResult doLogin(UserDto dto) {
    
    

        String sys_name = "admin";
        String sys_pwd = "123456";

        MyResult result = new MyResult();

        if(sys_name.equals(dto.getUserName())&&sys_pwd.equals(dto.getPwd())){
    
    
            result.setMsg("登录成功!");
        }else{
    
    
            result.setCode("9999");
            result.setMsg("用户名或密码错误!");
        }

        return result;
    }
}

9. 数据类型转换器Converter

作用:在controller方法接收参数时,可以将非相同类型的数据实现转换

类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换。注意:在使用内置类型转换器时,请求参数输入值与接收参数类型要兼容,否则会报 400 错误。

在SpringMVC内部已经内置了很多转换器,比如实现了String转换为int的方法,这些转换器我们称为“标量转换器”

在这里插入图片描述
在这里插入图片描述

1.自定义类型转换器

实现过程:

  1. 创建实体类。
  2. 创建控制器类。
  3. 创建自定义类型转换器类。
  4. 注册类型转换器。
  5. 创建相关视图。

实现案例:例如需要用户在页面表单中输入信息来创建商品信息。当输入“gaojingbo,27,2.3”时表示在程序中自动创建一个 new User,并将“gaojingbo”值自动赋给 name 属性,将“27”值自动赋给 age 属性,将“2.3”值自动赋给 height 属性。

实体类:

@Data
public class PersonDto implements Serializable {
    
    

   private String name;
   private int age;
   private double height;

}

controller方法

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(@RequestParam("dto") PersonDto dto){
    
    
        System.out.println("-- 测试方法");
        System.out.println(dto);
        return "main";
    }

转换器

/**
 * 模块名称:字符串转换为Person对象的类型转换器
 * 模块类型:类型转换器
 * 编码人:高靖博
 * 创建时间:2023/2/15
 * 联系电话:18587388612
 */
public class StringToPersonConverter implements Converter<String, PersonDto> {
    
    

    public PersonDto convert(String source) {
    
    //gaojingbo,18,2.8

        //数据格式校验
        // 1. 长度校验
        String[] arr = source.split(",");
        if(arr.length!=3){
    
    
            System.err.println("转换的数据格式不正确,请以:名称,年龄,身高  来传递.");
        }
        // 2. 验证数据类型是否正确
        String age = arr[1];
        boolean numeric = StringUtils.isNumeric(age);
        if(!numeric){
    
    
            System.err.println("数据类型不正确,年龄不能是字符串文字");
        }

        String height = arr[2];
        try{
    
    
            Double dd = new Double(height);
        }catch (Exception e){
    
    
            System.err.println("数据类型不正确,身高不能是字符串文字");
        }

        PersonDto dto = new PersonDto();

        dto.setName(arr[0]);
        dto.setAge(Integer.valueOf(arr[1]));
        dto.setHeight(Double.valueOf(arr[2]));

        return dto;
    }
}

springmvc配置文件注册转换器

<!-- 注册类型转换器 -->
       <mvc:annotation-driven conversion-service="conversionService" />
       <!-- 注册转换器工厂类 -->
       <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
              <property name="converters">
                     <list>
                            <bean class="com.huawei.mvcdemo2023.testClass.converters.StringToPersonConverter" />
                     </list>
              </property>
       </bean>

测试接口

http://localhost:8080/mvc/testclass/test?dto=admin,18,7.8

10.数据格式化Formatter

Spring MVC 框架的 Formatter 与 Converter<S, T> 一样,也是一个可以将一种数据类型转换成另一种数据类型的接口。不同的是,Formatter 的源类型必须是 String 类型,而 Converter 的源类型可以是任意数据类型。Formatter 更适合 Web 层,而 Converter 可以在任意层中。所以对于需要转换表单中的用户输入的情况,应该选择 Formatter,而不是 Converter。在 Web 应用中由 HTTP 发送的请求数据到控制器中都是以 String 类型获取,因此在 Web 应用中选择 Formatter 比选择 Converter<S, T> 更加合理。

1.内置的格式化转换器

  1. NumberFormatter:实现 Number 与 String 之间的解析与格式化。
  2. CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
  3. PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
  4. DateFormatter:实现 Date 与 String 之间的解析与格式化。

2.使用步骤

创建格式化类,实现Formatter接口


/**
 * 模块名称:时间格式化工具
 * 模块类型:格式化转换
 * 编码人:高靖博
 * 创建时间:2023/2/15
 * 联系电话:18587388612
 */
public class MyDateFormatter implements Formatter<Date> {
    
    

    /**
     * 将String---》Date
     * @param text 参数字符串
     * @param locale 本机配置参数
     * @return
     * @throws ParseException
     */
    public Date parse(String text, Locale locale){
    
    

        // 时间格式化工具
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        Date parse = null;
        try {
    
    
            parse = sdf.parse(text);
        } catch (ParseException e) {
    
    
            System.err.println("日期格式必须为:yyyy-MM-dd");
        }

        return parse;
    }

    /**
     * Date--->String
     * @param object Date对象
     * @param locale
     * @return 日期字符串
     */
    public String print(Date object, Locale locale) {
    
    

        // 时间格式化工具
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        String format = sdf.format(object);

        return format;
    }
}

注册格式化工具

<!-- 注册格式化工具 -->
       <bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
              <property name="formatters">
                     <set>
                            <bean class="com.huawei.mvcdemo2023.testClass.formatters.MyDateFormatter" />
                     </set>
              </property>
       </bean>
       <mvc:annotation-driven conversion-service="formattingConversionService" />

controller方法测试调用

@RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test(@RequestParam("date") Date date){
    
    
        System.out.println("-- 测试方法");
        System.out.println(date);
        return "main";
    }

11.@ModelAttribute注解

将参数的值封装到model中返回到视图层渲染的功能

1. 将注解用在非@RequestMapping方法上,且无返回值

被@ModelAttribute修饰的方法,会在访问目标controller方法前进行调用。

注意: 多个被@ModelAttribute修饰的方法没有执行顺序可言,建议一个类只要写一个被该注解修饰的方法

@Controller
@RequestMapping("/test")
public class TestController {
    
    

    // 可以拦截获取到目标请求的参数
    @ModelAttribute
    public void doModel(String name, Model model){
    
    
        System.out.println("1. 调用doModel方法");
        model.addAttribute("name",name);
    }

    @RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public String toTest(){
    
    
        System.out.println("2. toTest调用");
        return "testIndex";
    }

}

2. 将注解用在非@RequestMapping方法上,且有回值

@ModelAttribute
    public String doModel2(String name, Model model){
    
    
        System.out.println("1. 调用doModel2方法");
        
        return "123";// 等同于: model.addAttribute("string","123");
    }

3. 将注解用在@RequestMapping方法的参数中

只写@ModelAttribute 注解,没有参数

等于: 1. 在方法中注入了一个Model对象,2. 调用model.addAttribute方法

  	3. 添加的数据:key:被修饰参数类型的首字母小写,value:参数对象
  	3.  然后返回视图
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public String toTest(@ModelAttribute UserDto dto){
    
    
        // model.addAttribute("userDto",dto);
        System.out.println("2. toTest调用");
        System.out.println(dto);
        return "testIndex";
    }

写@ModelAttribute 注解,且添加参数

 @RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public String toTest(@ModelAttribute("cxk") UserDto dto){
    
    
        // model.addAttribute("cxk",dto);
        System.out.println("2. toTest调用");
        System.out.println(dto);
        return "testIndex";
    }

4. 定义在别的类中让controller继承的方法

调用过程等同于在类中定义ModelAttribute方法的效果,只是用继承的方式将代码分开,解耦

ModelAttribute类

public class BaseController1{
    
    

    @ModelAttribute
    public void base1(){
    
    
        System.out.println("1-1 base1");
    }
}

controller类

@Controller
@RequestMapping("/test")
public class TestController extends BaseController1{
    
    


    @RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public String toTest(){
    
    
        System.out.println("2. toTest调用");
        return "testIndex";
    }

}

4.Model对象和ModelAndView对象

1. Model对象

代表数据包。

是在controller方法形参中声明使用

只能添加数据返回到视图层渲染

 @RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public String toTest(Model model){
    
    
        model.addAttribute("cxk",dto);
        return "testIndex";
    }

2. ModelAndView对象

代表数据包和视图对象(视图、数据合二为一)

是在controller方法返回值中使用,需要自己手动创建

既能添加数据返回到视图层渲染,也能指定返回的视图是谁

 @RequestMapping(value = "/toIndex",method= RequestMethod.GET)
    public ModelAndView toTest(){
    
    
        ModelAndView mv = new ModelAndView();
        mv.addAttribute("cxk",dto);//添加数据
        mv.setViewName("testIndex");// 指定返回的逻辑视图
    }

12.SpringMVC-JSON的数据交互

1. Json数据结构

1. 对象结构

{
    
    
    name:"admin",// 字符串或字符类型使用单引号或双引号描述
    age:18,// 数值类型直接写数字
    height:2.7,
    isOk:true,
    arr:[],// 描述集合使用中括号
    arr2:[
        18,"aaa",18.9
    ],
    arr3:[
        {
    
    name:"张三",age:18},
        {
    
    name:"张三2",age:19}
    ]
}

2.数组结构

{
    
    
    11,12,13,"dasdas"
}

2. SpringMVC实现Json数据交互转换

为实现浏览器与控制器类之间的 JSON 数据交互,Spring MVC 提供了 MappingJackson2HttpMessageConverter 实现类默认处理 JSON 格式请求响应。该实现类利用 Jackson 开源包读写 JSON 数据,将 Java 对象转换为 JSON 对象和 XML 文档,同时也可以将 JSON 对象和 XML 文档转换为 Java 对象。

在使用注解开发时需要用到两个重要的 JSON 格式转换注解,分别是 @RequestBody 和 @ResponseBody。

@RequestBody:用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上。

@ResponseBody:用于直接返回 return 对象,该注解应用在方法上。添加了该注解的方法,就不会再实现逻辑视图跳转功能

1. 导入jsckson依赖

<dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.12.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.3</version>
    </dependency>

只要导入成功,@ResponseBody@RequestBody 注解进行json转换和响应json功能就能正常使用了

2. @RequestBody注解

使用该注解验证参数时,一定要保证是post请求

1. 响应一个json对象\处理一个json请求参数

登录案例:

前端代码

// 复杂json参数格式时,要使用json字符串方式提交
        var param = {
    
    
            userName:$("#userName").val(),
            pwd:$("#pwd").val(),
            arr:["张三","李四"]
        };

        $.ajax({
    
    
            url:"<%=basePath%>/testclass/doLogin",
            dataType:"json",//预计后端返回json数据
            type:"POST",
            async:true,
            data:JSON.stringify(param),// 发送的是一个json字符串
            contentType:"application/json;charset=UTF-8",// 设置请求头描述数据内容是一个json
            success:function(data){
    
    
                //data 后端响应的数据报文
                if(data.code=="9999"){
    
    
                    $("#msg").html(data.msg);
                }else{
    
    
                    $("#msg").html('');
                }
            },
            error:function(){
    
    
                alert("请求错误");
            }
        });

controller

@RequestMapping(value="/doLogin",method = RequestMethod.POST)
    @ResponseBody
// @RequestBody 保证前端发送的是json字符串才能映射,否则就会抛出415错误
    public MyResult doLogin(@RequestBody UserDto dto, HttpSession session){
    
    
        System.out.println("--- 进行登录验证");
        MyResult result = new MyResult();

        result = testClassService.doLogin(dto);

        //忽略登录验证
        session.setMaxInactiveInterval(60 * 30);
        session.setAttribute("userName",dto.getUserName());
        
        return result;
    }

完整案例实现登录(验证码、登录验证、登录校验)

实现流程:

1.相关类:验证码controller、登录controller、登录实体dto、登录业务层接口、登录业务层实现

2.登录功能流程:

​ 1.用户填写表单(用户名、密码、验证码)

​ 2.提交表单(提交表单使用ajax方法提交),首先进行表单验证,用户名、密码、验证码必填,保证必填后数据提交后端处理

​ 3.后端校验逻辑:首先验证验证码是否正确、验证码错误返回前端错误信息,正确则进入用户名和密码校验流程(用户名密码验证流程在业务实现层完成,用户名密码使用死代码验证不需要连接数据库)

​ 4.校验结束:正确的话跳转到主页jsp,错误则异步刷新提示错误信息

3.验证用户登录流程:

​ 1.需要在调用controller目标方法前验证session中是否保存了用户登录的信息,如果会话失效则返回登录界面提示错误信息,如果会话存在则继续流程

前端jsp

登录页面(login.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
    String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
    <title>登录页面</title>
    <script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form>
    <label>用户名</label>
    <input id="userName" name="userName" placeholder="请输入用户名.." /><br/>
    <label>密码</label>
    <input id="pwd" name="pwd" placeholder="请输入密码.." /><br/>
    <input id="reqCode" name="reqCode" placeholder="请输入验证码.." />
    <img id="reqCodeImg" src="" title="点击切换验证码" style="cursor:pointer;" οnclick="getReqCode()" /><br/>
    <button type="button" οnclick="doLogin()">登录</button>
    <span id="msg" style="color:red;"></span>
</form>
</body>
<script>

    $(function(){
        //1. 请求验证码
        getReqCode();
    });

    //进行登录
    function doLogin(){

        // 复杂json参数格式时,要使用json字符串方式提交
        var param = {
            userName:$("#userName").val(),
            pwd:$("#pwd").val(),
            reqCode:$("#reqCode").val()
        };

        //表单校验....

        $.ajax({
            url:"<%=basePath%>/testclass/doLogin",
            dataType:"json",//预计后端返回json数据
            type:"POST",
            async:true,
            data:JSON.stringify(param),// 发送的是一个json字符串
            contentType:"application/json;charset=UTF-8",// 设置请求头描述数据内容是一个json
            success:function(data){
                //data 后端响应的数据报文
                if(data.code=="9999"){
                    $("#msg").html(data.msg);
                }else if(data.code=="9998"){
                    getReqCode();//刷新验证码
                    $("#msg").html('验证码过期,已刷新,请从新填写验证码');
                }else{// 登录成功
                    $("#msg").html('');
                    location.href = "<%=basePath%>/testclass/toMain";
                }
            },
            error:function(){
                alert("请求错误");
            }
        });
    }

    //获取验证码
    function getReqCode(){
        $.ajax({
            url:"<%=basePath%>/req/getCode",
            dataType:"json",//预计后端返回json数据
            type:"GET",
            async:true,
            success:function(data){
                //data 后端响应的数据报文
                if(data.code=="9999"){
                    $("#msg").html(data.msg);
                }else{
                    $("#reqCodeImg").attr("src",data.data);
                }
            },
            error:function(){
                alert("请求错误");
            }
        });
    }


</script>
</html>

主页(main.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
   主页-欢迎<%= session.getAttribute("userName")%>

    // 请求其他数据的ajax

</body>
</html>

后端代码

登录controller

package com.huawei.mvcdemo2023.testClass.controller;

import com.huawei.mvcdemo2023.core.dto.MyResult;
import com.huawei.mvcdemo2023.testClass.dto.UserDto;
import com.huawei.mvcdemo2023.testClass.service.TestClassService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

/**
 * 模块名称:登录模块
 * 模块类型:控制器(C)
 * 编码人:高靖博
 * 创建时间:2023/2/14
 * 联系电话:18587388612
 */
// 不需要实现Controller接口
@Controller
@RequestMapping("/testclass")
public class LoginController {
    
    

    @Resource(name = "testClassServiceImpl")
    private TestClassService testClassService;
    
    /**
     * 跳转到登录页面
     * @return
     */
    @RequestMapping(value = "/toLogin",method = RequestMethod.GET)
    public String toLogin(){
    
    
        System.out.println("-- 跳转到登录页面");
        return "login";
    }


    /**
     * 跳转到主页页面
     * @return
     */
    @RequestMapping(value = "/toMain",method = RequestMethod.GET)
    public String toMain(){
    
    
        System.out.println("-- 跳转到主页页面");
        return "main";
    }
    
    @RequestMapping(value="/doLogin",method = RequestMethod.POST)
    @ResponseBody
    public MyResult doLogin(@RequestBody UserDto dto, HttpSession session){
    
    
        System.out.println("--- 进行登录验证");
        MyResult result = new MyResult();

        Object reqCode = session.getAttribute("reqCode");
        if(reqCode==null){
    
    
            result.setMsg("验证码过期请刷新!");
            result.setCode("9998");
            return result;
        }else{
    
    

            String reqCodeStr = (String) reqCode;

            if(!reqCodeStr.equalsIgnoreCase(dto.getReqCode())){
    
    
                result.setMsg("验证码错误!");
                result.setCode("9999");
                return result;
            }
        }


        result = testClassService.doLogin(dto);

        if(result.getCode().equals("0000")){
    
    //登录成功时再设置session
            session.setMaxInactiveInterval(60 * 30);
            session.setAttribute("userName",dto.getUserName());
        }
        
        return result;
    }
    
    /**
     * 跳转到注册页面
     * @return
     */
    @RequestMapping(value = "/toRegister",method = RequestMethod.GET)
    public String toRegister(){
    
    
        System.out.println("-- 跳转到注册页面");
        return "register";
    }
}

13. SpringMVC-集成Restful

springmvc 5.2.3 版本已经实现restful规范了

如果出现put、delete请求没有进入对应的方法时

需要在web.xml文件中配置一个过滤器

在这里插入图片描述

14. SpringMVC-过滤器

过滤器是什么?

依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次

使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

总之过滤器实际上就是对所有请求进行过滤操作,获取请求携带的数据或者修改请求的某些参数。

1. 内置过滤器-字符集过滤器

web.xml中配置

<!-- springMVC自带过滤器- 实现参数、响应编解码一致的过滤器 -->
    <filter>
        <description>字符集过滤器</description>
        <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>
            <!-- forceEncoding:true
                    请求、响应都会进行字符编码集的转换
                       request.setCharacterEncoding(“”);
                       response.setCharacterEncoding(“”);
                  forceEncoding:false (默认)
                      只在请求时转换字符编码集
                  request.setCharacterEncoding(“”);
               -->
            <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>

2. 自定义过滤器

  1. 定义一个类实现Filter接口
  2. 实现过滤器接口的三个方法:始化方法,dofilter方法和销毁方法
  3. 在web.xml中配置过滤器

chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在这个方法中进行的。

过滤器案例

public class MyTestFilter implements Filter {
    
    
    //初始化方法
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
        System.out.println("--- MyTestFilter初始化");
        String what = filterConfig.getInitParameter("what");//可以获取web.xml 中filter标签里配置的初始化参数
        System.out.println(what);
    }

    //执行拦截方法
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        System.out.println("--- 进入过滤器:MyTestFilter");

        //胡作非为(请求添加参数、改变contentType、改变head)
        servletRequest.setAttribute("name","cxk");

        //控制请求是否放行
        filterChain.doFilter(servletRequest,servletResponse);//放行
    }

    //销毁方法
    public void destroy() {
    
    
        System.out.println("--- MyTestFilter被销毁");
    }
}

配置过滤器

<!-- 自定义过滤器 -->
    <filter>
        <description>自定义过滤器</description>
        <filter-name>myFilter1</filter-name>
        <filter-class>com.huawei.mvcdemo2023.testClass.filters.MyTestFilter</filter-class>
        <init-param>
            <param-name>what</param-name>
            <param-value>123456</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>myFilter1</filter-name>
        <url-pattern>/test/*</url-pattern>
    </filter-mapping>

15. SpringMVC-拦截器

什么是拦截器?

是基于Java的jdk动态代实现的,实现HandlerInterceptor接口。不依赖于servlet容器,拦截器针对于contraller方法,并且能获取到所有的类,对类里面所有的方法实现拦截粒度更小,拦截器中可以注入service也可以调用业务逻辑。

拦截器的实现

•通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;

• 通过实现 WebRequestInterceptor 接口继承 WebRequestInterceptor 接口的实现类来定义

方法说明

HandlerInterceptor 接口,并实现了接口中的 3 个方法,说明如下:

preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作

postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。

afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

拦截器代码:

public class MyTestInterceptor implements HandlerInterceptor {
    
    

    // 在调用目标controller方法“前”会被调用
    // 返回值:true代表放行,false代表拦截住不继续往下执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("拦截器:preHandle");
        return true;
    }

    // 在调用目标controller方法“后”,解析视图之"前"
    // 可以让编程者,控制最终视图的走向、以及视图中携带的数据内容是否发生更改
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("拦截器:postHandle");
    }

    // 在调用目标controller方法“后”,以及视图渲染完成,数据返回成功才执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("拦截器:afterCompletion");
    }
}

springmvc配置文件中

mvc:interceptors:该元素用于配置一组拦截器。

:该元素是 mvc:interceptors 的子元素,用于定义全局拦截器,即拦截所有的请求。

mvc:interceptor:该元素用于定义指定路径的拦截器。

mvc:mapping:该元素是 mvc:interceptor 的子元素,用于配置拦截器作用的路径,该路径在其属性 path 中定义。path 的属性值为/**时,表示拦截所有路径,值为/gotoTest时,表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置

<!-- 拦截器的配置 -->
       <mvc:interceptors>
              <!-- 定义一个拦截器 -->
              <mvc:interceptor>
                     <!-- 哪些路径进入拦截器 -->
                     <mvc:mapping path="/**"></mvc:mapping>
                     <!-- 描述自定义拦截器实例 -->
                     <bean class="com.huawei.mvcdemo2023.testClass.interceptors.MyTestInterceptor" />
              </mvc:interceptor>
       </mvc:interceptors>

过滤器和拦截器执行流程

先过滤器、后拦截器。

preHandler是处理映射器回调;postHandler是handlerApter回调

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmT9vKCL-1678070111804)(笔记图片/image-20230216171745106.png)]

16.SpringMVC文件上传

2.两种实现模式

1. 基于CommonsMultipartResolver

1. 导入依赖

<!-- 文件上传和下载 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

2.在springmvc配置文件中配置文件上传解析器

<!-- 文件上传解析器 -->
       <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
              <!-- 单个请求文件上传最大值 -->
              <property name="maxUploadSize" value="50000000000" />
              <!-- 文件上传字符编码集 -->
              <property name="defaultEncoding" value="UTF-8" />
       </bean>

1.表单提交文件上传

前端代码

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
    String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
    <title>测试</title>
    <script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form action="<%=basePath%>/test/fileUpload" method="POST" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" value="确定上传" />
</form>
</body>
</html>

2.ajax提交文件上传

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
    String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
    <title>测试</title>
    <script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form id="fileForm">
    <input type="file" name="file" />
    <input type="button" οnclick="doFileUpload()" value="确定上传" />
</form>
</body>
<script>

    function doFileUpload(){
        // ajax 模拟表单提交
        var formData = new FormData($("#fileForm")[0]);

        $.ajax({
            url:"<%=basePath%>/test/fileUpload",
            dataType:"json",
            type:"POST",
            async:true,
            cache:false,// 去除缓存影响
            processData: false, // 规定通过请求发送的数据是否转换为查询字符串,设置为否
            contentType:false,// 不设置任何的mime类型以二进制方式发送
            data:formData,
            success:function(data){
                console.dir(data);
            },
            error:function(){
                alert("请求错误");
            }
        });

    }

</script>
</html>

3. 后端解析文件写入磁盘,完成上传

写入磁盘的路径可以写在配置文件中

web.xml 配置文件中配置:
<context-param>
        <param-name>uploadPath</param-name>
        <param-value>F:\\sysFile</param-value>
    </context-param>

controller方法

@RequestMapping(value = "/fileUpload",method = RequestMethod.POST)
    @ResponseBody
    public MyResult fileUpload(MultipartFile file, HttpServletRequest request){
    
    
        MyResult res = new MyResult();

        System.out.println("--- 文件上传!");
        //文件要上传的位置(通过配置文件加载)
        ServletContext servletContext = request.getServletContext();
        String uploadPath = servletContext.getInitParameter("uploadPath");

        //上传文件
        try {
    
    
            byte[] fileBytes = file.getBytes();

            //文件名称
            String originalFilename = file.getOriginalFilename();
            //文件后缀
            String fileSub = originalFilename.substring(originalFilename.lastIndexOf("."));

            //加密的文件名称
            String fileID = UUID.randomUUID().toString().replaceAll("-", "");

            //要创建的文件路径
            String newFile = uploadPath + "\\" + fileID + fileSub;

            //写出
            file.transferTo(new File(newFile));

        } catch (IOException e) {
    
    
            e.printStackTrace();
            res.setMsg("文件上传出错!");
            res.setCode("9999");
            return res;
        }

        res.setMsg("文件上传成功!");

        return res;
    }

17.SpringMVC文件下载

1.使用servlet的方法下载文件

@RequestMapping(value = "/filedownload",method = RequestMethod.GET)
    @ResponseBody
    public void filedownload(HttpServletRequest request, HttpServletResponse response){
    
    

        System.out.println("--- 文件下载");

        //要下载的文件。带后缀(固定下载这个文件)
        String needDownLoad = "d10fb3edd4cc43b7be5da10aef985fcc.png";

        //设置响应头
        response.setHeader("Content-Type", "application/x-msdownload");//描述是文件下载
        try {
    
    
            response.setHeader("Content-Disposition", "attachment;filename="+this.toUTF8(needDownLoad));//下载文件的名称
        } catch (UnsupportedEncodingException e) {
    
    
            System.err.println("文件下载错误:文件名称编码错误!");
            e.printStackTrace();
        }


        //获取文件保存的文件夹
        ServletContext servletContext = request.getServletContext();
        String uploadPath = servletContext.getInitParameter("uploadPath");

        try {
    
    
            //读出来
            FileInputStream fileInputStream = new FileInputStream(uploadPath + "\\" + needDownLoad);

            byte [] fileByte = new byte[fileInputStream.available()];

            fileInputStream.read(fileByte);
            fileInputStream.close();

            //写出去(使用响应对象写出)
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(fileByte);
            outputStream.flush();
            outputStream.close();


        } catch (IOException e) {
    
    
            System.err.println("文件下载错误!");
            e.printStackTrace();
        }


    }

    // 将字符串进行utf-8编解码
    private String toUTF8(String tarStr) throws UnsupportedEncodingException {
    
    

        byte[] bytes = tarStr.getBytes("UTF-8");

        String s = new String(bytes, "UTF-8");

        return s;
    }

2.mvc+commonse-io的文件下载方法

@RequestMapping(value = "/mvcDownload",method=RequestMethod.GET)
    public ResponseEntity<byte[]> mvcDownload(HttpServletRequest request){
    
    
        System.out.println("-- mvc文件下载");
        //要下载的文件。带后缀(固定下载这个文件)
        String needDownLoad = "d10fb3edd4cc43b7be5da10aef985fcc.png";

        //获取文件保存的文件夹
        ServletContext servletContext = request.getServletContext();
        String uploadPath = servletContext.getInitParameter("uploadPath");

        //设置响应头
        HttpHeaders httpHeaders = new HttpHeaders();

        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);//描述是文件下载
        httpHeaders.setContentDispositionFormData("attachment", needDownLoad);// 描述下载文件的名称

        ResponseEntity<byte[]> responseEntity = null;

        try {
    
    
            responseEntity = new ResponseEntity<byte[]>(
                    FileUtils.readFileToByteArray(new File(uploadPath + "\\" + needDownLoad)),// 将文件读取成byte数组
                    httpHeaders,//设置响应头
                    HttpStatus.CREATED // 设置Http响应的初始状态
            );
        } catch (IOException e) {
    
    
            System.err.println("mvc文件下载错误!");
            e.printStackTrace();
        }

        return responseEntity;

    }

web项目文件管理设计

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gjb760662328/article/details/129356600