SpringMVC快速入门【基础总结】

SpringMVC

简介

什么是Spring MVC?

Spring MVC是Spring Framework即Spring框架的一部分,是基于Java来实现MVC的轻量级Web框架。官方文档

Spring MVC的特点?

  • 轻量级,简单易学。
  • 高效,基于请求响应的MVC框架。
  • 与Spring兼容性好,无缝结合。
  • 约定大于配置。
  • 功能强大,如支持: Restful风格,数据验证,格式化,本地化,主题等…
  • 简介灵活

Spring MVC框架围绕DispatcherServlet【调度Servlet】来设计的!

DispatcherServlet的作用是将请求分发到不同的Controller控制器,并且,从Spring 2.5开始,使用Java5或者以上版本的用户可以采用基于注解的形式在Spring MVC中进行开发,简洁的同时提高了开发的效率!

为什么要学习Spring MVC?

  • SpringMVC简单,便捷,易学,且和Spring的高度集成(Spring的IOC和AOP),且支持Restful风格,异常处理,本地化,数据验证,类型转换,拦截器等…
  • 最重要一点: 使用的人多,使用的公司多

前端控制器/中心控制器(DispatcherServlet)

Spring MVC框架围绕DispatcherServlet设计

Spring MVC框架像其它许多MVC框架一样,以请求为驱动,围绕一个中心Servlet,即DispatcherServlet来分配请求和进行调度。通俗理解如下图示:【请求分发和调度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNbI8QTj-1640795765545)(SpringMVC.assets/image-20211220134614453.png)]

其中DispatcherServlet本质上也是一个Servlet.

只要实现了Servlet接口就是一个Servlet,而它继承自HttpServlet基类,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3K1J2XL-1640795765551)(SpringMVC.assets/image-20211220124722835.png)]

Spring MVC基本执行原理

当发起请求时被前端控制器即DispatcherServlet拦截到请求,其根据请求参数生成代理请求,找到请求对应的实际控制器即JAVA类,控制器处理请求,创建数据模型,访问数据库,将模型即ModelAndView即响应给中心控制器,控制器使用模型和视图来渲染视图结果,将结果返回给前端控制器,前端控制器最后再将结果返回给请求者。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCFSvWI6-1640795765552)(SpringMVC.assets/image-20211220123004269.png)]

SpringMVC的具体执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zb1PsJTx-1640795765552)(SpringMVC.assets/image-20211220232935105.png)]

上图为Spring MVC的一个具体完整执行的流程图,

其中实线箭头的表示Spring MVC框架提供的技术,即已经帮我们实现了的,不需要开发者实现;

其中虚线箭头的表示需要开发者自己去编码实现的部分!!

其执行流程可分为三大部分

第一部分:根据URL寻找Handler,即适配咱们传递的URL请求是干嘛的

1.DispatcherServlet表示前端控制器/中心控制器,其是整个Spring MVC的控制中心,用户发出请求,DispatcherServlet接收请求并拦截请求。

  • 假设客户端请求的URL为: http://localhost:8080/SpringMVC/hello
  • 如上的URL可拆分为三部分
  • http://localhost:8080表示服务器域名
  • SpringMVC表示部署在服务器上的web站点
  • hello表示控制器Controller
  • 即如上的URL表示的意思为: 请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

2.HandlerMapping为处理器映射。其会被DispatcherServlet自动调用。

  • HandlerMapping的作用为: 根据传入的URL查找指定的Handler即处理器.

3.HandlerExecution表示具体的Handler,其主要作用是根据URL查找控制器,如上面的URL被查找的控制器为: hello

4.HandlerExecution将解析后的信息传递给DispatcherServlet

第二部分:寻找Controller,即适配这个请求到底要做什么

5.HandlerAdapter为处理器适配器

  • 其按照特定的规则去查找具体的Controller,然后去执行Handler.

6.Handler让具体的Controller执行.

7.Controller将执行过后的信息返回给HandlerAdapter,如:ModelAndView模型和视图信息

8.HandlerAdapterModelAndView中的视图逻辑名或带有数据的模型传递给DispatcherServlet

第三部分:视图解析器解析视图和渲染数据

9.DispatcherServlet调用ViewResolver即视图解析器来解析HandlerAdapter传递的逻辑视图名

10.ViewResolver视图解析器将解析的逻辑视图名传给DispatcherServlet

11.DispatcherServlet根据ViewResolver视图解析器解析的视图结果,调用具体的视图

12.最终将视图返回呈现给用户.

: ViewResolver视图解析器具体执行了什么?

  1. 获取ModelAndView的数据
  2. 解析ModelAndView的视图名字
  3. 拼接视图所在的完整路径
  4. 将数据渲染到这个视图

虚线1: 开发者设置需要返回的视图的名字。

虚线2: 开发者设置和封装相应的数据到Model中。

虚线3: 开发者自己编写代码,即 Controller层去调用具体的Service业务层的逻辑代码。


Hello Spring MVC

1.在Maven父项目的基础上新建一个子Module,并右键选择Add Framework Support添加web应用支持

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SE2RgdyO-1640795765553)(SpringMVC.assets/image-20211221094225769.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w284tGvy-1640795765554)(SpringMVC.assets/image-20211221094259220.png)]

2.pom.xml中确定导入了Spring MVC的依赖和其它相关依赖

pom.xml

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

3.配置web.xml,注册DispatcherServlet

web.xml

