Spring MVC

1、SpringMVC

ssm:mybatis + Spring + SpringMVC

2、MVC架构

  • MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
  • 是将业务逻辑、数据、显示分享的方法来组织代码
  • MVC主要作用是“降低了视图与业务逻辑间的双向耦合。
  • MVC不是一种设计模式,MVC是一种架构模式,当然不同的MVC存在差异Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao)和服务层(行为Service)也就是模型提供了模型数据查询和模型数据的状态更新等功能,包含数据和业务。View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。Constroller(控制器):接收用户夜静请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示,也就是说控制器做了个调度员的工作。最典型的MVC就是:JSP + Serivel + JavaeBean的模式。
image-20210719090655877

2.1 Model1时代

在web早期的开发中,通常采用的都是Model1。

Model1中,主要分为两层,视图层和模型层。

image-20210719090912752

Model1的优点:架构简单,比较适合小型项目开发。

Model1的缺点:JSP职责不单一,职责过重,不便于维护。

2.2 Model2时代

Model2把一个项目分成三部分,包括:视图、控制、模型。

image-20210719091306982
  1. 用户发送请求。
  2. Servlet接收请求数据,并调用对应的业务逻辑方法。
  3. 业务处理完毕,返回更新后的数据给Servlet。
  4. Servlet转向到JSP,由JSP来渲染页面。
  5. 响应给前面更新后的页面。

职责分析

  • Constroller()
    1. 取得表单数据
    2. 调用业务逻辑
    3. 转向指定的页面
  • Model(模型)
    1. 业务逻辑
    2. 保存数据的状态
  • View(视图)
    1. 显示页面

Model2这样不仅提高了代码的利用率与项目的扩展性,且大大降低了项目的维护成本。Model1模式的实现比较简单,适用于快速开发小规模项目,Model1中JSP页面身兼View和Constroller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护难度。Model2消除了Model1缺点。

2、回顾Servlet

  1. 新建一个Maven工程作为父工程,并配置公共依赖pom.xml

<?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>org.example</groupId>
<artifactId>springmvc-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springmvc-01-servlet</module>
</modules>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>

