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来分配请求和进行调度。通俗理解如下图示:【请求分发和调度

SpringMVC快速入门【基础总结】_第1张图片

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

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

SpringMVC快速入门【基础总结】_第2张图片

Spring MVC基本执行原理

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

SpringMVC快速入门【基础总结】_第3张图片

SpringMVC的具体执行流程

SpringMVC快速入门【基础总结】_第4张图片

上图为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)]

SpringMVC快速入门【基础总结】_第5张图片

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

pom.xml

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
dependency>
<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>servlet-apiartifactId>
    <version>2.5version>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.2.0.RELEASEversion>
dependency>

3.配置web.xml,注册DispatcherServlet

web.xml


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

    
     
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            
            <param-value>classpath:springmvc-servlet.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    
    
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>
web-app>

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

springmvc-servlet.xml


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

    
    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
    
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

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


    test jsp



${msg}



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


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

    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/jsp/" /> 
        
        <property name="suffix" value=".jsp" />
    bean>
    
    
      
      
    <bean id="/hello" class="com.carson.controller.HelloController" />
beans>

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

SpringMVC快速入门【基础总结】_第6张图片

说明:

  • 实现接口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


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

    
     
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            
            <param-value>classpath:springmvc-servlet.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>

    
    <servlet-mapping>
        <servlet-name>springmvcservlet-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


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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

    
    <mvc:annotation-driven />

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


    hello jsp


    
    ${msg}


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

SpringMVC快速入门【基础总结】_第7张图片


总结开发步骤:

  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数据出现乱码时,可以在注解驱动中进行如下配置防止乱码!!


<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

SpringMVC快速入门【基础总结】_第8张图片

使用路径变量的好处?

  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请求,所以会报错

SpringMVC快速入门【基础总结】_第9张图片

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

SpringMVC快速入门【基础总结】_第10张图片

限定请求类型的注解

  • @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请求进行测试:

SpringMVC快速入门【基础总结】_第11张图片

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

SpringMVC快速入门【基础总结】_第12张图片

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


Spring MVC结果跳转方式

ModelAndView

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

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

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


<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配置文件


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

    
    
    <context:component-scan base-package="com.carson.controller" />
    
    <mvc:default-servlet-handler />

    
    <mvc:annotation-driven />
    
    
    
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配置文件中配置的视图解析器


<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


<filter>
    <filter-name>encodingfilter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
    <init-param>
        <param-name>encodingparam-name>
        <param-value>utf-8param-value>
    init-param>
filter>

<filter-mapping>
    <filter-name>encodingfilter-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

<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">
    
    
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:applicationConfig.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/url-pattern>
    filter-mapping>
web-app>
  1. applicationConfig.xml

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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

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

    
    <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>
    
    <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");
        }
    }
}

效果:

SpringMVC快速入门【基础总结】_第13张图片

SpringMVC快速入门【基础总结】_第14张图片

示例二:

前端test2.jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ajax test2title>
    <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 += ""+""+data[i].name+""+""+data[i].age+""+""+data[i].sex+""+""
                   }
                   $("#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;
    }
}

效果:

SpringMVC快速入门【基础总结】_第15张图片

示例三:

前端login.jsp文件

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


    login test
    
    


用户名:

密码:

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;
    }
}

效果:

SpringMVC快速入门【基础总结】_第16张图片


拦截器

什么是拦截器?

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

过滤器和拦截器的区别?

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

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

自定义拦截器

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

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

web.xml


<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">
    
    
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:applicationConfig.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/url-pattern>
    filter-mapping>
web-app>
  1. 配置spring配置文件

applicationConfig.xml


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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

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

    
    <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


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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

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

    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/jsp/" /> 
        
        <property name="suffix" value=".jsp" />
    bean>

    
    
    <mvc:interceptors>
        <mvc:interceptor>
            
            
            
            <mvc:mapping path="/**"/> 
            
            <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测试,测试结果如下:

SpringMVC快速入门【基础总结】_第17张图片


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

实现思路

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

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

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

测试

  1. 编写前端相关页面

登陆页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login pagetitle>
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 successtitle>
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

<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">
    
    
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:applicationConfig.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/url-pattern>
    filter-mapping>
web-app>
  1. 配置Spring的配置文件,并在其中注册拦截器

applicationConfig.xml


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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

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

    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/jsp/" /> 
        
        <property name="suffix" value=".jsp" />
    bean>

    
    
    <mvc:interceptors>
        <mvc:interceptor>
            
            
            
            <mvc:mapping path="/**"/>
            
            
            <bean class="com.carson.interceptor.LoginInterceptor"/>
        mvc:interceptor>
    mvc:interceptors>
beans>
  1. 启动tomcat测试

SpringMVC快速入门【基础总结】_第18张图片

SpringMVC快速入门【基础总结】_第19张图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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: 这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

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


<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>
    
    <dependency>
        <groupId>commons-fileuploadgroupId>
        <artifactId>commons-fileuploadartifactId>
        <version>1.4version>
    dependency>
    
    <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.0.1version>
        dependency>
dependencies>
  1. springMVC配置文件中配置bean:multipartResolver

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

applicationConfig.xml


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

    
    
    <context:component-scan base-package="com.carson.controller" />

    
    <mvc:default-servlet-handler />

    
    <mvc:annotation-driven />

    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/jsp/" /> 
        
        <property name="suffix" value=".jsp" />
    bean>

    
    <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="defaultEncoding" value="utf-8"/>
        
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    bean>
beans>
  1. 配置的web.xml

<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">
    
    
    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:applicationConfig.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/url-pattern>
    filter-mapping>
web-app>
  1. 测试的前端页面

index.jsp

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


    upload test





  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. 运行效果

SpringMVC快速入门【基础总结】_第20张图片

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

SpringMVC快速入门【基础总结】_第21张图片

文件下载

文件下载实现步骤:

1、设置response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流 (先开后关)


前端页面:

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

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

SpringMVC快速入门【基础总结】_第22张图片

后台的控制器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”,获取本文所有的完整测试代码!

你可能感兴趣的:(javaweb,restful,spring,java,springmvc,经验分享)