SpringMVC学习

一、SpringMVC概述

SpringMVC是一个基于java实现MVC的轻量级Web框架。从下面给出的官方描述可以看出,该框架的设计围绕着DispatcherServlet这一核心进行,它的作用是将请求分发到不同的处理器以进行相应的处理。本质上DispatcherServlet还是一个Servlet,它的直接继承了FrameworkServlet类,更深层次的继承关系如下图所示。

官方描述:The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale, time zone and theme resolution as well as support for uploading files. The default handler is based on the @Controller and @RequestMapping annotations, offering a wide range of flexible handling methods. With the introduction of Spring 3.0, the @Controller mechanism also allows you to create RESTful Web sites and applications, through the @PathVariable annotation and other features.

在这里插入图片描述
可以看出,DispatcherServlet的本质还是一个Servlet,从某种层面上来看,SpringMVC框架通过增加一个Servlet类型的中心控制器来帮助我们适配不同的请求和处理器(Controller)。并且,通过SpringMVC的注释功能,为处理器的实现提供了灵活的处理方式,极大的减少了开发的代码量,简化了开发的复杂性。

SpringMVC实现方式:由于本质上还是实现了MVC架构,因此SpringMVC也可以分成模型、视图和控制器这几部分,如下
在这里插入图片描述

二、实现原理

下面展示了SpringMVC的详细执行流程,通过这个图我们能够很好的理解SpringMVC的工作方式。

在这里插入图片描述
每个步骤功能如下

  1. 用户发送请求至前端控制器DispatcherServlet

  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器,根据请求的url查找Handler

  3. 将解析到的Handler名交给HandlerExecution进行进一步处理

  4. HandlerExecution根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet

  5. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

  6. 交给处理器进行处理(Controller,也叫后端控制器)

  7. Controller执行完成返回ModelAndView

  8. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

  9. DispatcherServletModelAndView传给ViewReslover视图解析器

  10. ViewReslover解析后返回具体的结果

  11. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)

  12. 视图展示给用户

需要注意的是:第3, 4步中的HandlerExecution并不是一个真实存在的对象。在代码实现上,DispatcherServlet通过doService()方法响应请求,而doService最终通过doDispatch()来进行分发请求以进行进一步处理。
查看源码可知,doDispatch()方法根据request获取到HandlerExecutionChain ,再根据HandlerExecutionChain获取到handler,最后根据handler获取HandlerAdapterHandlerAdapter 判断文件是否被修改过,如果没修改过,直接返回。否则,HandlerExecutionChain执行拦截器的prehandle方法,然后HandlerAdapter 执行实际处理。

接下来我们通过一个实例来具体感受在SpringMVC框架下如何进行开发。

1、开发流程

  • 新建web项目
  • 导入相关jar包
  • 编写web.xml , 注册DispatcherServlet
  • 编写springmvc配置文件

配置文件是为DispatcherServlet的具体实现进行相关配置,包含声明所应用的处理器映射器、处理器适配器、视图解析器的具体实现、是否开启注解、是否处理静态资源等。处理器映射器处理器适配器视图解析器作为DispatcherServlet实现的基本要素,因此必不可少。

  • 创建对应的控制类(controller)
  • 最后实现控制器类,完善前端视图和controller之间的对应
  • 测试运行调试

2、控制器管理方式

非注解实现:该方法采用注册bean的方式实现对控制器的管理。

该方式在实现控制器时需要继承Controller接口实现handleRequest方法,并且需要显式的在与DispatcherServlet关联的Spring配置文件中进行注册,比较麻烦。

注解实现:该方法采用基于注解实现对控制器的管理(推荐)。

该方式只需在Spring配置文件中开启注解支持,就能通过相应的注解直接管理控制器,极大的简化了开发难度。

3、具体实例

项目目录结构在这里插入图片描述

step1:用Maven创建一个Web项目
step2:pom.xml中导入相关maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lee.springmvc</groupId>
    <artifactId>SpringMVCStudy</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
</project>