<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.37</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
  1. 新建一个springmvc-01-servlet工程,并添加web app的支持。
  2. 为为安全,我们也给springmvc-01-servlet工程导入jsp和servlet依赖<?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”>
    <parent>
    <artifactId>springmvc-parent</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springmvc-01-servlet</artifactId>
    <packaging>war</packaging>

    <name>springmvc-01-servlet Maven Webapp</name>
    <!– FIXME change it to the project’s website –>
    <url>http://www.example.com</url>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
    </dependency>

    <dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>servlet-api</artifactId>
    <version>6.0.37</version>
    </dependency>

    <dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>jsp-api</artifactId>
    <version>6.0.37</version>
    </dependency>
    </dependencies>

    <build>
    <finalName>springmvc-01-servlet</finalName>


    </build>
    </project>
  3. 创建一个Servlet01并编写相应的逻辑public class Servlet01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    request.setCharacterEncoding(“utf-8”);

    String method = request.getParameter(“method”);
    HttpSession session = request.getSession();
    if (“add”.equals(method)) {
    session.setAttribute(“msg”, “执行了add方法”);
    } else if (“delete”.equals(method)) {
    session.setAttribute(“msg”, “执行了delete方法”);
    } else {
    session.setAttribute(“msg”, “没有执行方法”);
    }
    request.getRequestDispatcher(“/WEB-INF/jsp/index.jsp”).forward(request, response);
    }
    }
  4. 在webapp/WEB-INF/web.xml中配置我们的servlet,或者使用注解来配置。<!DOCTYPE web-app PUBLIC
    “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
    “http://java.sun.com/dtd/web-app_2_3.dtd” >

    <web-app>
    <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>Servlet01</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <session-config>
    <session-timeout>15</session-timeout>
    </session-config>

    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
  5. 最后我们配置tomcat,前提是已经安装tomcatimage-20210719104134239image-20210719104223042image-20210719104229395image-20210719104248017
  6. 最后进行测试即可[Title](http://localhost:8080/springmvc_01_servlet_war/hello?method=addimage-20210719104456703[Title](http://localhost:8080/springmvc_01_servlet_war/hello?method=deleteimage-20210719104521488[Title](http://localhost:8080/springmvc_01_servlet_war/helloimage-20210719104559377
  • MVC框架要做哪些事情
    1. 把url映射到java类或java类的方法。
    2. 封装用户提交的数据。
    3. 处理请求–调用相关的业务处理–封装响应数据。
    4. 将响应的数据进行渲染,jsp/html等表示层数据。
  • 说明常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET、Zend Framework、JSF;常见的前端MVC框架:vue、angularjs、react、backbone由MVC演化出了另外一些模式,如:MVP、MVVM等…

3、什么是SpringMVC?

Spring MVC是Spring Framework的一部分,是基于java实现MVC的轻量级Web框架。

官方文档:Web on Servlet Stack (spring.io)

我们为什么要学习SpringMVC呢?

  1. 轻量级,简单易学
  2. 高效,基于请求响应的MVC框架。
  3. 与Spring兼容性好,无缝结合。
  4. 约定优于配置。
  5. 功能强大:RESTful、数据验证、格式化、本地化、主题等。
  6. 简洁灵活。

Spring的web框架围绕DispatcherServlet [调度Servlet]设计。

DispatcherServlet的作用是将请求分到不同的处理器,从Spring2.5开始,使用Java5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁。

正因为SprinMVC好,简单,便捷、易学、天生和Spring无缝集成(使用Spring IOC和AOP),使用约定优于配置,能够进行简单的junit测试,支持Restful风格,异常处理,本地化,国际化,数据验证,类型转换,拦截器 等等…所以我们要学习。

最重要的一点还是用的人多,使用的公司多

3.1 中心控制器

Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器,从Spring2.5开始,使用Java 5或者以上的用户可以采用基于注解的controller声明方式。

Sprin MVC框架像许多其他MVC框架一样,以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Serviet(它继承自HtppServlet基类)

图片

SpringMVC的原理如下图所示:

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

图片

3.2 SpringMVC执行原理

  1. 参考图1image-20210719221224017image-20210719221114821
  2. 参考图2图片

图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

简要分析执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。我们假设请求的url为 : http://localhost:8080/SpringMVC/hello如上url拆分成三部分:http://localhost:8080服务器域名SpringMVC部署在服务器上的web站点hello表示控制器通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
  6. Handler让具体的Controller执行。也就是我们Controllerer的实现。
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。
  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
  12. 最终视图呈现给用户。
  13. image-20210719213926151

4、Hello,SpringMVC

  • 配置版、
    1. 新建一个web工程,并导入SpringMVC的依赖。
    2. 配置web.xml,注册DispatcherServlet<!DOCTYPE web-app PUBLIC
      “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
      “http://java.sun.com/dtd/web-app_2_3.dtd” >

      <web-app>
       <display-name>Archetype Created Web Application</display-name>


       <!– SpringMVC的前端控制器,这个是SpringMVC的核心,请求分发器 –>
       <servlet>
         <servlet-name>springmvc</servlet-name>
         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
               <!–
             以下两行配置不是必须的,如果没有配置,配置文件就应该在WEB-INF下[servlet=name]-servlet.xml
             如果配置了,就没有名字要求,否则必须以:[servlet-name]-servlet.xml
            –>
           <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:springmvc-servlet.xml</param-value>
         </init-param>
         <!– 启动级别,数值越小优先执行,n > 0 –>
         <load-on-startup>1</load-on-startup>
       </servlet>

       <!– 拦截设置 –>
       <!–
           /:只匹配所有请求,不会去匹配jsp
           /*:匹配所有的请求,包括jsp页面。
       –>
       <servlet-mapping>
         <!– 由SpringMVC拦截所有请求 –>
         <servlet-name>springmvc</servlet-name>
         <url-pattern>/</url-pattern>
       </servlet-mapping>

      </web-app>
    3. 编写SpringMVC的配置文件,名称:springmvc-servlet.xml:[servlet-name]-servlet.xml。说明:这晨的名称要求是按照官方来的。<?xml version=”1.0″ encoding=”UTF-8″?>
      <beans xmlns=”http://www.springframework.org/schema/beans”
            xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
            xsi:schemaLocation=”http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-4.2.xsd”>

      </beans>
    4. 添加处理器映射器(Spring4.0后会默认加载到容器中)<bean class=”org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping”/>
    5. 添加处理器适配器(Spring4.0后会默认加载到容器中)<bean class=”org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter”/>
    6. 添加视图解析器(可以不用配置,如果不配置在处理器返回的路径中应该写全限定名)<bean id=”InternalResourceViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
      <property name=”prefix” value=”/WEB-INF/jsp/”/>
      <property name=”suffix” value=”.jsp”/>
      </bean>
    7. 编写处理器/**
      * 处理器
      * 一般我们不用以下实现接口的方式进行开发,只是在学习期间,更进一步了解工作原理
      */
      public class HelloMvc implements Controller {
      @Override
      public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
      ModelAndView mv = new ModelAndView();
      mv.addObject(“msg”, “HelloMvc”);
      mv.setViewName(“hello”);
      return mv;
      }
      }
    8. 在sprinvmvc-servlat.xml中配置我们的处理器,我们在实际开发中也不会这样去配置,一般使用注解来开发,由于是笔记,学习期间,这样写更进一步了解工作原理。<!– 处理器,又名:Controller –>
      <bean id=”/hello” class=”cn.ziyia.controller.HelloMvc”/>说明:
      • 实现接口Controller定义控制器是较老的办法,我们实际开发中不会这么使用,只是为了更进一步了解工作原理。
      • 这种方式的缺点是:一个控制器中只有一个方法,如果要多个方法则需要再定义多个Controller;定义的方式比较麻烦,所以现在不会这样使用。
      注意:Spring4.0后的处理器映射器和处理器适配器默认会加载到容器中,不需要我们手动配置。
  • 注解版实现步骤其实非常的简单:新建一个web项目导入相关jar包编写web.xml , 注册DispatcherServlet编写springmvc配置文件接下来就是去创建对应的控制类 , controller最后完善前端视图和controller之间的对应测试运行调试.使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
    1. 新建一个web工程,springmvc-03-annotation
    2. 由于Maven中可能存储资源过虑问题,我们将配置完善<build>

      <resources>
      <resource>
      <directory>src/main/java</directory>
      <includes>
      <include>**/*.properties</include>
      <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
      </resource>
      <resource>
      <directory>src/main/resources</directory>
      <includes>
      <include>**/*.properties</include>
      <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
      </resource>
      </resources>
      </build>
    3. 在pom.xml文件引入相关的依赖,主要有Spring框架核心库,Spring MVC、Servlet、JSTL,等,我们在父工程依赖中已经引入了。
    4. 配置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″>


      <!–1.注册servlet–>
      <servlet>
      <servlet-name>SpringMVC</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!–通过初始化参数指定SpringMVC配置文件的位置,进行关联–>
      <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc-servlet.xml</param-value>
      </init-param>
      <!– 启动顺序,数字越小,启动越早 –>
      <load-on-startup>1</load-on-startup>
      </servlet>


      <!–所有请求都会被springmvc拦截 –>
      <servlet-mapping>
      <servlet-name>SpringMVC</servlet-name>
      <url-pattern>/</url-pattern>
      </servlet-mapping>

      </web-app>
      • / 和 /* 的区别:< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。< url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
 - 注意web.xml版本问题,要最新版!
 - 注册DispatcherServlet
 - 关联SpringMVC的配置文件
 - 启动级别为1
 - 映射路径为 / 【不要用/*,会404】
  1. 添加Spring MVC配置文件在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:<?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:content=”http://www.springframework.org/schema/context”
    xmlns:mvc=”http://www.springframework.org/schema/mvc”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd”>

    <!– 自动投扫描包,让指定包下的注解生效 –>
    <content:component-scan base-package=”cn.ziyia.controller”/>
    <!– 让SpringMVC不处理静态资源,html,js,css –>
    <mvc:default-servlet-handler/>
    <!–
    支持mvc注解驱动
    在Spring中一般采用@RequestMappering游逛来完成映射关系
    要想使@RequestMapping注解生效
    必须向上下文中注解@DefaultAnntationHandlerMapping
    和一个AnnotationMethodHandlerAdapter实例。
    这两个实例分别在类级别和方法级处理。
    而annotation-driven配置帮助我们自动完成上述两个实例的注入
    在Spring4.0后,这两个类默认会加载到容器中,所以如果你使用的是4.0版本及以上的版本
    可以不需要配置。
    –>
    <mvc:annotation-driven/>

    <!– 视图解析器DispatcherServlet给他的ModelAndView–>
    <bean id=”InternalResourceViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
    <property name=”prefix” value=”/WEB-INF/jsp/”/>
    <property name=”suffix” value=”.jsp”/>
    </bean>

    </beans>在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
    • 让IOC的注解生效
    • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
    • MVC的注解驱动
    • 配置视图解析器
  2. 创建Controller编写一个Java控制类:cn.ziyia.controller.HelloSpringMVC , 注意编码规范@Controller
    @RequestMapping(“/parent”)
    public class HelloSpringMVC {

    // http://localhost:8080/springmvc_03_annotation_war/hello
    @RequestMapping(“/hello”)
    public String hello(Model model) {
    // 为部分数据存储在session作用域中
    model.addAttribute(“msg”, “年少有为”);
    // /WEB-INF/jsp/hello.jsp
    return “hello”;
    }


    // http://localhost:8080/springmvc_03_annotation_war/m
    @RequestMapping(“/m”)
    public String msg(Model model) {
    // 存储到session作用域中
    model.addAttribute(“msg”, “嘿嘿,msg”);
    // /WEB-INF/jsp/msg.jsp
    return “msg”;
    }
    }
    • @Controller是为了让Spring IOC容器初始化时自动扫描到。
    • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该为:主机:端口/站点名/parent/hello
    • 方法中声明Model类型的参数是为了把Action中的数据带到视图中。
    • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp
  3. 创建视图层在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;可以通过EL表示取出Model中存放的值,或者对象;hello.jsp<%–
    Created by IntelliJ IDEA.
    User: Administrator
    Date: 2021/7/20
    Time: 9:46
    To change this template use File | Settings | File Templates.
    –%>
    <%@ page contentType=”text/html;charset=UTF-8″ language=”java” %>
    <%@ page isELIgnored=”false” %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>msg.jsp<%–
    Created by IntelliJ IDEA.
    User: Administrator
    Date: 2021/7/20
    Time: 9:55
    To change this template use File | Settings | File Templates.
    –%>
    <%@ page contentType=”text/html;charset=UTF-8″ language=”java” %>
    <%@ page isELIgnored=”false” %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
  4. 配置Tomcat运行image-20210720102608726image-20210720102619478

可以发现,我们的两个请求都可以指向一个视图,但是页面结果都是不一样的,从这里可以看出视图是被复用的,而控制器参与视图之间是弱耦合关系。

5、Controller及RestFul风格

5.1 控制器Controller

  • 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
  • 控制器负责解析用户的请求并将其转换为一个模型。
  • 在Spring MVC中一个控制器类可以包含多个方法(继承接口的方式除外)
  • 在Spring MVC中,对于Controler的配置方式有很多种

5.2 RestFul风格

RestFul就是不念旧恶资源定位及资源操作的风格,不是标准也不是协议,只是一种 风格,基于这个风格设计的软件可以更简洁,更有层次,更安全,更易于实现缓存等机制。

功能:

  • 资源:互联网所有的事物都可以被抽象为资源。
  • 资源操作:使用POST、GET、DELETE、PUT,使用不同方法对资源进行操作。
  • 分别对应:添加,查询、删除、修改

传统方式操作资源:通过不同的参数来实现不同的效果,方法单一:post和get

image-20210720110809462

使用RestFul操作资源:可以通过不同的请求方式来实现不同的效果,如果:请求地址一样,但是功能可以不同。

5.2.1 实践

在已配置Spring MVC的情况下,新建一个RestFulController

@Controller
public class RestFulController {
}

在Spring MVC中可以使用@PathVariable注解,让方法参数的值对应绑定到一个URL模型变量上。

@Controller
public class RestFulController {
@RequestMapping("/test02/{str}")
public String test02(@PathVariable int str, Model model) {
model.addAttribute("msg", str);
return "test02";
}
}
image-20210720151904409

使用路径变量的好处:

  1. 使路径变得更加简洁
  2. 获参数更加方便,框架会自动进行类型转换。
  3. 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,而不会是参数转换失败。如:我们传递一个字符串image-20210720151949247
  4. 我们来修改下对应的参数类型,再次测试@Controller
    public class RestFulController {

    @RequestMapping(“/test01”)
    public String test01(String str, Model model) {
    model.addAttribute(“msg”, str);
    return “test01”;
    }

    @RequestMapping(“/test02/{str}”)
    public String test02(@PathVariable String str, Model model) {
    model.addAttribute(“msg”, str);
    return “test02”;

    }
    }image-20210720152952598Spring MVC的@RequestMapping注解能够处理HTTP请求的方法,比如:GET、PUT、POST、DELETE、PATCH所有地址栏请求默认都会是HTTP GET类型的,我们在注解中可以使用method属性来指定只被处理的请求方式。方法级别的注解变体有如下几个:组合注解
    • @GetMapping
    • @PostMapping
    • @PutMappin
    • @DeleteMapping
    • @PatchMapping
    例如:@GetMapping是一个组合注解它所扮演的是@RequestMapping(value = “xxx”, method = RequestMethod.GET)的一个快捷方式,平时使用的会比较多。

6、结果跳转方式

6.1 ModelAndView

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

页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>

对应的controller类

public class ControllerTest1 implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}

6.2 Servlet Api

通过设置ServletAPI , 不需要视图解析器 .

  1. 1、通过HttpServletResponse进行输出/* 输出数据 */
    @RequestMapping(“/result/t1”)
    public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
    rsp.getWriter().println(“Hello,Spring BY servlet API”);
    }
  2. 通过HttpServletResponse实现重定向/* 重定向 */
    @RequestMapping(“/result/t2”)
    public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException, IOException {
    // 拼接站点名,容错
    rsp.sendRedirect(req.getContextPath() + “/index.jsp”);
    }
  3. 通过HttpServletRequest实现转发/* 请求转发 */
    @RequestMapping(“/result/t3”)
    public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
    //转发
    req.setAttribute(“msg”, “/result/t3”);
    req.getRequestDispatcher(“/WEB-INF/jsp/test01.jsp”).forward(req, rsp);
    }