<?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">

    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
     <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件springmvc-servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--classpath 和 classpath* 是有区别的:
             classpath:只会到你的class路径中查找找文件。
             classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。
             注意:用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;
                  classpath是指WEB-INF文件夹下的classes目录
             因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。-->
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!--在Spring MVC中, / 和 /*是有区别的
    / 只会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
    /* 则会匹配所有url:路径型的和后缀型的url(包括/login,.jsp,.js和*.html等)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4.在resources目录下编写Spring MVC绑定的配置文件

springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--添加HandlerMapping处理器映射器,spring中已提供-->
    <!--这个映射器的特定是会根据传入的URL去寻找相应的Handler,即相应的bean-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    <!--添加HandlerAdapter处理器适配器,spring中已提供-->
    <!--这个适配器会去寻找相应的Controller-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    <!--添加ViewResolver视图解析器,这里这个解析器只解析jsp,后续可根据需要更换模板解析器来解析Freemarker或者Thymeleaf-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

5.在WEB-INF目录下建立jsp目录并在其目录下再建立测试用的test.jsp文件,视图都放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端无法直接访问!

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>test jsp</title>
</head>
<body>
<!--提取msg中存储的数据-->
${msg}

</body>
</html>

6.src目录下编写相应的Controller

HelloController.java

package com.carson.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
//只要实现了Controller接口的类,说明这就是一个控制器了
public class HelloController implements Controller {
    //重写其中唯一的方法
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //实例化一个ModelAndView模型和视图的对象
        ModelAndView mv = new ModelAndView();
        //业务代码书写的位置
         //这里示例只给其中添加数据,addObject()的参数是一个键值对
        mv.addObject("msg","Hello Spring MVC!!");
        //视图跳转
        mv.setViewName("test");//对应/WEB-INF/html/test.html
        //返回ModelAndView
        return mv;
    }
}

7.编写完Controller后,要在Spring配置文件 即springmvc-servet.xml中注册bean作为Handler: id对应请求路径,class对应处理请求的类

springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--添加HandlerMapping处理器映射器,spring中已提供-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    <!--添加HandlerAdapter处理器适配器,spring中已提供-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    <!--添加ViewResolver视图解析器,这里这个解析器只解析jsp,后续可根据需要更换模板解析器来解析Freemarker或者Thymeleaf-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
    
    <!--Handler-->
      <!--由于上面使用的BeanNameUrlHandlerMapping,其会根据URL中的/test来匹配bean id,从而对应到下面这个Handler-->
      <!--故这里handler的id需要以/开头以匹配URL-->
    <bean id="/hello" class="com.carson.controller.HelloController" />
</beans>

8.启动tomcat测试【这里项目的启动路径配置为空】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YWeLD4JH-1640795765557)(SpringMVC.assets/image-20211221110242155.png)]

说明:

  • 实现接口Controller来定义控制器是较老的方法,不建议使用!
  • 实现接口Controller定义控制器这样用法的缺点:
    • 一个控制器中只能有一个方法,如果需要有多个方法的话则需要定义多个Controller;
    • 定义控制器的方式比较麻烦!!推荐使用注解开发控制器!

使用注解开发Spring MVC

1.新建一个Module,添加web应用支持,跟之前上面操作一样。

2.pom.xml中确定导入了Spring MVC, servlet, JSTL等依赖,这里已经在父依赖中导入了.

3.配置web.xml

:

  1. web.xml的版本问题,要为4.0最新版!
  2. 注册DispatcherServlet
  3. 关联Spring的配置文件
  4. 启动级别为1
  5. 映射路径为/,不要用/*

web.xml

<?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">

    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
     <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--classpath 和 classpath* 是有区别的:
             classpath:只会到你的class路径中查找找文件。
             classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。
             注意:用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;
                  classpath是指WEB-INF文件夹下的classes目录
             因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。-->
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--在Spring MVC中, / 和 /*是有区别的
    / 只会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
    /* 则会匹配所有url:路径型的和后缀型的url(包括/login,.jsp,.js和*.html等)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4.resources目录下配置Spring MVC绑定的Spring配置文件springmvc-servlet.xml

:

  1. 让Spring IOC的注解生效
  2. 静态资源过滤:HTML,CSS,JS,图片,视频…
  3. MVC的注解驱动
  4. 配置视图解析器

springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       https://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       https://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动
      在spring中一般采用@RequestMapping注解来完成映射关系
      要想使@RequestMapping注解生效
      必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例
      这两个实例分别在类级别和方法级别处理
      通过使用下面这个annotaion-driver配置,可以帮我们自动完成上述两个实例的注入
      即通过如下这个配置其帮助我们省去了之前对处理器映射器和处理器适配器的配置
      -->
    <mvc:annotation-driven />

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

5.src目录下编写相应的Controller作为Java控制类

HelloController.java

package com.carson.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//@Controller注解代表注册Controller到IOC容器中,其方法返回的String参数对应一个视图名
//@Controller注解代表这个类会被Spring接管
@Controller
//在类上使用@RequestMapping注解书写映射路径
@RequestMapping("/HelloController")
public class HelloController {
    //在方法上使用@RequestMapping注解书写映射路径
    //真实的访问地址为:localhost:8080/web项目名/HelloController/hello
    @RequestMapping("/hello")
    //任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
    public String test01(Model model){
        //向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
        model.addAttribute("msg","Hello Spring MVC Annotation!");
        //返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }
}

:

  • @Controller注解是用于声明Spring类的实例是一个控制器,其为了让Spring IOC容器初始化时自动扫描到.其方法返回的字符串若对应一个逻辑视图名,则其会被视图解析器进行进一步解析,此时如果想方法返回的字符串不被视图解析器解析,需要在方法上加一个@ResponseBody注解;而如果使用@RestController注解,其方法返回的就仅仅是一个字符串,其不会被视图解析器进行进一步解析!!
  • @RequestMapping是为了映射请求路径,即用于映射URL到控制器类或一个特定的处理程序方法。其可以加在类上或者方法上 ; 若用于类上,表示类中的所有响应请求的方法都是以该地址为父地址;即URL地址遵循先指定类路径再指定方法路径的原则; 上面例子中因为类和方法上都有映射,所以访问路径是先类后方法:/HelloController/hello
  • 方法中声明的Model类型的参数是为了把Controller中的数据带到视图中.
  • 方法返回的String类型参数对应逻辑视图名称hello,其会被视图解析器加上配置文件中的前后缀从而形成完整视图路径: /WEB-INF/jsp/hello.jsp

6.创建视图层

WEB-INF目录下建立jsp目录并在其目录下再建立测试用的hello.jsp文件,视图都放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端无法直接访问!

hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello jsp</title>
</head>
<body>
    <!--视图可以直接取出Model类型参数中存放的数据 -->
    ${msg}
</body>
</html>

7.启动tomcat测试【这里项目的启动路径配置为SpringMVC_03_annotation

在这里插入图片描述


总结开发步骤:

  1. 新建一个web项目
  2. 导入相关的依赖
  3. 编写web.xml,注册DispatcherServlet
  4. 编写Spring MVC依赖的Spring配置文件
  5. 创建对应的控制类,Controller
  6. 完善前端视图和Controller之间的对应关系,书写相关函数
  7. 测试运行

使用Spring MVC必须配置的三大件:

  1. HandlerMapping处理器映射器
  2. HandlerAdapter处理器适配器
  3. ViewResolver视图解析器

通过使用注解开发,我们只需要手动配置视图解析器即可,而处理器映射器和处理器适配器只需要开启注解驱动即可,从而省去相关xml的配置!!

: 当返回JSON数据出现乱码时,可以在注解驱动中进行如下配置防止乱码!!

<!--JSON乱码问题配置-->
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Restful风格

简介

  • Restful就是一个资源定位及资源操作的风格

  • 其不是标准也不是协议,只是一种风格.

  • 基于这个风格设计的应用可以更简洁,更有层次,更易于实现缓存等机制.

相关概念

  • 资源: 位于互联网中的所有事物都可以被抽象为资源.
  • 资源操作: 使用POST, DELETE, PUT, GET等不同请求方法来对资源进行操作,其分别对应 添加, 删除, 修改, 查询等功能.

传统方式操作资源

传统方式: 主要通过GET/POST请求同时问号传递不同的参数来实现不同的效果!,如下示例:

  • http://127.0.0.1/item/query?id=1【查询数据请求,GET请求,问号传递参数】
  • http://127.0.0.1/item/save 【新增数据请求,POST请求】
  • http://127.0.0.1/item/update 【更新数据请求,POST请求,问号传递参数】
  • http://127.0.0.1/item/delete?id=1 【删除数据请求,GET请求,问号传递参数】

使用Restful操作资源

使用Restful: 可以通过不同的请求方式来实现不同的效果!

如下示例,请求地址一样,但实现的效果可以不同!!

  • http://127.0.0.1/item/1 【查询数据请求,GET请求】
  • http://127.0.0.1/item 【新增数据请求,POST请求】
  • http://127.0.0.1/item 【更新数据请求,PUT请求】
  • http://127.0.0.1/item/1 【删除数据请求,DELETE请求】

学习测试案例

  1. 新建一个控制器类RestfulController
@Controller
public class RestfulController {
}
  1. 在SpringMVC中可以使用@PathVariable注解,让方法形参的值对应绑定到一个URL模板变量上

若方法参数名称和需要绑定的uri中变量名称不一致时:

@Controller
public class RestfulController {
    //映射访问路径, URL模板变量使用格式: {模板变量名}
    @RequestMapping("/test/{a}/{b}")
    public String index(@PathVariable("a") int p1, @PathVariable("b") int p2, Model model){
        int result = p1+p2;
        //Model类型参数向视图中传递数据(前端视图hello.jsp只写了展示msg中的数据)
        model.addAttribute("msg","结果"+result);
        //返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }
}

若方法参数名称和需要绑定的uri中变量名称一致时,可以简写如下:

@Controller
public class RestfulController {
    //映射访问路径, URL模板变量使用格式: {模板变量名}
    @RequestMapping("/test/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable int p2, Model model){
        int result = p1+p2;
        //Model类型参数向视图中传递数据(前端视图hello.jsp只写了展示msg中的数据)
        model.addAttribute("msg","结果"+result);
        //返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }
}
  1. 启动tomcat测试【这里项目发布路径为:SpringMVC_04_Restful

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sDlXoJj2-1640795765559)(SpringMVC.assets/image-20211221230529828.png)]

使用路径变量的好处?

  1. 使映射路径变得更加简洁,更加具有层次。
  2. 获得参数更加方便。
  3. 通过方法形参设置路径变量的类型可以约束前端访问参数的类型,如果参数类型不一致,则访问不到对应的资源,如上所示,限制了传递的两个参数类型都为int类型。

使用注解的method属性指定请求的类型

我们可以约束请求的类型,从而收窄可以请求的范围;

@RequestMapping注解中的method属性对应的是一个枚举数组类型RequestMethod

我们可以通过RequestMethod.xx指定请求的类型,如:GET, POST, PUT, DELETE等.

测试案例如下:

  1. 控制器类中新增一个方法
//注意:@RequestMapping注解当有多个属性时,路径的设置可以通过value属性或者path属性来设置
//映射访问路径(限定请求类型为POST)
@RequestMapping(value="/hello",method = {RequestMethod.POST})
//等价于:@RequestMapping(path="/hello",method = {RequestMethod.POST})
public String index1(Model model){
    model.addAttribute("msg","hello");
    //返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
    return "hello";
}
  1. 启动tomcat地址栏输入映射地址进行测试,由于地址栏默认是GET请求,所以会报错

在这里插入图片描述

  1. 而如果将请求类型限定为GET,以同样的方式访问,则正常了
//映射访问路径(限定请求类型为GET)
@RequestMapping(value="/hello",method = {RequestMethod.GET})
public String index1(Model model){
    model.addAttribute("msg","hello");
    //返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
    return "hello";
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MuBp6UwH-1640795765560)(SpringMVC.assets/image-20211221232401415.png)]

限定请求类型的注解

  • @GetMapping注解 【直接限定请求类型只能为GET请求】
    • @GetMapping等价于@RequestMapping(method=RequestMethod.GET)
  • @PostMapping注解【直接限定请求类型只能为Post请求】
    • @PostMapping等价于@RequestMapping(method=RequestMethod.POST)
  • @PutMapping注解【直接限定请求类型只能为Put请求】
    • @PutMapping等价于@RequestMapping(method=RequestMethod.PUT)
  • @DeleteMapping注解【直接限定请求类型只能为Delete请求】
    • @DeleteMapping等价于@RequestMapping(method=RequestMethod.DELETE)

测试案例如下:

  1. 控制器类中新增一个方法, 以限定为POST请求为例:
//映射访问路径(限定请求类型为POST)
@PostMapping("/hello")
public String index2(Model model){
    model.addAttribute("msg","hello");
    //返回视图位置,默认是请求转发的形式,请求转发视图名才会被视图解析器拼接
    return "hello";
}
  1. 启动tomcat地址栏输入映射地址发送GET请求进行测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sd9QoTtz-1640795765560)(SpringMVC.assets/image-20211221233445117.png)]

  1. 而将请求类型改为GET请求,重新进行测试:
//映射访问路径(限定请求类型为GET)
    @GetMapping("/hello")
    public String index2(Model model){
        model.addAttribute("msg","hello");
        //返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }

在这里插入图片描述

至于其它的限定请求类型的注解用法和上面一样,同样操作即可!!


Spring MVC结果跳转方式

ModelAndView

通过设置ModelAndView对象,根据view的名称viewName和视图解析器的解析跳转到指定的页面。

跳转的页面 = (视图解析器前缀) + viewName + (视图解析器后缀)

spring配置文件中配置的视图解析器

<!--添加ViewResolver视图解析器,这里示例解析jsp,后续可更改解析器解析其它文件-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!--设置视图前缀-->
    <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
    <!--设置视图后缀-->
    <property name="suffix" value=".jsp" />
</bean>

对应的Controller类

//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
public class HelloController implements Controller {
    //重写其中唯一的方法
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //实例化一个ModelAndView模型和视图的对象
        ModelAndView mv = new ModelAndView();
        //业务代码书写的位置
         //这里示例只给其中添加数据,addObject()的参数是一个键值对
        mv.addObject("msg","Hello Spring MVC!!");
        //视图跳转
        mv.setViewName("test");//对应/WEB-INF/html/test.html
        //返回ModelAndView
        return mv;
    }
}

上面说过,这种方式是最老的方法,不建议使用!

Servlet API

由于Spring MVC的核心是DispatcherServlet,而其本质又是Servlet,故可以通过设置

Servlet的相关API,来实现结果跳转,且不需要视图解析器!

  1. 通过设置HttpServletResponse向前端输出内容。
  2. 通过设置HttpServletResponse实现重定向。
  3. 通过设置HttpServletRequest实现请求转发。
@Controller
public class HelloController2 {
    //通过设置`HttpServletResponse`向前端输出内容
    @RequestMapping("/result/t1")
    public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.getWriter().write("Hello,Spring MVC By Servlet API!");
    }
    //通过设置`HttpServletResponse`实现重定向
    @RequestMapping("result/t2")
    public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException{
        response.sendRedirect("/index.jsp");
    }
    //通过设置`HttpServletRequest`实现请求转发
    @RequestMapping("result/t3")
    public void test3(HttpServletRequest request, HttpServletResponse response) throws Exception{
        request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request,response);
    }
}

虽然可以通过Servlet API实现结果跳转,但不建议这么使用!!

Spring MVC

使用Spring MVC实现结果跳转,但是没有视图解析器的情况

测试前,需要将Spring配置文件中的视图解析器部分注释掉:

spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />
    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven />
    
    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        &lt;!&ndash;设置视图前缀&ndash;&gt;
        <property name="prefix" value="/WEB-INF/jsp/" /> &lt;!&ndash;注意以/结尾,代表下一级路径&ndash;&gt;
        &lt;!&ndash;设置视图后缀&ndash;&gt;
        <property name="suffix" value=".jsp" />
    </bean>-->
</beans>

对应的Controller类

@Controller
public class HelloController3 {
    @RequestMapping("result/t1")
    public String test1(){
        //请求转发
        return "forward:/index.jsp";
    }

    @RequestMapping("result/t2")
    public String test2(){
        //重定向
        return "redirect:/index.jsp";
    }
}

上面的没有使用视图解析器的也不推荐使用!

使用Spring MVC实现结果跳转,有视图解析器的情况

spring配置文件中配置的视图解析器

<!--添加ViewResolver视图解析器,这里示例解析jsp,后续可更改解析器解析其它文件-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!--设置视图前缀-->
    <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
    <!--设置视图后缀-->
    <property name="suffix" value=".jsp" />
</bean>

对应的Controller类

@Controller
public class HelloController3 {
    @RequestMapping("result/t1")
    public String test1(){
        //默认返回的字符串视图名,就是请求转发,,请求转发视图名才会被视图解析器拼接
        return "test";// 对应拼接成/WEB-INF/jsp/test.jsp
    }

    @RequestMapping("result/t2")
    public String test2(){
        //重定向,虽然有视图解析器,但是不会被视图解析器拼接
        return "redirect:/index.jsp";
        //return "redirect:hello"; //hello为另一个请求
    }
}

推荐使用Spring MVC有视图解析器的用法!!


接收请求数据

提交的参数名称和处理方法中的形参参数名一致时

提交数据:http://localhost:8080/hello?name=Carson

处理方法:

@Controller
public class HelloController4 {
    @RequestMapping("/hello")
    //前端传入的参数名为name和下面方法中的形参name是一样的
    public String test(String name){
        System.out.println("传入的参数值是:"+name);
        return "hello";
    }
}

后台输出:
在这里插入图片描述

提交的参数名称和处理方法中的形参参数名不一致时

提交数据: http://localhost:8080/hello?username=Carson

处理方法:

@Controller
public class HelloController4 {
    @RequestMapping("/hello")
    //前端传入的参数名为username和下面方法中的形参name是不一样的
    //需要在方法形参中增加@RequestParam("前端传入参数名")进行说明
    public String test(@RequestParam("username") String name){
        System.out.println("传入的参数值是:"+name);
        return "hello";
    }
}

后台输出:
在这里插入图片描述

建议: 接收前端请求参数在方法形参中都添加一个@RequestParam()注解,增强代码可读性!

提交的是一个对象

要求提交的表单域属性和对象的属性名一致,控制器中的方法的形参使用对象即可!

  1. 实体类
public class User {
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 提交数据:http://localhost:8080/user?id=1&name=carson&age=20
  2. 处理方法:
@Controller
public class HelloController4 {
    @RequestMapping("/user")
    //要求提交的表单域属性和对象的属性名一致,否则匹配不到对象的属性
    //控制器中的方法的形参使用对象即可!
    public String test(User user){
        System.out.println(user);
        return "hello";
    }
}

后台输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpfJkLg8-1640795765563)(SpringMVC.assets/image-20211222234948985.png)]


后台传递数据到前端

通过ModelAndView

示例如下:

//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
//只要实现了Controller接口的类,说明这就是一个控制器了
public class HelloController implements Controller {
    //重写其中唯一的方法
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //实例化一个ModelAndView模型和视图的对象
        ModelAndView mv = new ModelAndView();
        //业务代码书写的位置
         //这里示例只给其中添加数据,addObject()的参数是一个键值对
        mv.addObject("msg","Hello Spring MVC!!");
        //视图跳转
        mv.setViewName("test");//对应/WEB-INF/html/test.html
        //返回ModelAndView
        return mv;
    }
}

通过ModelMap

示例如下:

@RequestMapping("/HelloController")
public class HelloController {
    @RequestMapping("/hello")
    //任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
    public String test01(ModelMap modelMap){
        //向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
        modelMap.addAttribute("msg","Hello Spring MVC!");
        //返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }
}

通过Model

示例如下:

@RequestMapping("/HelloController")
public class HelloController {
    @RequestMapping("/hello")
    //任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
    public String test01(Model model){
        //向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
        model.addAttribute("msg","Hello Spring!");
        //返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
        return "hello";
    }
}

简单对比:

  1. Model只有几个方法适合用于存储数据,简化了开发者对于Model对象的理解和操作!Mo
  2. ModelMap继承了LinkedHashMap,除了自身部分存储数据的方法,还继承了LinkHashMap的方法和特性。
  3. ModelAndView可以在存储数据的同时设置返回的逻辑视图,从而进行视图层的跳转。

当然更多的差异考虑的是性能和优化的问题,从而作出不同的选择!!


乱码问题解决

常规解决办法

//Servlet中添加如下请求和响应编码设置
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");

自定义简单的编码过滤器

package com.carson.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {
    //初始化:当web服务器启动时,就自动初始化了,随时等待过滤对象的出现
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter已经初始化");

    }

    /*
    1:过滤器中的所有代码,在过滤特定请求的时候都会执行
    2:必须写chain.foFilter()方法让过滤器继续前进
    */

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		//转换request和response为HttpServlet类型
		HttpServletRequest request = (HttpServletRequest) request;
		HttpServletResponse response = (HttpServletResponse) response;
		//解决请求参数的中文乱码问题
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=UTF-8");
        //让我们的请求继续走,如果不写,程序到这里就会被拦截停止
        //即这句代码表示放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("CharacterEncodingFilter执行后");

    }
    //销毁:当web服务器关闭的时候,会进行销毁
    public void destroy() {
        System.out.println("CharacterEncodingFilter已经销毁");

    }
}