step3:在web.xml配置文件中配置DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatcherServlet:SpringMVC的核心:请求分发器,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1 (与服务器一同启动)-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--
    /:只匹配所有请求,不匹配jsp页面
    /*:匹配所有请求,包括jsp页面
    -->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

step4:添加DispatcherServlet对应的配置文件spring-mvc.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">

    <!--下面两个bean有默认的设置,可以不显示指定-->
    <!--核心要素一:处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    <!--核心要素二:处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    <!--注册Controller以进行Handler,用于非注释实现-->
    <bean id="/hello2" class="com.lee.controller.HelloController2" />


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

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

    <!--开启mvc注解驱动:用于注释实现-->
    <mvc:annotation-driven />

    <!--核心要素三:视图解析器:解析DispatcherServlet给它的ModelAndView
    1. 获取ModelAndView的数据
    2. 解析ModelAndView的视图名字
    3. 拼接视图名字找到对应的视图 /WEB-INF/jsp/..
    4. 将数据渲染到视图上-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--视图路径前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!--视图路径后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

step5:编写前端jsp文件 (/web/WEB-INF/jsp目录下)

hello.jsp:由注解实现的控制器渲染

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SpringMVC-hello</title>
</head>
<body>
    ${msg}
</body>
</html>

hello2.jsp:由非注解实现的控制器渲染

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SpringMVC-hello2</title>
</head>
<body>
    ${msg}
</body>
</html>

step6:编写控制器处理请求

注解方式 (com.lee.ontroller.HelloController.java)

package com.lee.controller;

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

/*
* 注释方式实现Controller
* */
@Controller
@RequestMapping("/HelloController")
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello(Model model){
        model.addAttribute("msg","hello,springmvc!");
        //web-inf/jsp/hello.jsp
        return  "hello";
    }
}

非注解方式 (com.lee.ontroller.HelloController2.java)

package com.lee.controller;

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

/*
* 非注释方式实现Controller
* */
public class HelloController2 implements Controller {

    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "hello, springmvc!!");
        //设置视图名称(路径web-inf/jsp/hello2.jsp)
        mv.setViewName("hello2");
        return mv;
    }
}

step7:将控制器交给SpringIOC容器进行管理(注解实现/非注解实现)

注解方式不用显式在Spring配置文件中进行配置,非注解方式需要在Spring配置文件中注册bean。
本案例中HelloController2控制类在配置文件中已标识,对应
<bean id="/hello2" class="com.lee.controller.HelloController2" />

step8:开启Tomcat进行测试,结果如下:

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

三、实现细节

1、Controller和RestFul风格

Controller

在SpringMVC中,控制器负责处理用户的请求并返回结果,有通过接口或通过注释两种实现方式。在上节实例中我们已经进行了展示,接下来对应上述实例作简单介绍。

方式一:实现Controller接口,该接口包含一个handleRequest方法处理请求,并在Spring配置文件中注册

package com.lee.controller;

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

/*
* 非注释方式实现Controller
* */
public class HelloController2 implements Controller {
	//处理请求并返回一个模型视图对象
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        //添加属性对象用于渲染前端界面
        mv.addObject("msg", "hello, springmvc!!");
        //设置视图名称(路径web-inf/jsp/hello2.jsp)
        mv.setViewName("hello2");
        return mv;
    }
}

注册代码:<bean id="/hello2" class="com.lee.controller.HelloController2" />

可以看出,这种方式对应每一种处理需求都需要用户手动实现一个控制类,并且注册到Spring配置文件,依旧十分麻烦。

方式二:注解实现,需要在Sping配置文件中开启相应支持,并在控制类中用注解修饰对应的类和方法

配置文件中配置注释扫描支持:<mvc:annotation-driven />

package com.lee.controller;

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

/*
* 注释方式实现Controller
* */
@Controller
@RequestMapping("/HelloController")
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello(Model model){
        model.addAttribute("msg","hello,springmvc!");
        //web-inf/jsp/hello.jsp
        return  "hello";
    }
    public String sayHello(ModelAndView mv){
        mv.addAttribute("msg","hello,springmvc!");
        //web-inf/jsp/hello3.jsp
        return  "hello3";
    }
}