6.3 SpringMVC

由于重定向相当于2次请求,所以无法把参数加在model中传过去。在上面例子中,页面获取不到msg参数。要想获取参数,可以手动拼url,把参数带在后面。

Spring 3.1 提供了一个很好用的类:RedirectAttributes。 使用这个类,我们可以把参数随着重定向传到页面,不需自己拼url了。

还是使用RedirectAttributes,但是这次不用addAttribute方法,spring为我们准备了新方法,addFlashAttribute()。

这个方法原理是放到session中,session在跳到页面后马上移除对象。所以你刷新一下后这个值就会丢失。

  • 无视图处理器通过SpringMVC来实现转发和重定向 – 无需视图解析器;测试前,需要将视图解析器注释掉@RequestMapping(“/rsm/t1”)
    public String test1(){
    //转发
    return “/index.jsp”;
    }

    @RequestMapping(“/rsm/t2”)
    public String test2(){
    //转发二
    return “forward:/index.jsp”;
    }

    @RequestMapping(“/rsm/t3”)
    public String test3(){
    //重定向
    return “redirect:/index.jsp”;
    }
  • 有视图处理器重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.可以重定向到另外一个请求实现 .@RequestMapping(“/rsm2/t1”)
    public String test1(){
    // 默认就是进行请求转发
    return “test01”;
    }

    @RequestMapping(“/rsm2/t2”)
    public String test2(){
    // 重定向
    return “redirect:/index.jsp”;
    // return “redirect:hello.do”; //hello.do为另一个请求/
    }

注意:如果没有视图解析器,我们所引用的资源,如果以/开头的资源,则从WEB-INF目录下查询,如果没有以/开头,则在当前URL下查找。如果有视图解析器,页面 : {视图解析器前缀} + viewName +{视图解析器后缀}。视图解析器虽然可以省略,但不推荐那样做。

7、数据的处理