SpringMVC提供的编码过滤器

SpringMVC给我们提供了一个过滤器,可以直接在web.xml中进行相关配置即可使用!

web.xml

<!--配置SpringMVC的乱码过滤器-->
<filter>
    <filter-name>encoding</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>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

注意:

有些极端情况下,这个过滤器对GET请求的支持效果不太好!!

所以要注意开发环境的所有编码设置最好都设置为UTF-8!!【特别是tomcat环境中】

解决get和post请求乱码的通用过滤器

这个通用乱码过滤器是大神开发的其定义的编码过滤器,能够较好的解决get/post请求乱码的问题!

通用乱码过滤器

package com.carson.filter;
 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
 
/**
 * 解决get和post请求 全部乱码的过滤器
 */
public class GenericEncodingFilter implements Filter {
 
    @Override
    public void destroy() {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //处理response的字符编码
        HttpServletResponse myResponse=(HttpServletResponse) response;
        myResponse.setContentType("text/html;charset=UTF-8");
 
        // 转型为与协议相关对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 对request包装增强
        HttpServletRequest myrequest = new MyRequest(httpServletRequest);
        chain.doFilter(myrequest, response);
    }
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
}
 
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
 
    private HttpServletRequest request;
    //是否编码的标记
    private boolean hasEncode;
    //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
    public MyRequest(HttpServletRequest request) {
        super(request);// super必须写
        this.request = request;
    }
 
    // 对需要增强方法 进行覆盖
    @Override
    public Map getParameterMap() {
        // 先获得请求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post请求
            try {
                // 处理post乱码
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 处理get乱码
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }
        return super.getParameterMap();
    }
 
    //取一个值
    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回参数的第一个值
    }
 
    //取所有值
    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }
}