由Spring的相关知识可知(Spring学习文档),Spring可以使用扫描机制来找到程序中所有基于注解的控制器类,因此我们需要熟悉注解类型并在合适的地方应用。

注解类型

  • @Controller:该注解用于声明Spring类的实例是一个控制器。
  • @RequestMapping:该注解用于映射url到控制器类或者一个特定的方法。

@RequestMapping可用于类或方法上,作用于类上时表示该类中的所有响应请求的方法都是以该地址作为父路径。
例如上面的示例来说,可通过http://localhost:8080/项目名/HelloController/hello访问hello.jsp;可通过http://localhost:8080/项目名/hello3来访问hello3.jsp。

Restful风格

概述:Rest(representational state transfer(表象性状态转变)或者表述性状态转移)是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,满足这些约束条件和原则的应用程序或设计就是RESTful。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
特性

  • 资源

互联网所有的事物都可以被抽象为资源 ,可以用一个URI(统一资源定位符)指向它,每种资源对应一个特性的URI。要获取这个资源,访问它的URI就可以,因此URI即为每一个资源的独一无二的识别符。

  • 表现层

把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式。

  • 状态转换

每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转换”(State Transfer)。而这种转换是建立在表现层之上的,所以就是“表现层状态转换”。
具体的说,HTTP协议里,使用POST、DELETE、PUT、GET几个动词表示使用不同方法对资源进行操作,分别对应 添加、 删除、修改、查询。

用一句话概括就是:使用URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作。

RESTful应用程序的API的设计包含两个部分

  • 路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对应的路径也可以设计出来。
  • 动词设计:也就是针对资源的具体操作类型,有HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET

设计原则:

  • URL中只使用名词来指定资源,原则上不使用动词
  • 用HTTP协议里的动词来实现资源的添加,修改,删除等操作
  • Server和Client之间传递某资源的一个表现形式,比如用JSON,XML传输文本,或者用JPG,WebP传输图片等
  • 用 HTTP Status Code传递Server的状态信息

在SpringMVC中的应用

路径变量设置

基于Restful风格的URL-PATTERN设置为了/分隔的形式(如/Order/1),而不是采用原来的/Order?orderId=1形式。在Controller中,可以采用@PathVariable注解来解析URL中的模板变量。

资源操作设置

在Controller中,有两种指定资源操作的方式。

  • @RequestMapping:通过设置method属性的CRUD,可以将同一个URL映射到不同的HandlerMethod方法上。
  • 简化版,@GetMapping、@PostMapping、@PutMapping、@DeleteMapping,这些注解与@RequestMapping注解的method属性设置是一致的。

示例:

/WEB-INF/jsp/restful.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>RestFul-Test</title>
</head>
<body>
${result}
</body>
</html>

RestFulController.java

package com.lee.controller;

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

@Controller
@RequestMapping("/rest")
public class RestFulController {
    @RequestMapping("/add/{x1}/{x2}")
    public String add(@PathVariable int x1, @PathVariable int x2, Model model){
        int result = x1 + x2;
        model.addAttribute("result", result);
        return "restful";
    }

    //只响应GET方式的HTTP请求,下面两个方法实现了同一功能
    @RequestMapping(value = "/action", method = {RequestMethod.GET})
    public String actionTest(Model model){
        model.addAttribute("result", "action: GET");
        return "restful";
    }

    @GetMapping("/action2")
    public String actionTest2(Model model){
        model.addAttribute("result", "action: GET");
        return "restful";
    }
}

RestFul的资源表述

RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,这便衍生出了SpringMVC中的内容协商机制。