7.1 数据处理

  1. 提交的域名称和处理方法的参数名一致@RequestMapping(“/hello”)
    public String hello(String name){
    System.out.println(name);
    return “hello”;
    }请求:hello?name=abc后台输出 : abc
  2. 提交的域名称和处理方法的参数名不一致可以使用@RequestParam注解来指定请求时的参数名//@RequestParam(“username”) : username提交的域的名称 .
    @RequestMapping(“/hello”)
    public String hello(@RequestParam(“username”) String name){
    System.out.println(name);
    return “hello”;
    }请求:hello?username=nsyw后台输出:nsyw
  3. 提交的是一个对象要求提交的表单域和对象的属性名一致 , 参数使用对象即可,// 实体类
    public class User {
    private int id;
    private String name;
    private int age;
    //构造
    //get/set
    //tostring()
    }

    // 处理器方法
    @RequestMapping(“/user”)
    public String user(User user){
    System.out.println(user);
    return “hello”;
    }请求: user?name=nsyw&id=1&age=18后台输出 : User { id=1, name=’kuangshen’, age=15 }说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则对象字段就是null。如果对象中有字段和处理器方法上的字段名一致,那么都会被赋值。

7.2 数据显示到前端

  1. 通过ModelAndView我们前面一直都是如此 . 就不过多解释,只能使用在实现了Controller的handleRequest方法上public class ControllerTest1 implements Controller {

    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    //返回一个模型视图对象
    ModelAndView mv = new ModelAndView();
    mv.addObject(“msg”,”ControllerTest1″);
    mv.setViewName(“test”);
    return mv;
    }
    }
  2. 通过ModelMap
    @RequestMapping(“/hello”)
    public String hello(@RequestParam(“username”) String name, ModelMap model){
    //封装要显示到视图中的数据
    //相当于req.setAttribute(“name”,name);
    model.addAttribute(“name”,name);
    System.out.println(name);
    return “hello”;
    }
  3. 通过Model
    @RequestMapping(“/ct2/hello”)
    public String hello(@RequestParam(“username”) String name, Model model){
    //封装要显示到视图中的数据
    //相当于req.setAttribute(“name”,name);
    model.addAttribute(“msg”,name);
    System.out.println(name);
    return “test”;
    }

7.3 处理请求乱码问题

  • 之前在Servlet中处理乱码
请求方式Tomcat7Tomcat7以上解决
GET乱码正常修改Tomcat的server.xml或手动转码
POST乱码乱码response.setCharacterEncoding(“UTF-8”); response.setContentType(“text/html;charset=UTF-8”);
  • 在Spring MVC中处理乱码
    1. 我们在首先写一个提交表单<form action=”/springmvc_04_restful_war/rsm2/t1″ method=”post”>
      <input name=”a” value=”年少有为”>
      <button>button</button>
      </form>
    2. 对应的处理器@RequestMapping(“/rsm2/t1”)
      public String test1(String a, String b, Model model){
      model.addAttribute(“msg”, a + b);
      System.out.println(“a = ” + a);
      System.out.println(“b = ” + b);
      // 默认就是进行请求转发
      return “test01”;
      }image-20210721092721545
    3. 显而易见,出现了乱码image-20210721092834476
  • 方案一:手动转码,不推荐
  • 方案二
    1. 修改Tomcat的conf/server.xml配置文件 <Connector port=”8080″ protocol=”HTTP/1.1″ ConnectorURIEncoding=”utf-8″ URIEncoding=”utf-8″
      connectionTimeout=”20000″
      redirectPort=”8443″ />
      <!– 这一步起到的作用是:解决Tomcat8以下版本的GET请求乱码问题 –>
    2. 我们自己写一个过虑器来处理POST请求的乱码,或者使用Spring MVC中提供的
      • 自己写public class Encoding implements Filter{

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {

        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        request.setCharacterEncoding(“UTF-8”);
        response.setCharacterEncoding(“UTF-8”);
        response.setHeader(“Content-type”, “text/html;charset=UTF-8”);
        filterChain.doFilter(servletRequest, servletResponse);
        }

        @Override
        public void destroy() {

        }
        }web.xml
        <filter>
        <filter-name>encoding</filter-name>
        <filter-class>cn.ziyia.controller.pojo.Encoding</filter-class>
        </filter>
        <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
        </filter-mapping>
      • 直接使用Spring MVC提供的,不需要手动写过虑器规则,直接配置那可。<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.xml中那可。Encoding.javapublic class Encoding 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 {
    }

    }

    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;
    }
    }一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!然后在web.xml中配置这个过滤器即可!

web.xml

<filter>
<filter-name>encoding</filter-name>
<filter-class>cn.ziyia.controller.pojo.Encoding</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

8、JSON

  • JSON(JavaScript Object Notation)Js对象标记,是一种轻量级的数据交换格式,目前使用特别广泛。
  • 采用完全独立于编程语言的文本格式来存储和表示数据。
  • 易于阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
  • 对象表示为键值对,数据由逗号分隔。
  • 花括号保存对象
  • 方括号保存数组