可以将上面的通用编码过滤器添加到自己的项目文件中,作为万能编码过滤器使用!

然后再在web.xml中配置这个过滤器和过滤路径即可。

乱码问题,平时需要多注意,尽可能在可以设置编码的地方都设置为UTF-8的编码格式!!


SpringMVC中使用Ajax

这阶段的ajax基于JQuerySpringMVC的注解实现来演示使用Ajax

环境搭建:

  1. web.xml
<?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">
    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
    <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationConfig.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置SpringMVC的乱码过滤器,注意其对GET请求的支持不是很好-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>
  1. applicationConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven>
        <!--json乱码配置-->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

示例一:

前端index.jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
    <!--引用本地文件的JQuery,注意script标签不能使用自闭合标签,要使用成对标签-->
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
    <script>
      function a() {
        $.post({
          url:"${pageContext.request.contextPath}/t2",
          data:{
            name:$("#username").val()
          },
          success:function (data) {
            alert(data);
          }
        })
      }
    </script>
  </head>

  <body>
    <%--失去焦点时,发起ajax请求--%>
    用户名:<input type="text" id="username" onblur="a()">

  </body>
</html>

controller

@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
    @RequestMapping("/t2")
    public void test1(String name, HttpServletResponse response) throws IOException {
        System.out.println("a1=>param:"+name);
        if("carson".equals(name)){
            response.getWriter().print("true");
        }else{
            response.getWriter().print("false");
        }
    }
}