(这部分内容博主YourBatman讲解的非常透彻,详见SpringMVC内容协商

SpringMVC的内容协商方式有四种:

  • 基于扩展名(默认)

直接根据url中的文件扩展名决定,例如/test/1.xml返回的是xml内容。

  • 基于HTTP请求头的Accept参数(默认)

根据请求头的Accept参数决定,例如Accept:application/json指定接收内容形式为json。
:该种方式Spring MVC默认支持且默认已开启,但优先级比扩展名低。

  • 基于固定类型(显式)

该方式利用@RequestMapping注解中的produces属性来显式指定内容形式,例如:控制器用
@GetMapping(value = {"/test/{id}", "/test"}, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
注释后访问/test返回的就是json格式的内容。
:produces指定的MediaType类型不能和后缀、请求参数、Accept冲突。

  • 基于请求参数(显式)

这种方式也是根据url中的请求参数来决定,但需要在SpringMVC中显式开启,例如/test?format=xml返回xml格式,/test?format=json返回json格式。
:该方式优先级低于扩展名。

在SpringMVC中,可以使用ContentNegotiatingManager这个内容协商管理器来实现这种方式,该接口有几个实现类,分别对应上述几个内容协商策略,详见博主YourBatman的文章Spring MVC内容协商实现原理及自定义配置

本节相关参考资料:
[1] 什么是RESTful?
[2] Spring MVC内置支持的4种内容协商方式
[3] REST 架构该怎么生动地理解

2、视图跳转方式

视图跳转方式包括重定向和转发。实现方式有下面几种:

ModelAndView跳转到指定页面:通过生成ModelAndView对象,设置视图对象名后返回该对象以实现视图跳转。

@Controller
@RequestMapping("/view")
public class ViewJumpController {
    @RequestMapping("/test1")
    public ModelAndView test1(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "Test!");
        mv.setViewName("viewtest");
        return mv;
    }

ServletAPI实现:通过调用HTTPServletResponse和HTTPServletRequest对象的相应API实现转发和重定向。

@Controller
@RequestMapping("/view")
public class ViewJumpController {

    @RequestMapping("/test2")
    public void test2(Model model, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        model.addAttribute("msg", "test2!");
        req.getRequestDispatcher("/WEB-INF/jsp/viewtest.jsp").forward(req, resp);
    }
}

SpringMVC方式实现:分为包含视图解析器和不包含视图解析器两种,只是在返回的路径上有所区别。

不含视图解析器

@Controller
@RequestMapping("/view")
public class ViewJumpController {

    @RequestMapping("/test3")
    public String test3(Model model){
        model.addAttribute("msg", "test3!");
        //实现转发
        return "/WEB-INF/jsp/viewtest.jsp";//equals to forward:/WEB-INF/jsp/viewtest.jsp
    }

    @RequestMapping("/test4")
    public String test4(){
        //实现重定向
        return "redirect:/WEB-INF/jsp/viewtest.jsp";
    }
}

含视图解析器

@Controller
@RequestMapping("/view")
public class ViewJumpController {

    @RequestMapping(value = "/test3", method = {RequestMethod.GET})
    public String test3(Model model){
        model.addAttribute("msg", "test3!");
        //实现转发
        return "forward:test2";
    }

    @RequestMapping("/test4")
    public String test4(){
        //实现重定向
        return "redirect:./test1.do";
    }
}

3、数据处理操作

处理提交数据

分为下面四种情况:

  • 提交的名称与处理方法一致

不用处理,但建议还是采用第二种情况的注解进行注释,以区分参数是否是用户传入的。

  • 提交的名称与处理方法不一致

在方法列表的变量声明前加上@RequestParam注解,例如前台传入变量名为username,方法定义变量名name,此时可通过下面方式进行声明。
public String hello(@RequestParam("username") String name)

  • 提交的是一个对象

这种情况要求提交的表单域和对象的属性名一致 , 对象使用参数赋值对应变量即可,如果某属性名不一致,那么对象中该属性值为空。例如public String user(User user),直接通过传入的值为User类赋值。

数据渲染到前端界面

请求处理(Controller)方法可出现和返回的参数类型中,最重要就是Model和ModelAndView,两者不同之处在于:

Model只是用来传输数据的,并不会进行业务的寻址。ModelAndView 却是可以进行业务寻址的,即设置对应的要请求的静态文件,这里的静态文件指的是类似jsp的文件。
Model是每一次请求可以自动创建,ModelAndView 需要手动new出来。

  • ModelAndView
@RequestMapping("/test1")
    public ModelAndView test1(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "Test!");
        mv.setViewName("viewtest");
        return mv;
    }
  • Model / ModelMap

Model实际上是一个接口,其实现类继承自ModelMap类
ModelMap继承自LinkedHashMap,因此含有LinkedHashMap的特性。

@GetMapping("/action2")
    public String actionTest2(Model model){
        model.addAttribute("result", "action: GET");
        return "restful";
    }
@GetMapping("/action3")
    public String actionTest3(ModelMap model){
        model.addAttribute("result", "action: GET");
        return "restful";
    }

总的来说,

  • ​Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
  • ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;
  • ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

四、扩展知识

在实际项目中,前后端的交互大多采用Ajax技术,并用json作为前后交互数据格式。

1、JSON数据格式

1) 概述