在后台有许多支持解析JSON的库,目前对于Java开源的JSON类库有许多,下面我们介绍三种比较常用的JSON库,比对说明,它们分别是:

  • Gson(项目地址:https://github.com/google/gson)Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求由Google自行研发而来,但自从在2008年5月公开发布第一版后已被许多公司或用户应用。Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要额外的jar,能够直接跑在JDK上。而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象,类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是Json解析的神器。
  • FastJson(项目地址:https://github.com/alibaba/fastjson)FastJson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发,无依赖,不需要例外额外的jar,能够直接跑在jdk上,FastJson在复杂类型的Bean转换Json字符串上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要定制引用,FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。
  • Jackson(项目地址:https:/github.com/FasterXML/jackson)Jackson所依赖的jar包较少,简单易用并且性能也要相对高些,而且Jackson社区相对比较活跃,更新速度也比较快,Jackson对于复杂类型的json转换bean会出现问题,一些集合Map、List的转换出现问题,Jackson对于复杂类型的bean转换json,转换的json格式不是标准的json格式。

总结:

  • FastJson的Api设计的最简单,最方便使用,直接使用JSON的两个静态方法即可完成四种操作,而Gson和Jackson都需要实例化一个对象
  • 数据量大时,使用Jackson
  • 如果有属性要求可以使用Gson/Jackson将bean转换json确保数据的正确性,使用FastJson将Json转换成Bean。

下面我们来简单使用这三个库,使用前先创建一个实体类

public class User {
private int id;
private String username;
private String password;


public User() {
}

public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}

public int getId() {
return id;
}

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

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

8.1 Gson

Maven依赖:

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
public class Test {
public static void main(String[] args) {
Gson gson = new Gson();

// 准备数据
User u1 = new User(1, "年少有为","root");
User u2 = new User(2, "Hainan", "Hainan");
User u3 = new User(3, "root", "root");

List<User> userList = new ArrayList<>();
userList.add(u1);
userList.add(u2);
userList.add(u3);

// 简单的Bean转换为Json字符串
String s1 = gson.toJson(u1);
System.out.println("简单的Bean转换为Json字符串:" + s1);

// Json字符串转换为Bean对象
User user = gson.fromJson(s1, User.class);
System.out.println("Json客串转换为Bean对象:" + user.toString());

// 带泛型List集合转换为Json
String s2 = gson.toJson(userList);
System.out.println("带泛型List集合转换为Json字符串:" + s2);


// 带泛型List集合转换为Json - 方式一
System.out.println("");
List<User> users1 = gson.fromJson(s2, new TypeToken<List<User>>(){}.getType());
for (User user1_e : users1) {
System.out.println(user1_e);
}

// 带泛型List集合转换为Json - 方式二
System.out.println("");
User[] users2 = gson.fromJson(s2, User[].class);
for (User user2_e : users2) {
System.out.println(user2_e);
}


// 带泛型List集合转换为Json - 方式三
System.out.println("");
List<Map<String, User>> users3 = gson.fromJson(s2, List.class);
for (Map user3_e : users3) {
System.out.println("user1 = " + user3_e);
}
}
}

简单的Bean转换为Json字符串:{“id”:1,”username”:”年少有为”,”password”:”root”} Json客串转换为Bean对象:User{id=1, username=’年少有为’, password=’root’} 带泛型List集合转换为Json字符串:[{“id”:1,”username”:”年少有为”,”password”:”root”},{“id”:2,”username”:”Hainan”,”password”:”Hainan”},{“id”:3,”username”:”root”,”password”:”root”}]

User{id=1, username=’年少有为’, password=’root’} User{id=2, username=’Hainan’, password=’Hainan’} User{id=3, username=’root’, password=’root’}

User{id=1, username=’年少有为’, password=’root’} User{id=2, username=’Hainan’, password=’Hainan’} User{id=3, username=’root’, password=’root’}

user1 = {id=1.0, username=年少有为, password=root} user1 = {id=2.0, username=Hainan, password=Hainan} user1 = {id=3.0, username=root, password=root}

8.2 FastJson

fastjson 三个主要的类:

JSONObject 代表 json 对象

JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。

JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取”键:值”对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。

JSONArray 代表 json 对象数组

内部是有List接口中的方法来完成操作的。

JSON代表 JSONObject和JSONArray的转化

JSON类源码分析与使用

仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。

/**

  • JSON实际上也是键值对(”key”:”value”)
  • key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)
  • value如果是字符串,用jsonobj.getString(“key”)获取
  • value如果是数 字,用jsonobj.getIntValue(“key”),jsonobj.getFloatValue(“key”),jsonobj.getInteger(“key”)等基本数据类型及其包装类的方法获取
  • value如果是布尔值,用jsonobj.getBoolean(“key”),jsonobj.getBooleanValue(“key”)获取
  • value如果是数 组,用jsonobj.getJSONArray(“key”)获取
  • value如果是Object对象,用jsonobj.get(“key”),获取
  • value如果是JSONObject对象,用jsonobj.getJSONObject(“key”)获取 */ /**
  • 该方法用于将已有的json字符串转换为json对象,并取出该对象中相应的key对应的value值
  • 将已有的字符串转换成jsonobject,用JSON.parseObject(jsonStr)方法
  • json中只要是{}就代表一个JSONObject,[]就代表一个JSONArray
  • 获取JSONObject对象用JSONObject jsonobject.getJSONObject(“key”)方法
  • 获取JSONArray对象用JSONObject jsonobject.getJSONArray(“key”)方法 */

Maven依赖:

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
public class Test {
public static void main(String[] args) {
// 准备数据
User u1 = new User(1, "年少有为","root");
User u2 = new User(2, "Hainan", "Hainan");
User u3 = new User(3, "root", "root");

List<User> userList = new ArrayList<>();
userList.add(u1);
userList.add(u2);
userList.add(u3);

// 简单的Bean转换为Json字符串
String s1 = JSON.toJSONString(u1);
System.out.println("简单的Bean转换为Json字符串:" + s1);

// Json字符串转换为Bean对象
User user = JSON.parseObject(s1, User.class);
System.out.println("Json字符串转换为Bean对象:" + user);

// 带泛型List集合转换为Json
String s2 = JSON.toJSONString(userList);
System.out.println(s2);

// 方式一
System.out.println();
List<User> users1 = JSON.parseObject(s2, new TypeReference<List<User>>(){});
System.out.println("users1 = " + users1);

// 方式二
System.out.println();
User[] users2 = JSON.parseObject(s2, User[].class);
System.out.println("users2 = " + Arrays.toString(users2));

// 方式三
System.out.println();
List<HashMap<String, User>> users3 = JSON.parseObject(s2, List.class);
System.out.println("users3 = " + users3);
}
}

简单的Bean转换为Json字符串:{“id”:1,”password”:”root”,”username”:”年少有为”} Json字符串转换为Bean对象:User{id=1, username=’年少有为’, password=’root’} [{“id”:1,”password”:”root”,”username”:”年少有为”},{“id”:2,”password”:”Hainan”,”username”:”Hainan”},{“id”:3,”password”:”root”,”username”:”root”}]

users1 = [User{id=1, username=’年少有为’, password=’root’}, User{id=2, username=’Hainan’, password=’Hainan’}, User{id=3, username=’root’, password=’root’}]

users2 = [User{id=1, username=’年少有为’, password=’root’}, User{id=2, username=’Hainan’, password=’Hainan’}, User{id=3, username=’root’, password=’root’}]

users3 = [{“password”:”root”,”id”:1,”username”:”年少有为”}, {“password”:”Hainan”,”id”:2,”username”:”Hainan”}, {“password”:”root”,”id”:3,”username”:”root”}]

8.3 Jackson

Maven依赖:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0-rc1</version>
</dependency>
public class Test {

public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();

// 准备数据
User u1 = new User(1, "年少有为", "root");
User u2 = new User(2, "Hainan", "Hainan");
User u3 = new User(3, "root", "root");

List<User> userList = new ArrayList<>();
userList.add(u1);
userList.add(u2);
userList.add(u3);

String s1 = mapper.writeValueAsString(u1);
System.out.println(s1);

User user = mapper.readValue(s1, User.class);
System.out.println(user);

String s2 = mapper.writeValueAsString(userList);
System.out.println(u2);


// 方式一
System.out.println();
List<User> users1 = mapper.readValue(s2, new TypeReference<List<User>>() { });
System.out.println("users1 = " + users1);

System.out.println();
User[] users2 = mapper.readValue(s2, User[].class);
System.out.println("users2 = " + Arrays.toString(users2));

// 方式三
System.out.println();
List<Map<String, User>> user3 = mapper.readValue(s2, List.class);
System.out.println("user3 = " + user3);

}
}

{“di”:1,”username”:”年少有为”,”password”:”root”} User{di=1, username=’年少有为’, password=’root’} User{di=2, username=’Hainan’, password=’Hainan’}

users1 = [User{di=1, username=’年少有为’, password=’root’}, User{di=2, username=’Hainan’, password=’Hainan’}, User{di=3, username=’root’, password=’root’}]

users2 = [User{di=1, username=’年少有为’, password=’root’}, User{di=2, username=’Hainan’, password=’Hainan’}, User{di=3, username=’root’, password=’root’}]

user3 = [{di=1, username=年少有为, password=root}, {di=2, username=Hainan, password=Hainan}, {di=3, username=root, password=root}]

8.4 在Spring MVC中使用使用JSON

假设已经搭建好SPring MVC环境的情况下,我们新建一个Controller

@Controller
public class Test {
@RequestMapping(value = "/test01")
@ResponseBody // 表示不使用视图解析器,直接将方法的返回值输出到页面
public String test01() {
User u1 = new User(1, "root", "root");
User u2 = new User(2, "年少有为", "root");
User u3 = new User(3, "今晚抓皮友", "root");
List<User> users = new ArrayList<>();
users.add(u1);
users.add(u2);
users.add(u3);
return JSON.toJSONString(users);
}
}
image-20210721152923446

发现出现了乱码问题,我们需要设计一下他的编码格式为UTF-8,以及它的返回类型,我们通过@RequestMapping的produces属性来实现,代码如下所示:

@Controller
public class Test {
@RequestMapping(value = "/test01", produces = {"text/plain;charset=utf-8","text/html;charset=utf-8"})
@ResponseBody // 表示不使用视图解析器,直接将方法的返回值输出到页面
public String test01() {
User u1 = new User(1, "root", "root");
User u2 = new User(2, "年少有为", "root");
User u3 = new User(3, "今晚抓皮友", "root");
List<User> users = new ArrayList<>();
users.add(u1);
users.add(u2);
users.add(u3);
return JSON.toJSONString(users);
}
}
image-20210721153326328

此时页面就正常了。

上方解决乱码的方式比较麻烦,如果项目中有许多请求则每个都 要添加,可以通过Spring配置统一指定,这样就不用每次都去处理了,我们可以在springmvc的配置文件上添加一段配置StringHttpMessageConverter转换配置。

    <mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value = "text/html;charset=UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
  • @ResponseBody // 表示不使用视图解析器,直接将方法的返回值输出到页面
  • Spring 4之后新加入了@RestController注解,是@ResponseBody和@Controller的组合注解,在类上加上该注解,类中所有的方法都只会返回 json 字符串了,不用再每一个都添加@ResponseBody !我们在前后端分离开发中,一般都使用 @RestController ,十分便捷!

9、Spring MVC注解总结

  1. @RequestParam注解作用:是将请求参数绑定到你的控制器的方法参数上,是springmvc中的接收普通参数的注解,要好好的理解,在实际开发中,很常用的注解之一。属性:value是请求参数中的名称。required是请求参数是否必须提供参数,它的默认是true,意思是表示必须提供,假如你不提供就会报错啦。简单理解:将请求中传递的参数对应到处理器的方法参数中,一般在url中的参数名和处理器方法中的参数名不一致时使用
  2. @ResponseBody注解作用:常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型简单理解:当前端传递的参数过多时,我们后端接收就比较繁琐,这时可以在处理器的参数上使用该注解,请求中的参数如果和注解对象中的字段名相同就为对象的字段赋值,我们如果不想写一一对应的参数,可以使用Map
  3. @PathVaribale注解作用:该注解是用于绑定url中的占位符,但是注意,spring3.0以后,url才开始支持占位符的,它是springmvc支持的rest风格url的一个重要的标志,什么是占位符呢,我去个例子比如:请求的url中/user/{id},这个{id}就是url的占位符简单理解:该注解使一个变量的值,可以映射到url中和参数同名的占位符变量上。
  4. @Controller注解作用:该注解是用来标记一个类的,如果被一个类被标注为Controller的话,那么它就会被spring扫面机制扫面到,然后会自动将其注册为spring应用程序的上下文里的一个Bean,controller的真正作用是负责是处理由DispatcherServlet分发的请求,它把用户请求的数据经过业务处理层处理的,然后我们通过调用对应的Service,最后封装成一个Model,然后把Model返回给View显示到前端给用户的。简单理解:使用该注解使我们类自动注册为bean。
  5. @RequsetMappring注解作用:该注解的作用就是用来处理请求地址映射的,也就是说将其中的所有处理器方法都映射到url路径上,他有6个属性。属性:method:是让你指定请求的method的类型,比如常用的有get和post。value:是指请求的实际地址,如果是多个地址就用{}来指定就可以啦。produces:指定返回的内容类型,当request请求头中的Accept类型中包含指定的类型才可以返回的。consumes:指定处理请求的提交内容类型,比如一些json、html、text等的类型。headers:指定request中必须包含那些的headed值时,它才会用该方法处理请求的。params:该属性是指定request中一定要有的参数值,它才会使用该方法处理请求,以上都是在我们实际开发中常用到的一些注解,希望大家好好的学习。简单理解:定义接收请求的规则,符合规则的请求将被处理。

10、整合SSM

  • 整合Spring
    1. 数据库连接池:必须的,不用多做解释
    2. SqlSessionFactory:提供给其他 Bean使用
    3. MapperScannerConfigurer:扫描Dao层的接口并注入SqlSessionFactory
    4. ………..
    spring-dao.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:p=”http://www.springframework.org/schema/p”
    xmlns:c=”http://www.springframework.org/schema/c”

    xmlns:context=”http://www.springframework.org/schema/context”
    xmlns:tx=”http://www.springframework.org/schema/tx”
    xmlns:aop=”http://www.springframework.org/schema/aop”
    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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd”>

    <!–
    Dao层的整合:
    1,数据库连接池:必须的,不用多做解释
    2,SqlSessionFactory:提供给其他 Bean使用
    3,MapperScannerConfigurer:扫描Dao层的接口并注入SqlSessionFactory
    –>


    <!– DriverManagerDataSource –>
    <!–方式一:直接在xml标签中配置–>
    <!– <bean id=”dataSource” class=”org.springframework.jdbc.datasource.DriverManagerDataSource”>
    <property name=”driverClassName” value=”com.mysql.cj.jdbc.Driver”/>
    <property name=”url” value=”jdbc:mysql://localhost:3306/SpringMybatisTest?serverTimezone=GMT%2B8&amp;characterEncoding=utf8″/>
    <property name=”username” value=”root”/>
    <property name=”password” value=”root”/>
    </bean>–>
    <!–方式二:引用properties–>
    <context:property-placeholder location=”classpath:mysql.properties”/>
    <bean id=”dataSource” class=”org.springframework.jdbc.datasource.DriverManagerDataSource”>
    <property name=”driverClassName” value=”${jdbc.driver}”/>
    <property name=”url” value=”${jdbc.url}”/>
    <property name=”username” value=”${jdbc.username}”/>
    <property name=”password” value=”${jdbc.password}”/>
    </bean>



    <!– SqlSessionFactory –>
    <bean id=”SqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”>
    <property name=”dataSource” ref=”dataSource”/>
    <!–Mybatis配置–>
    <property name=”configLocation” value=”classpath:mybatis-config.xml”/>
    </bean>


    <!–为业务实现类注入SqlSession–>
    <!– 方式一:手动注入SqlSession–>
    <!– <bean class=”org.mybatis.spring.SqlSessionTemplate”>–>
    <!– <constructor-arg index=”0″ ref=”SqlSessionFactory”/>–>
    <!– </bean>–>
    <!– <bean id=”userMapper” class=”org.example.UserMapperTemplate.UserMapperTemplate”>–>
    <!– <property name=”sqlSession” ref=”SqlSession”/>–>
    <!– </bean>–>

    <!– 方式二:凡是继承了SqlSessionDaoSupport的子类可以直接调用父类的getSqlSession()获得SqlSession –>
    <!– <bean id=”userMapper” class=”cn.ziyia.service.UserMapperTemplate”>–>
    <!– <property name=”sqlSessionFactory” ref=”SqlSessionFactory”/>–>
    <!– </bean>–>

    <!–
    方式三:让Spring自动扫描接口包,并注入SqlSessionFactoryBean,并注入到容器中
    简单来说就是代替了方式二,不用你手动配置,只需扫描实现了某个包下的接口,
    那么扫描到的类会被注入,并注入SqlSessionFactoryBean,并加入到容器中

    创建的映射器的命名问题。从beans.xml文件中我们可以看出,
    我们没有办法给MapperScannerConfigurer创建的这些映射器指定id或name属性,
    它们对我们似乎是不可见的。这个问题的解决之道在于采用了Spring针对自动侦测到的组件的默认命名策略
    ,亦即把类/接口名字的首字母小写,其他不变,作为映射器的名字。
    例如,映射器接口TeacherMapper被扫描后创建的映射器bean名为teacherMapper。
    因此,我们可以像以前一样使用这样的代码来得到TeacherMapper实例:
    –>
    <bean class=”org.mybatis.spring.mapper.MapperScannerConfigurer”>
    <property name=”sqlSessionFactoryBeanName” value=”SqlSessionFactory”/>
    <!– <property name=”sqlSessionFactory” ref=”SqlSessionFactory”/>–>
    <property name=”basePackage” value=”cn.ziyia.dao”/>
    </bean>

    </beans>
  • 整合Spring MVC
    1. 自动投扫描包,让指定包下的注解生效
    2. 视图解析器
    3. 使用@ResponseBody时的乱码问题
    4. ……..
    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:content=”http://www.springframework.org/schema/context”
    xmlns:mvc=”http://www.springframework.org/schema/mvc”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd”>

    <!–
    MVC层的整合,也包含Controller的配置:
    1,自动投扫描包,让指定包下的注解生效
    2,视图解析器
    3,使用@ResponseBody时的乱码问题
    –>


    <!– 自动投扫描包,让指定包下的注解生效 –>
    <content:component-scan base-package=”cn.ziyia.controller”/>

    <!–
    支持mvc注解驱动
    在Spring中一般采用@RequestMappering游逛来完成映射关系
    要想使@RequestMapping注解生效
    必须向上下文中注解@DefaultAnntationHandlerMapping
    和一个AnnotationMethodHandlerAdapter实例。
    这两个实例分别在类级别和方法级处理。
    而annotation-driven配置帮助我们自动完成上述两个实例的注入
    –>



    <!– 视图解析器DispatcherServlet给他的ModelAndView–>
    <bean id=”InternalResourceViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
    <property name=”prefix” value=”/WEB-INF/jsp/”/>
    <property name=”suffix” value=”.jsp”/>
    </bean>

    <mvc:annotation-driven>
    <mvc:message-converters register-defaults=”true”>
    <bean class=”org.springframework.http.converter.StringHttpMessageConverter”>
    <property name=”supportedMediaTypes” value = “text/html;charset=UTF-8” />
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>
    </beans>
  • 整合Mybatis
    1. 配置mybatis设置
    2. 配置实体类的别名
    mybatis-config.xml<?xml version=”1.0″ encoding=”UTF-8″?>
    <!DOCTYPE configuration
    PUBLIC “-//mybatis.org//DTD Config 3.0//EN”
    “http://mybatis.org/dtd/mybatis-3-config.dtd”>
    <configuration>

    <settings>
    <setting name=”mapUnderscoreToCamelCase” value=”true”/>
    <setting name=”useActualParamName” value=”false”/>
    <!– <setting name=”logImpl” value=”STDOUT_LOGGING”/>–>
    <!– <setting name=”logImpl” value=”LOG4J”/>–>
    </settings>

    <typeAliases>
    <package name=”cn.ziyia.pojo”/>
    </typeAliases>

    <mappers>
    <package name=”cn.ziyia.dao”/>
    </mappers>

    </configuration>

application.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--
将各层的配置整合到一起。
-->
<context:annotation-config/>
<!-- 让SpringMVC不处理静态资源,html,js,css -->
<mvc:default-servlet-handler/>

<import resource="spring-mvc.xml"/>
<import resource="spring-dao.xml"/>
<import resource="spring-service.xml"/>

</beans>

整合实现的简单例子:

11、拦截器

11.1 概述

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

过虑器与拦截器的区别:拦截器是AOP思想的具体应用

过虑器:

  • Servlet规范中的一部分,任何 java web工程都可以使用
  • 在url-patter中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器:

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js是不会进行拦截的

11.2 自定义拦截器

那如何实现拦截器呢?想要自定义拦截器,必须实现HandlerInterceptor接口。

1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持

2、配置web.xml 和 springmvc-servlet.xml 文件

3、编写一个拦截器

public class MyInterceptor implements HandlerInterceptor {
//在请求处理的方法之前执行
//如果返回true执行下一个拦截器
//如果返回false就不执行下一个拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("--------拦截前--------");
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("--------拦截后--------");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("--------清理--------");
}
}