效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azT4YrbM-1640795765564)(SpringMVC.assets/image-20211227002741285.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6brXEdym-1640795765565)(SpringMVC.assets/image-20211227002757643.png)]

示例二:

前端test2.jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ajax test2</title>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                /*简写形式:$.post(url,携带的param[可省略],success回调函数)*/
               $.post("${pageContext.request.contextPath}/t3",function (data) {
                    //console.log(data);
                   let html = "";
                   for(let i=0;i < data.length;i++){
                       html += "<tr>"+"<td>"+data[i].name+"</td>"+"<td>"+data[i].age+"</td>"+"<td>"+data[i].sex+"</td>"+"</tr>"
                   }
                   $("#content").html(html);
               });
            })
        });
    </script>
</head>
<body>

<input type="button" value="加载数据" id="btn">
<table>
    <thead>
        <tr>
            <td>姓名:</td>
            <td>年龄:</td>
            <tD>性别:</tD>
        </tr>
    </thead>
    <tbody id="content">
        <%--数据:来自后台--%>
    </tbody>
</table>

</body>
</html>

controller

@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
    @RequestMapping("/t3")
    public List<User> test2(){
        ArrayList<User> userList = new ArrayList<User>();
        userList.add(new User("Carson1",1,"男"));
        userList.add(new User("Carson2",2,"男"));
        userList.add(new User("Amy",3,"女"));
        return userList;
    }
}