Json(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据,广泛用于web开发中。

组成

  • 键值对的无序集合——对象(或者叫记录、结构、字典、哈希表、有键列表或关联数组等),用花括号表示
  • 值的有序列表——数组,用方括号表示

例如一个对象数组表示为:

[
	{"key1":"value1", "key2":"value2"},
	{"key3":"value3", "key4":"value4"}
]

常见用法

  • json字符串:指的是符合json格式要求的js字符串。例如:
    var jsonStr = "{StudentID:'100',Name:'tmac',Hometown:'usa'}";
  • json对象:指符合json格式要求的js对象。例如:
    var jsonObj = { StudentID: "100", Name: "tmac", Hometown: "usa" };

JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为HttpRequest),将 JSON 数据转换为 JavaScript 对象,然后在网页中使用该数据。可使用下面两个API实现:

  • JSON字符串到JSON(js)对象,使用JSON.parse()方法
  • 从JSON 对象转换为JSON字符串,使用 JSON.stringify() 方法

示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSON-Test</title>
</head>
<body>
<script type="text/javascript">
    var user = {
        name:"lee",
        age:0
    };
    //json对象转换为json字符串
    var str = JSON.stringify(user);
    console.log(str);
    //json字符串转换为json对象
    var user2 = JSON.parse(str);
    console.log(user2.name, user.age);
</script>

<h5>JSON test!</h5>
</body>
</html>

在这里插入图片描述

2) Controller传递JSON数据

通过在控制器(Controller)中引入json解析工具可以返回JSON数据给前端。比较常用的有Jackson和fastjson,实例如下。

步骤

  • Maven中导入相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lee.springmvc</groupId>
    <artifactId>SpringMVCStudy</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
    	<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
        <!--导入json依赖-->
        <!--jckjson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.3</version>
        </dependency>
        <!--<dependency> 
        	<groupId>com.alibaba</groupId> 
        	<artifactId>fastjson</artifactId> 
        	<version>1.2.60</version> 
        </dependency>-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
</project>
  • 相关配置

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:SpringMVC的核心:请求分发器,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.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>
</web-app>

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

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
        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 base-package="com.lee.controller"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--视图路径前缀-->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!--视图路径后缀-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>
  • 构建实体类
package com.lee.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
    private String sex;
}
  • Controller实现

两种方式(不同解析器)

package com.lee.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lee.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserJsonController {
    @RequestMapping("/json")
    @ResponseBody
    public String json() throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("lee", 0, "man");
        //将对象解析成为json格式
        String str = mapper.writeValueAsString(user);
        //@ResponseBody可将这里的str转换为json格式返回
        return str;
    }
}
package com.lee.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lee.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserJsonController {
    @RequestMapping("/json")
    @ResponseBody
    public String json(){
        User user = new User("lee", 0, "man");
        //将对象解析成为json格式
        String str = JSON.toJSONString(user);
        //将json字符串转换为java对象
        User user1 = JSON.parseObject(str, User.class);
        //java对象转换为json对象
        JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
        //json对象转换为java对象
        User user2 = JSON.toJavaObject(jsonObject, User.class);
        return str;
    }
}

  • 测试结果如下,前端成功获取到json字符串
    在这里插入图片描述

这里需要注意可能传递到前端后出现乱码问题,可通过设置@RequestMapping的produces属性解决,如下:

//produces:指定响应体返回类型和编码 
@RequestMapping(value = "/json",produces = "application/json;charset=utf-8")

实际上,这正是第三节讲到的内容协商机制。

3) 优化

为简化代码,可以优化上述处理乱码和json返回字符串的代码。修改如下:

  • 将@ResponseBody替换成再类上直接使用RestController
package com.lee.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lee.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class UserJsonController {
    @RequestMapping("/json")
    
    public String json() throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("lee", 0, "man");
        //将对象解析成为json格式
        String str = mapper.writeValueAsString(user);
        
        return str;
    }
}
  • 在SpringMVC配置文件中统一消息转换配置
<mvc:annotation-driven>
        <mvc:message-converters>
            <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>

2、Ajax

AJAX (Asynchronous JavaScript and XML)(异步的 JavaScript 和 XML)。不是新的编程语言,而是一种使用现有标准的新方法。传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页,而AJAX可在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容。AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

XMLHTTPRequest

  • XMLHttpRequest 是 AJAX 的基础,所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用ActiveXObject)。
  • XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

利用XMLHttpRequest可以用js实现AJAX,该对象为服务器发送请求和解析服务器响应提供了接口,可以以异步的方式从服务器获取数据。

JQuery.ajax

  • jQuery 是一个 JavaScript 库,是最为流行的js框架。
  • jQuery 提供多个与 AJAX 有关的方法,通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON - 同时您能够把这些外部数据直接载入网页的被选元素中

JQuery能够极大的简化AJAX的编程,这里我们通过几个例子来感受一下AJAX的作用。关于这部分更详细的内容可参考JQuery教程

简单案例

  • 配置准备

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:SpringMVC的核心:请求分发器,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.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>

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

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
        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.lee.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>
  • 前端界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>AJAX-test</title>
    <%--导入上下文中的包--%>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>
    <%--使用在线CDN导入--%>
<%--    <script src="https://code.jquery.com/jquery-3.1.1.min.js"> </script>--%>
    <script>
        function test() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax",
                data:{'name':$("#txtName").val()},
                success:function (data, status) {
                    alert(data);
                    alert(status);
                }
            });
        }
    </script>
</head>
<body>
用户名:<input type="text" id="txtName" onblur="test()"/>
</body>
</html>
  • 处理方法
package com.lee.controller;

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

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
public class AjaxController {
    @RequestMapping("/ajax")
    public void ajax1(String name, HttpServletResponse response) throws IOException {
        if(name.equals("lee")){
            response.getWriter().print("yes!");
        }else {
            response.getWriter().print("no!");
        }
    }
}
  • 测试结果如下,成功发送AJAX请求!

在这里插入图片描述

传递json格式数据

  • 添加实体类User
package com.lee.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
    private String sex;
}
  • 处理方法中添加方法
@RequestMapping("/ajax1")
    public List<User> ajax1(){
        List<User> list = new ArrayList<User>();
        list.add(new User("lee", 1, "man"));
        list.add(new User("liu", 1, "woman"));
        list.add(new User("yan", 1, "woman"));
        return list;
    }
  • 前端界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Data-trans</title>
</head>
<body>
<input type="button" id="btn" value="获取数据"/>
<table width="80%" align="center">
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>
    <tbody id="content">
    </tbody>
</table>