4,在springmvc的配置文件中配置拦截器


<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路径及其子路径-->
<!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
<!--/admin/** 拦截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是拦截器-->
<bean class="cn.ziyia.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

5,编写一个Controller,接收请求

@Controller
public class UserController {

private static ApplicationContext context;
static {
context = new ClassPathXmlApplicationContext("application.xml");
}

@RequestMapping("/test")
@ResponseBody
public String test01() {
User user = new User(1, "Administrator", "Administrator", "Administrator@qq.com");
String jsonString = JSON.toJSONString(user);
return jsonString;
}
}

6,打开浏览器,访问处理器

image-20210723201234288
image-20210723201316205

11.3 验证用户是否登录 (认证用户)

  1. 我们写一个首页,作为用户登录之后才可浏览的页面main.xml<%–
    Created by IntelliJ IDEA.
    User: Administrator
    Date: 2021/7/23
    Time: 20:34
    To change this template use File | Settings | File Templates.
    –%>
    <%@ page contentType=”text/html;charset=UTF-8″ language=”java” %>
    <html>
    <head>
    <title>主界面</title>
    </head>
    <body>
    ${sessionScope.username}
    </body>
    </html>
  2. 既然要登录,就得有一个登录页面login.jsp<%–
    Created by IntelliJ IDEA.
    User: Administrator
    Date: 2021/7/23
    Time: 20:04
    To change this template use File | Settings | File Templates.
    –%>
    <%@ page contentType=”text/html;charset=UTF-8″ language=”java” %>
    <!doctype html>
    <html lang=”en”>
    <head>
    <meta charset=”UTF-8″>
    <meta name=”viewport”
    content=”width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0″>
    <meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>登录</title>
    </head>
    <body>
    <form action=”${pageContext.request.contextPath}/user/login” method=”post”>
    <p><input name=”username” value=”admin”/> </p>
    <p><input name=”password” value=”admin”/> </p>
    <p><button>登录</button></p>
    </form>
    </body>
    </html>
  3. 在index.jsp中进行导航,为首页和登录页提供访问入口
  4. index.xml<%@ page contentType=”text/html;charset=UTF-8″ language=”java” %>
    <!doctype html>
    <html lang=”en”>
    <head>
    <meta charset=”UTF-8″>
    <meta name=”viewport”
    content=”width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0″>
    <meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>Index</title>
    </head>
    <body>

    <a href=”${pageContext.request.contextPath}/user/toLogin”><button>登录</button></a>
    <a href=”${pageContext.request.contextPath}/user/toMain”><button>首页</button></a>

    </body>
    </html>
  5. 创建一个处理来实现登录以及跳转的功能MyInterceptor.java
    @Controller
    @RequestMapping(“/user”)
    public class UserController {

    private static ApplicationContext context;
    static {
    context = new ClassPathXmlApplicationContext(“application.xml”);
    }

    // 环境搭建测试,请读者忽略
    @RequestMapping(“/test”)
    @ResponseBody
    public String test01() {
    User user = new User(1, “Administrator”, “Administrator”, “Administrator@qq.com”);
    String jsonString = JSON.toJSONString(user);
    System.out.println(“jsonString = ” + jsonString);
    return jsonString;
    }

    // 跳转到登录在页
    @GetMapping(“/toLogin”)
    private String toLogin() {
    return “login”;
    }

    // 跳转到首页
    @GetMapping(“/toMain”)
    private String toMain() {
    return “main”;
    }

    // 处理登录请求
    @PostMapping(“/login”)
    private String login(HttpSession session, String username, String password) {
    if (!””.equals(username)) {
    session.setAttribute(“username”, username);
    }
    return “main”;
    }
    }
  6. 编写我们的拦截器,编写拦截规则MyInterceptor.javpublic class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HttpSession session = request.getSession();

    // 不拦截登录页面
    if (request.getRequestURI().contains(“/login”)) {
    return true;
    }

    // 不拦截已经登录的用户
    if (session.getAttribute(“username”) != null) {
    return true;
    }

    // 用户没有登录就跳转到登录页面
    request.getRequestDispatcher(“/WEB-INF/jsp/login.jsp”).forward(request, response);
    return false;
    }

    }
  7. 配置我们编写的拦截器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:content=”http://www.springframework.org/schema/context”
    xmlns:mvc=”http://www.springframework.org/schema/mvc”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd”>
    <!– 自动投扫描包,让指定包下的注解生效 –>
    <content:component-scan base-package=”cn.ziyia.controller”/>

    <!– 视图解析器DispatcherServlet给他的ModelAndView–>
    <bean id=”InternalResourceViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
    <property name=”prefix” value=”/WEB-INF/jsp/”/>
    <property name=”suffix” value=”.jsp”/>
    </bean>

    <mvc:annotation-driven>
    <mvc:message-converters register-defaults=”true”>
    <bean class=”org.springframework.http.converter.StringHttpMessageConverter”>
    <property name=”supportedMediaTypes” value = “text/html;charset=UTF-8” />
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>



    <!–关于拦截器的配置–>
    <mvc:interceptors>
    <mvc:interceptor>
    <!–/** 包括路径及其子路径–>
    <!–/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截–>
    <!–/admin/** 拦截的是/admin/下的所有–>
    <mvc:mapping path=”/**”/>
    <!–bean配置的就是拦截器–>
    <bean class=”cn.ziyia.config.MyInterceptor”/>
    </mvc:interceptor>
    </mvc:interceptors>

    </beans>
  8. 最后使用Tomcat运行项目即可。

12、文件上传及下载功能

12.1 准备工作

文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

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

对表单中的 enctype 属性做个详细的说明:

application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。

multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

  • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
  • 而Spring MVC则提供了更简单的封装。
  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
  • CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

12.2 文件上传

  1. 导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;<!–文件上传–>
    <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
    </dependency>
    <!–servlet-api导入高版本的–>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    </dependency>
  2. 配置bean:multipartResolver<!–文件上传配置–>
    <bean id=”multipartResolver” class=”org.springframework.web.multipart.commons.CommonsMultipartResolver”>
    <!– 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 –>
    <property name=”defaultEncoding” value=”utf-8″/>
    <!– 上传文件大小上限,单位为字节(10485760=10M) –>
    <property name=”maxUploadSize” value=”10485760″/>
    <property name=”maxInMemorySize” value=”40960″/>
    </bean>
  3. 编写前端文件上传页面<%@ page pageEncoding=”UTF-8″ %>
    <!doctype html>
    <html lang=”en”>
    <head>
    <meta charset=”UTF-8″>
    <meta name=”viewport”
    content=”width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0″>
    <meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>文件上传</title>
    </head>
    <body>

    <form action=”${pageContext.request.contextPath}/file/upload” method=”post” enctype=”multipart/form-data”>
    <input type=”file” name=”file”/>
    <button>上传</button>
    </form>
    </body>
    </html>
  4. 处理器实现上传@Controller
    @RequestMapping(“/file”)
    public class FileController {

    private static ApplicationContext context;
    static {
    context = new ClassPathXmlApplicationContext(“application.xml”);
    }
    }
    1. 方式一(推荐)//@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”;
      }
    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”;
      }
    • 方式三:使用Servlet Api 的Part(不推荐,了解即可)
      1. 需要修改配置bean:multipartResolver,使@MultipartConfig生效<bean id=”multipartResolver” class=”org.springframework.web.multipart.support.StandardServletMultipartResolver”/>
      2. 处理器需要 @MultipartConfig@MultipartConfig
        @Controller
        @RequestMapping(“/file”)
        public class FileController {}
      3. 在Spring MVC中心控制器中添加 <multipart-config/><servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>


        <multipart-config/>
        </servlet>
      4. 处理器代码 @RequestMapping(“/upload3”)
        public String fileUpload3(HttpServletRequest request) throws IOException, ServletException {

        Part part = request.getPart(“file”);
        String filename = part.getSubmittedFileName();
        System.out.println(“filename = ” + filename);

        System.out.println(“part = ” + part);
        // InputStream inputStream = part.getInputStream();

        part.write(“E:\\3051142026\\user.jpg”);
        return “redirect:/index.jsp”;
        }

12.3 文件下载

文件下载步骤:

  1. 设置 response 响应头
  2. 读取文件 — InputStream
  3. 写出文件 — OutputStream
  4. 执行操作
  5. 关闭流 (先开后关)

代码实现:

@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
// 要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "基础语法.jpg";

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

之后访问该处理器就会下载文件。

发表回复

相关

浙ICP备2021031744号-3