效果:

在这里插入图片描述

示例三:

前端login.jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login test</title>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
    <script>
        function checkUsername() {
            $.post({
                url:"${pageContext.request.contextPath}/t4",
                data:{
                    username:$("#userName").val()
                },
                success:function (data) {
                    if(data == "ok"){
                        $("#userInfo").css("color","green");
                    }else{
                        $("#userInfo").css("color","red");
                    }
                    $("#userInfo").html(data);
                }
            })
        };
        function checkPwd() {
            $.post({
                url:"${pageContext.request.contextPath}/t4",
                data:{
                    pwd:$("#pwd").val()
                },
                success:function (data) {
                    if(data == "ok"){
                        $("#pwdInfo").css("color","green");
                    }else{
                        $("#pwdInfo").css("color","red");
                    }
                    $("#pwdInfo").html(data);
                }
            })
        };
    </script>
</head>
<body>
<p>用户名:<input type="text" id="userName" οnblur="checkUsername()">
    <span id="userInfo"></span>
</p>
<p>密码:<input type="text" id="pwd" οnblur="checkPwd()">
    <span id="pwdInfo"></span>
</p>

</body>
</html>

controller

@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
    @RequestMapping("/t4")
    public String test3(String username,String pwd){
        System.out.println("username是:"+username);
        System.out.println("pwd是:"+pwd);
        String msg = "";
        if(username!=null){
            //正常这些数据是在数据库中查,这里只是demo
            if("admin".equals(username)){
                msg = "ok";
            }else if("".equals(username)){
                msg = "用户名不能为空!";
            }else{
                msg = "用户名有误!";
            }
        }

        if(pwd!=null){
            //正常这些数据是在数据库中查,这里只是demo
            if("123456".equals(pwd)){
                msg = "ok";
            }else if("".equals(pwd)){
                msg = "密码不能为空!";
            }else{
                msg = "密码有误!";
            }
        }
        return msg;
    }
}

效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTktTWKT-1640795765567)(SpringMVC.assets/image-20211227003327049.png)]


拦截器

什么是拦截器?

SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,开发者可以自己定义一些拦截器来实现特定的功能!!

过滤器和拦截器的区别?