<script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>
<script>
    $(function () {
        $("#btn").click(function () {
            $.post("${pageContext.request.contextPath}/ajax1", function (data) {
                console.log(data);
                var html="";
                for(var 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>
</body>
</html>
  • 结果如下:

在这里插入图片描述

注册提示效果

  • 添加处理方法
@RequestMapping("/ajax2")
    public String ajax2(String name, String pwd){
       String msg = "";
       if(name != null){
           if(name.equals("lee")){
               msg = "OK";
           }else {
               msg = "用户名无效!";
           }
       }
        if(pwd != null){
            if(pwd.equals("123456")){
                msg = "OK";
            }else {
                msg = "密码错误!";
            }
        }
        return msg;
    }
  • 添加前端界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login-ajax</title>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.4.1.js"></script>
    <script>
        function a1() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax2",
                data:{'name':$("#name").val()},
                success:function (data) {
                    if(data.toString()=='OK'){
                        $("#userInfo").css("color","green");
                    }else {
                        $("#userInfo").css("color","red");
                    }
                    $("#userInfo").html(data);
                }
            });
        }
        function a2() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax2",
                data:{'pwd':$("#pwd").val()},
                success:function (data) {
                    if(data.toString()=='OK'){
                        $("#pwdInfo").css("color","green");
                    }else {
                        $("#pwdInfo").css("color","red");
                    }
                    $("#pwdInfo").html(data);
                }
            });
        }
    </script>
</head>
<body>
<p>
    用户名:<input type="text" id="name" onblur="a1()">
    <span id="userInfo"></span>
</p>
<p>
    密码:<input type="text" id="pwd" onblur="a2()">
    <span id="pwdInfo"></span>
</p>
</body>
</html>
  • 结果如下:

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

五、常用功能实现

1、过滤器

在开发中经常出现乱码问题,在以前的开发中通过用户定义过滤器来解决。SpringMVC中提供了一个现成的过滤器,可直接在web.xml中进行配置。在web.xml中添加以下代码:

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

当然,用户也可自定义过滤器实现过滤操作。

2、拦截器

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

拦截器与过滤器的区别如下:

  • 拦截器是应用在SpringMVC框架下的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 对于其他的静态资源,如jsp/html/css/image/js,不会进行拦截。

实现步骤

  • 编写一个拦截器,必须实现HandlerInterceptor接口
package com.lee.interceptor;

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

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

public class MyInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("---------处理前---------");
        return false;
    }

    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("----------处理后-----------");
    }

    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("-------------清理------------");
    }
}
  • 在SpringMVC的配置文件中进行配置,添加以下代码