拦截器是AOP面向切面编程思想的具体应用。

  • 过滤器
    • servlet规范的一部分,任何java web工程都可以使用。
    • url-pattern中配置了/*以后,可以对所有要访问的资源进行拦截。
    • 过滤器需要在web.xml中进行配置。
  • 拦截器
    • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用。
    • 拦截器只会拦截访问的控制器方法,如果访问的是静态资源文件则不会进行拦截!
    • 拦截器则在spring 的配置文件中进行配置。

自定义拦截器

想要自定义拦截器,必须实现HandlerInterceptor接口!

  1. 新建一个Maven Model模块,并添加web应用支持!
  2. 配置web.xml

web.xml

<?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">
    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
    <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationConfig.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置SpringMVC的乱码过滤器,注意其对GET请求的支持不是很好-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>
  1. 配置spring配置文件

applicationConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven>
        <!--json乱码配置-->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>
  1. 编写一个自定义的拦截器类,并实现HandlerInterceptor接口

MyInterceptor.java

package com.carson.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

//拦截器是AOP面向切面编程思想的具体体现
public class MyInterceptor implements HandlerInterceptor {
    //这个方法在请求处理的方法执行之前执行
    //其如果返回true则执行下一个拦截器(即放行请求)
    //其如果返回false就不执行下一个拦截器(即不放行请求)
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("------------请求处理前------------");
        return true;//返回true,放行请求
    }
    //这个方法在请求处理方法执行之后执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("----------请求处理后--------------");

    }
    //这个方法在dispatcherServlet处理后执行,主要做清理工作.
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("-------------DispatcherServlet处理后,做清理工作--------------");
    }
}
  1. spring配置文件中增加关于拦截器的配置

applicationConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven>
        <!--json乱码配置-->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>

    <!--关于拦截器的配置,其中可配置多个拦截器-->
    <!--拦截器是SpringMVC是特有的,mvc开头-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--/** 包括路径及其子路径-->
            <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
            <!--/admin/** 拦截的是/admin/下的所有路径,包括子路径-->
            <mvc:mapping path="/**"/> <!--这里配置拦截根路径下的所有请求-->
            <!--通过bean配置的就是拦截器,选择自定义的拦截器-->
            <bean class="com.carson.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>
  1. 编写一个接收请求的Controller
//测试拦截器的Controller
@RestController //使用@RestController注解不对方法中返回的字符串经过视图解析器解析
public class HelloController {
    @RequestMapping("/interceptor")
    public String test1(){
        System.out.println("控制器中的方法执行了");
        return "OK";
    }
}
  1. 启动tomcat测试,测试结果如下:

在这里插入图片描述


通过拦截器验证用户是否登陆

实现思路

1、前端有一个登陆页面,登陆页面有一提交表单的动作。

2、表单提交后需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。

3、拦截器拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面。

测试

  1. 编写前端相关页面

登陆页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login page</title>
</head>
<body>

<h1>登录页面</h1>
<hr>

<form action="${pageContext.request.contextPath}/user/Login">
    用户名:<input type="text" name="username"> <br>
    密码:<input type="password" name="pwd"> <br>
    <input type="submit" value="提交">
</form>

</body>
</html>

登陆成功页面success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login success</title>
</head>
<body>
<h1>登录成功页面</h1>
<hr>

${user}登陆成功,欢迎你!
<a href="${pageContext.request.contextPath}/user/Logout">注销</a>

</body>
</html>

首页index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
  </head>
  <body>
  <h1>首页</h1>
  <hr>
  <%--登录--%>
  <a href="${pageContext.request.contextPath}/user/toLogin">登录</a>
  <a href="${pageContext.request.contextPath}/user/toSuccess">成功页面</a>
  </body>
</html>
  1. 编写一个Controller处理请求

UserController.java

package com.carson.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

//处理表单请求的Controller
@Controller
@RequestMapping("/user")
public class UserController {
    //跳转到登陆页面
    @RequestMapping("/toLogin")
    public String toLogin() throws Exception {
        return "login";//返回登陆页面视图
    }

    //跳转到成功页面
    @RequestMapping("/toSuccess")
    public String toSuccess() throws Exception {
        return "success";//返回登陆成功页面视图
    }

    //登陆提交
    @RequestMapping("/Login")
    public String login(HttpSession session, String username, String pwd) throws Exception {
        // 向session记录用户身份信息
        System.out.println("接收前端的username为:"+username);
        session.setAttribute("user", username);
        return "success";//返回登陆成功页面视图
    }

    //退出登陆
    @RequestMapping("Logout")
    public String logout(HttpSession session) throws Exception {
        // session 过期
        session.invalidate();
        return "login";//返回登陆页面视图
    }
}
  1. 编写用户登录拦截器

LoginInterceptor.java

package com.carson.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        // 如果是登陆页面则放行
        System.out.println("uri: " + request.getRequestURI());
        if (request.getRequestURI().contains("Login")) {
            return true;//返回true,请求放行
        }
        //获取session信息
        HttpSession session = request.getSession();
        // 从session信息中取出数据,如果用户已登陆也放行
        if(session.getAttribute("user") != null) {
            return true;//返回true,请求放行
        }
        // 用户没有登陆则跳转到登陆页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        return false; //返回false,请求不放行
    }

    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
  1. 配置web.xml
<?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">
    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
    <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationConfig.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置SpringMVC的乱码过滤器,注意其对GET请求的支持不是很好-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>
  1. 配置Spring的配置文件,并在其中注册拦截器

applicationConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven>
        <!--json乱码配置-->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>

    <!--关于拦截器的配置,其中可配置多个拦截器-->
    <!--拦截器是SpringMVC是特有的,mvc开头-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--/** 包括路径及其子路径-->
            <!--如:/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
            <!--如:/admin/** 拦截的是/admin/下的所有路径,包括子路径-->
            <mvc:mapping path="/**"/>
            <!--通过bean配置的就是拦截器,选择自定义的拦截器-->
            <!--<bean class="com.carson.interceptor.MyInterceptor"/>-->
            <bean class="com.carson.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>
  1. 启动tomcat测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jxxTwnVc-1640795765569)(SpringMVC.assets/image-20211227233713986.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4txyZFnJ-1640795765569)(SpringMVC.assets/image-20211227234447703.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lGZ9PDmo-1640795765569)(SpringMVC.assets/image-20211227234457818.png)]


SpringMVC实现文件上传和下载

文件上传

文件上传是项目开发中最常见的功能,springMVC可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。

如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver

前端表单要求

为了能上传文件,必须将表单的提交方式method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据流的方式发送给服务器;

enctype的各个属性说明

  • application/x-www-form-urlencoded:表单默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • text/plain: 除了把空格转换为 “+” 号外,其他字符都不做编码处理,适用直接通过表单发送邮件。
  • multipart/form-data: 这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

支持文件上传的表单示例:

<!--为了支持文件上传,需要设置enctype和post请求-->
<form action="" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit">
</form>

如上所示, 一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则需要在服务器端解析原始的HTTP进行响应。

SpringMVC处理文件上传的优势

  • 虽然Servlet中可以完成文件上传,但实现复杂!而Spring MVC则提供了更简单的封装。

  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。

  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件


文件上传测试

  1. 导入相关依赖。

pom.xml

<dependencies>
    <!--引入文件上传的相关依赖, Maven会自动帮我们导入他的依赖包 commons-io包-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    <!--servlet依赖版本需要3.0以上-->
    <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
        </dependency>
</dependencies>
  1. springMVC配置文件中配置bean:multipartResolver

注意: 配置的这个bean的id必须为:multipartResolver , 否则上传文件会报措!

applicationConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.carson.controller" />

    <!--让Spring MVC不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--支持MVC注解驱动-->
    <mvc:annotation-driven />

    <!--配置ViewResolver视图解析器,这里这个解析器只解析jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--设置视图前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" /> <!--注意以/结尾,代表下一级路径-->
        <!--设置视图后缀-->
        <property name="suffix" value=".jsp" />
    </bean>

    <!--文件上传配置,使用spring中已存在的CommonsMultipartResolver类-->
    <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>
</beans>
  1. 配置的web.xml
<?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">
    <!--配置DispatcherServlet:这个是Spring MVC的核心:作为请求分发器和中心控制器-->
    <!--这个DispatcherServlet是使用spring中已经提供好的-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationConfig.xml</param-value>
        </init-param>
        <!--配置启动级别为1:服务器一启动就跟着启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置SpringMVC的乱码过滤器,注意其对GET请求的支持不是很好-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>
  1. 测试的前端页面

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>upload test</title>
</head>
<body>

<!--测试的前端上传页面-->
<!--<form action="/upload2" enctype="multipart/form-data" method="post">-->
<form action="/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit" value="upload">
</form>

</body>
</html>
  1. 编写后台的Controller:【有两种写法

需要用到一个:CommonsMultipartFile类型参数

CommonsMultipartFile类型参数的 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中

5.1 第一种保存上传文件的方式

//文件上传处理的Controller
@Controller
public class UploadController {
    //@RequestParam("file") 将表单name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {

        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();

        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名是: "+uploadFileName);

        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个路径对应下的新的文件夹
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件的保存地址:"+realPath);

        InputStream is = file.getInputStream(); //获取文件输入流对象
        OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //设置文件输出流对象

        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];//暂存的字节数组
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        //回到首页
        return "redirect:/index.jsp";
    }
}
  1. 2第二种保存上传文件的方式,采用file.Transto 来保存上传的文件
/*
 * 采用file.Transto 来保存上传的文件
 */