<mvc:interceptors>
        <mvc:interceptor>
            <!--/**包括当前路径及其子路径,例如/test/**表示拦截/test/..下所有的,而/test/*则表示拦截形如/test/add,而不拦截/test/add/user-->
            <mvc:mapping path="/**" />
            <bean class="com.lee.interceptor.MyInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
  • 测试,前端代码和Controller类如下

InterceptorController

package com.lee.controller;

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

@Controller
public class InterceptorController {
    @RequestMapping("/interceptor")
    public String testInterceptor(Model model){
        model.addAttribute("msg", "我被拦截过了!");
        System.out.println("??");
        return "hello";
    }
}

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/16
  Time: 17:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="${pageContext.request.contextPath}/interceptor">拦截器测试</a>
  </body>
</html>

/WEB-INF/jsp/hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SpringMVC-hello</title>
</head>
<body>
    ${msg}
</body>
</html>

结果如下,成功
在这里插入图片描述

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

常见应用:用户登录判断

功能需求:用户登录是判断用户名密码是否正确,如果正确,向session中写入用户信息,返回登录成功界面;拦截用户请求,判断用户是否登录,若已经登录则放行,否则跳转到登录页面。

实现:

  • 编写前端界面

/WEB-INF/jsp/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="${pageContext.request.contextPath}/user/jumpLogin">登录</a>
  <a href="${pageContext.request.contextPath}/user/jumpSuccess">成功页面</a>
  </body>
</html>

/WEB-INF/jsp/login.jsp

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/18
  Time: 21:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<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>

/WEB-INF/jsp/success.jsp

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/18
  Time: 21:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功页</title>
</head>
<body>
    ${user}
    <a href="${pageContext.request.contextPath}/user/logout">注销</a>
</body>
</html>
  • 编写Controller处理器处理请求

UserController.java

package com.lee.controller;

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

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(@RequestParam("username") String user, String pwd, HttpSession session){
        //向session中记录用户信息
        //这里可添加业务逻辑处理,如判断用户是否注册
        session.setAttribute("user",user);
        return "success";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session){
        //注销session,表明用户信息已经失效
        session.invalidate();
        return "login";
    }

    @RequestMapping("/jumpLogin")
    public String jumpLogin(){
        return "login";
    }

    @RequestMapping("/jumpSuccess")
    public String jumpSuccess(){
        return "success";
    }
}

  • 编写用户拦截器

LoginInterceptor.java

package com.lee.interceptor;

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

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

public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        HttpSession session = httpServletRequest.getSession();
        //登录页面或处于登录状态不拦截
        if(httpServletRequest.getRequestURI().contains("login") || session.getAttribute("user") != null){
            return true;
        }

        //未登录跳转则到登录页面
        httpServletRequest.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(httpServletRequest, httpServletResponse);

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

    }
}
  • 注册拦截器
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="com.lee.interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

结果如下,功能成功实现!

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

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

3、文件上传和下载

文件上传和下载是实际项目中常出现的两个功能,SpringMVC中的MultipartResolver可以很好地支持文件上传,但需要在配置文件中显示进行配置。文件下载则比较简单,与不使用框架的情况类似。

前提条件:

  • 前端:为支持文件上传,需将前端的表单的method设置为POST,并将enctype设置为multipart/form-data,这样浏览器才会把用户选择的文件以二进制数据发送给服务器。

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

  • 后端:在服务器端解析用户请求进行文件上传处理需要开发者显式编码。Servlet3.0规范已经提供相关方法处理文件上传,但该方式需要在Servlet中进行。SpringMVC为文件上传提供了更简单的封装(将一些通用代码封装成方法),由MultipartResolver来支持该功能,该接口有一个实现类CommonsMultipartResolver,利用Apache Commons FileUpload技术实现,因此要支持SpringMVC的文件上传,还需要添加Apache Commons FileUpload的相关组件。

实现步骤:

  • pom.xml中导入依赖包commons-fileupload
<dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
</dependency>
  • 在SpringMVC中配置bean:multipartResolver

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

<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="1048576"/>
   <property name="maxInMemorySize" value="40960"/>
</bean>
  • 前端界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>

<form enctype="multipart/form-data" method="post" action="${pageContext.request.contextPath}/upload2">
    <input type="file" name="file">
    <<input type="submit">
</form>
<p><a href="${pageContext.request.contextPath}/download">下载图片</a></p>
</body>
</html>

  • 后端处理代码

上传包含两种方式

package com.kuang.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import sun.security.util.Length;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;

@Controller
public class FileUploadController {

    //文件上传方式一:采用流
    @RequestMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
        //1.获得文件名
        String filename = file.getOriginalFilename();
        if ("".equals(filename)){
            return "文件不存在";
        }
        //2.上传文件保存路径
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //3.上传文件
        InputStream is = file.getInputStream();
        FileOutputStream os = new FileOutputStream(new File(realPath, filename));

        int len = 0;
        byte[] buffer =  new byte[1024];

        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }

        //4.关闭流
        os.close();
        is.close();
        return "上传完毕";
    }

    //文件上传方式二:采用CommonsMultipartFile的方法,简单
    @RequestMapping(value = "/upload2")
    @ResponseBody
    public String upload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        //上传文件保存路径
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }

        //transferTo:将文件写入到磁盘,参数就是一个文件
        file.transferTo(new File(realPath+"/"+file.getOriginalFilename()));

        return "上传完毕";
    }

    //下载实现
    /*
    //设置响应头
    1.读取本地文件
    2.写出文件
    3.执行操作
    4.关闭流
     */
    @RequestMapping(value = "/download")
    public String download(HttpServletResponse response) throws IOException {

        //要下载的图片路径;
        String path = "E://test/img/";
        String filename = "test.jpg";

        //设置响应头信息;【固定的不用记,保存即可】
        response.reset(); //让页面不缓存
        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);

        //输入流
        FileInputStream input = new FileInputStream(file);
        //输出流
        ServletOutputStream out = response.getOutputStream();

        //执行操作
        int len = 0;
        byte[] buffer = new byte[1024];

        while ((len = input.read(buffer))!=-1){
            out.write(buffer,0,len);
            out.flush();
        }

        out.close();
        input.close();

        return null;

    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值