@RequestMapping("/upload2")
public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
 
    //上传路径保存设置
    String path = request.getServletContext().getRealPath("/upload");
    //如果路径不存在,创建一个路径对应下的新的文件夹
    File realPath = new File(path);
    if (!realPath.exists()){
        realPath.mkdir();
    }
    //上传文件地址
    System.out.println("上传文件保存地址:"+realPath);
 
    //通过CommonsMultipartFile的方法直接写文件
    file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
 
    return "redirect:/index.jsp";
}
  1. 运行效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YfZpIbXJ-1640795765571)(SpringMVC.assets/image-20211230001213923.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vfJ4Tx8O-1640795765572)(SpringMVC.assets/image-20211230001223604.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHp19rWS-1640795765572)(SpringMVC.assets/image-20211230001233706.png)]

文件下载

文件下载实现步骤:

1、设置response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流 (先开后关)


前端页面:

<a href="/download">测试下载的文件,点击下载</a>

测试的待下载的文件:【已手动放入输出的war包的对应目录中】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nloqeWht-1640795765573)(SpringMVC.assets/image-20211230003426544.png)]

后台的控制器Controller:

//文件下载
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
    //要下载的资源地址
    String  path = request.getServletContext().getRealPath("/upload");
    String  fileName = "resource.txt";

    //1、设置response 响应头
    response.reset(); //设置页面不缓存,清空buffer
    response.setCharacterEncoding("UTF-8"); //字符编码
    response.setContentType("multipart/form-data"); //二进制传输数据
    //设置响应头
    response.setHeader("Content-Disposition",
                       "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

    File file = new File(path,fileName);
    //2、 读取文件--输入流
    InputStream input=new FileInputStream(file);
    //3、 写出文件--输出流
    OutputStream out = response.getOutputStream();

    byte[] buff =new byte[1024];
    int index=0;
    //4、执行 写出操作
    while((index= input.read(buff))!= -1){
        out.write(buff, 0, index);
        out.flush();
    }
    out.close();
    input.close();
    return null;
}
  1. 运行效果:

在这里插入图片描述


欢迎关注个人公众号,回复“SpringMVC”,获取本文所有的完整测试代码!

在这里插入图片描述


版权声明:本文为weixin_46350177原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。