zoukankan      html  css  js  c++  java
  • Spring MVC笔记

    SpringMVC

    lWYj2T.png

    重点:SpringMVC的执行流程!(和IOC、AOP同等重要)

    SpringMVC Vue SpringBoot SpringCloud Linux

    JSP:本质就是一个Servlet

    假设:你项目的架构,是设计好的,还是演进的?

    • Alibaba 原先使用PHP
    • 随着用户访问量增大 后来使用Java
    • 王坚 去 IOE 化
    • MySQL,开源小巧,可把插化,组件式,AliSQL、AliRedis
    • All in one(单击) ---> ... ---> 微服务

    1、什么是SpringMVC

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

    官方文档

    4.3.24版

    我们为什么要学习SpringMVC呢?

    SpringMVC的特点:

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

    1.1、中心控制器

    Spring的web框架围绕DispatchServlet设计!

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

    lD16Ts.md.png

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

    Spring MVC的原理如图所示:

    ​ 当发起请求时,被前置控制器(Front Controller)拦截,根据请求参数生成代理请求(Delegate request),找到请求对应的实际控制器(Controller),控制器处理请求(Handle request),创建数据模型(Create model),访问数据库,将模型响应给中心控制器,控制器将携带了数据的ModelAndView返回给前置控制器,再由前置控制器找到具体的视图,填充数据,将结果返回给前置控制器,最后将结果返回给请求者!

    lD8ib4.png

    1.2、SpringMVC执行原理

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

    lDDNz4.md.png

    SpringMVC的执行流程十分重要!和Spring的IOC、AOP等同地位

    简要分析执行流程:

    1. DispatchServlet:表示前置控制器,是整个SpringMVC的控制中兴。用户发出请求,DispathcServlet接受请求并拦截请求。

      • 我们假设请求的url为:http://localhost:8080/SpringMVC/hello

      • 如上url我们拆成三部分:

      • http:localhost:8080:服务器域名

      • SpringMVC:部署在服务器上的web站点

      • hello:表示控制器

      • 通过以上分析,如上url表示为,请求位于服务器为localhost:8080上的SpringMVC站点的hello控制器。

    2. HandlerMapping:处理器映射器。DispatchServlet调用。HandlerMapping根据请求url查找Handler

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

    4. HandlerExecution将解析后的信息传递给DispatchServlet,如解析控制器映射等。

    5. HandlerAdapter:处理器适配器,其按照特定的规则去执行Handler

    6. Handler让具体的Controller去执行。

    7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView

    8. HandlerAdapter将视图逻辑名或者模型传递给DispatchServlet

    9. DispatchServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图。

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

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

    12. 最终视图呈现给用户。

    2、HelloSpringMVC

    2.1、配置版

    1. 先新建一个Moudle,再添加web的支持!

      lWUOPI.png

    2. 确定导入了SpringMVC的依赖!

      <!--导入依赖-->
      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-webmvc</artifactId>
              <version>5.2.2.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>servlet-api</artifactId>
              <version>2.5</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet.jsp</groupId>
              <artifactId>jsp-api</artifactId>
              <version>2.2</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>jstl</artifactId>
              <version>1.2</version>
          </dependency>
      </dependencies>
      
    3. 配置web.xml,注册DispatchServlet

    <?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">
    
        <!--配置 DispatchServlet,这是SpringMVC的核心,请求分发器 前端控制器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--DispatchServlet绑定Spring的配置文件-->
            <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中 /和/*的区别
            /:匹配所有的请求,不会去匹配jsp页面
            /*:匹配所有的请求,包括jsp页面
        -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
    1. 编写SpringMVC的配置文件!(也就是Spring的配置文件)名称为:springmvc-servlet.xml:[servletname]-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.xsd">
      
      </beans>
      
    2. 添加 处理器映射器

      <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
      
    3. 添加 处理器适配器

      <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
      
    4. 添加 视图解析器

      <!--视图解析器:DispatcherServlet给他的ModelAndView-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
          <!--前缀-->
          <property name="prefix" value="/WEB-INF/jsp/"/>
          <!--后缀-->
          <property name="suffix" value=".jsp"/>
      </bean>
      
    5. 编写我们要操作业务Controller,要么实现Controller接口,要么增加一个注解;需要返回一个ModelAndView

      package com.rainszj.controller;
      
      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * 注意:这里我们先导入Contorller的接口
       */
      public class HelloController implements Controller {
      
          public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
              // 模型和视图 ModelAndView
              ModelAndView mv = new ModelAndView();
      
              // 调用业务逻辑
              mv.addObject("msg", "HelloSpringMVC");
              // 转发视图
              mv.setViewName("test"); // /WEB-INF/jsp/test.jsp
      
              return mv;
          }
      }
      
    6. 将自己的类交给SpringIOC容器,注册bean

      <!--Handler-->
      <bean id="/test" class="com.rainszj.controller.HelloController"/>
      
    7. 写要跳转的jsp页面,显示ModelandView存放的数据,以及我们的正常页面;

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>rainszj</title>
      </head>
      <body>
          ${msg}
      </body>
      </html>
      
    8. 配置tomcat 9 ,启动测试

    【注意事项】

    可能遇到的问题:访问出现404,排查步骤:

    1. 查看控制台输出,看一下是不是缺少了什么jar包。
    2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!
    3. 重启Tomcat 即可解决!

    具体添加方法,打开项目目录结构:

    lWUZHH.md.png

    2.2、注解版

    1. 新建一个Maven的Moudle,添加web的支持!

    2. 由于Maven可能存在资源过滤的问题,在pom.xml中添加如下:

      <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-webmvc、junit、servlet-api、jsp-api、jstl
      
    4. 配置web.xml

      • 注册DispatcherServlet
      • 关联SpringMVC的配置文件
      • 设置启动级别为:1
      • Servlet映射路径为:/【用 /*,会出现 404】
      <?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">
      
          <!--配置 DispatchServlet,这是SpringMVC的核心,请求分发器 前端控制器-->
          <servlet>
              <servlet-name>springmvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!--DispatchServlet绑定Spring的配置文件-->
              <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中 /和/*的区别
              /:匹配所有的请求,不会去匹配jsp页面
              /*:匹配所有的请求,包括jsp页面
          -->
          <servlet-mapping>
              <servlet-name>springmvc</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      
      
      </web-app>
      
    5. 添加springmvc的配置文件

      • 让IOC的注解生效
      • 静态资源过滤:html、js、css、图片、视频、音频
      • MVC的注解驱动
      • 配置视图解析器
      • 在resource目录下添加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:mvc="http://www.springframework.org/schema/mvc"
             xmlns:context="http://www.springframework.org/schema/context"
             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.rainszj.controller"/>
          <!--让SpringMVC不处理静态资源-->
          <mvc:default-servlet-handler/>
          <!--
         支持mvc注解驱动
             在spring中一般采用@RequestMapping注解来完成映射关系
             要想使@RequestMapping注解生效
             必须向上下文中注册DefaultAnnotationHandlerMapping
             和一个AnnotationMethodHandlerAdapter实例
             这两个实例分别在类级别和方法级别处理。
             而annotation-driven配置帮助我们自动完成上述两个实例的注入。
          -->
          <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>
      

      ​ 在视图解析器中,我们把所有的视图都存放在:/WEB-INF/ 目录下,保证视图安全,在这个目录下,客户端不能直接访问。

    6. 创建Controller

      • 编写一个Java控制类:com.rainszj.controller.HelloController
      package com.rainszj.controller;
      
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      
      @Controller
      public class HelloController {
      
          @RequestMapping("/h1")
          public String hello(Model model) {
              // 封装数据
              model.addAttribute("msg", "hello,SpringMVCAnnotation!");
      
              // 返回结果会被视图解析器解析
              return "hello";
          }
      }
      
      • @Contorller:为了让Spring IOC容器初始化时,自动扫描到该类

      • @RequestMapping:映射请求路径。类与方法上都可以映射

      • hello 方法中声明的 Model 类型的参数是为了把 Action 中的数据带到视图中;

      • 方法返回的结果是视图的名称,加上视图解析器的拼接,为:WEB-INF/jsp/hello.jsp

    7. 创建视图层

      在WEB-INF/jsp,目录下创建hello.jsp,通过EL表达式取出Model中存放的值,或者对象;

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>SpringMVC</title>
      </head>
      <body>
          ${msg}
      </body>
      </html>
      
    8. 配置Tomcat,测试

      lW0kid.md.png

    小结:

    1. 新建一个Module,添加web支持,成为web项目
    2. 导入相关jar包
    3. 编写web.xml,注册DispatchServlet类
    4. 编写SpringMVC的配置文件
    5. 创建Contorller类
    6. 完善前端视图和Contoller之间的对应
    7. 测试运行即可。

    使用springMVC必须配置的三大件:

    处理器映射器、处理器适配器、视图解析器

    通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

    下面三个讲的都是围绕注解版说明的:

    • @RequestMapping
    • Model
    • return

    3、Controller和RestFul风格

    3.1、控制器Controller

    • 控制器负责提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
    • 控制器负责解析用户的请求并将其转换为一个模型。
    • 在SpringMVC中,使用注解方式,一个控制器类中可以包含多个方法。

    3.2、实现Controller接口

    Controller是一个接口,在 org.springframework.web.servlet.mvc 包下,接口中只有一个方法。

    @FunctionalInterface
    public interface Controller {
    	@Nullable
    	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
    
    }
    

    测试:

    1. 新建一个Module,添加web支持

    2. 在web.xml中,注册DiapatchServlet

    3. 在SpringMVC配置文件中配置视图解析器

      <!--配置视图解析器-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/jsp/"/>
          <property name="suffix" value=".jsp"/>
      </bean>
      
    4. 编写一个类,实现Controller接口

      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * 第一种方式,实现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;
          }
      }
      
    5. 在SpringMVC配置文件中,注册刚才的类【注意 id】

      <!--使用实现Controller接口的方式,需注册实现该接口的类的Bean,id为访问的控制器名称-->
      <bean id="/t1" class="com.rainszj.controller.ControllerTest1"/>
      
    6. 在WEB-INF/jsp目录下,编写前端 jsp页面,与视图解析器中对应

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      <form action="/c1/t1/1/2" method="post">
          <input type="submit">
      </form>
      
      </body>
      </html>
      
    7. 使用Tomcat测试最终结果

    l5rpo6.png

    • 使用Controller接口的方式定义控制器是较老的方法
    • 缺点:一个控制器中只能有一个方法,如果要定义多个方法,则需要定义多个实现类Controller接口的类

    3.3、使用注解@Controller

    • @Controller注解:用于声明Spring类的实例是一个控制器(此外,还有@Repository,@Service,@Component)

    • Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器类,需要在Spring的配置文件中,添加:context约束下的组件扫描 component-scan

      <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
      <context:component-scan base-package="com.rainszj.controller"/>
      
    • 编写一个使用@Controller注解的类

      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      
      /**
       * 第二种方式,使用Controller注解
       */
      @Controller
      public class ControllerTest2 {
      
          @RequestMapping("/test2")
          public String test1(Model model) {
      
              model.addAttribute("msg", "ControllerTest2");
              return "test";
      
          }
      }
      
    • 测试结果

      l5skNV.png

    ​ 在以上例子中,我们的两个请求都指向了同一个视图(同一个jsp页面),但是页面的结果是不一样的,可以看出视图是被复用的,而控制器与视图之间的关系是弱耦合的。

    ​ 注解是平时使用最多的,此外还有其它方式。

    3.4、RequestMapping

    • @RequestMapping 注解用于映射url到控制器类或一个特定的处理程序方法,可用于类或方法上。用于类上,表示类上所有响应请求的方法都是以改地址作为父路径!

    3.5、RestFul风格

    • RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这种风格,可以更加简洁(通过/分隔)、高效(缓存)、安全(传值不传参)。

    • 资源:互联网上的所有的事物都可以被抽象为资源。

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

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

    @Controller
    public class ControllerTest3 {
    
        // 传统的:      http://localhost:8080/c1/t1?a=1&b=2
        // RestFul风格: http://localhost:8080/c1/t1/12/12
        @RequestMapping(path = "c1/t1/{a}/{b}", method = RequestMethod.POST)
        public String test1(@PathVariable int a, @PathVariable int b, Model model) {
            int res = a + b;
            model.addAttribute("msg", "结果1为:"+res);
    
            return "test";
        }
    
        // 映射访问路径
        @GetMapping("c1/t2/{a}/{b}")
        public String test2(@PathVariable int a, @PathVariable String b, Model model) {
            String res = a + b;
    
            // Spring MVC 会自动实例化一个Model对象,用于向视图中传值
            model.addAttribute("msg", "结果2为:"+res);
    
            // 返回视图位置
            return "test";
        }
    }
    

    小结:

    • 所有地址栏请求默认都会是HTTP GET 类型的

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

    • 组合注解有:

      @GetMapping
      @PostMapping
      @PutMapping
      @DeleteMapping
      @PatchMapping
      
    • @GetMapping是一个组合注解,它等价于:@RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

    4、结果跳转方式

    4.1、ServletAPI

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

    1. 通过HttpServletRequest,实现请求转发
    2. 通过HttpServletResponse,实现重定向和页面内容输出
    @Controller
    public class ModelTest1 {
    
        // 向页面输出内容
        @RequestMapping("/m1/t2")
        public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.getWriter().println("Hello,HttpServletResponse!");
        }
    
        // 重定向
        @RequestMapping("/m1/t3")
        public void test3(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
            response.sendRedirect("/index.jsp");
        }
    
        // 请求转发
        @RequestMapping("/m1/t4")
        public void test4(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // request.getSession().setAttribute("msg", "redirect");
            request.setAttribute("msg", "request域");
    
            request.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(request, response);
        }
    }
    

    4.2、SpringMVC

    通过SpringMVC来实现转发和重定向 - 无需视图解析器;

    @Controller
    public class ModelTest2 {
    
        @RequestMapping("/m2/t1")
        public String test1(Model model) {
    
            model.addAttribute("msg", "通过SpringMVC,无需视图解析器!");
    
            // 请求转发,默认
            return "/WEB-INF/jsp/test.jsp";
        }
    
        @RequestMapping("/m2/t2")
        public String test2(Model model) {
    
            model.addAttribute("msg", "转发方式二!");
    
            // 请求转发方式二
            return "forward:/WEB-INF/jsp/test.jsp";
        }
    
        @RequestMapping("/m2/t3")
        public String test3() {
    
            // 重定向
            return "redirect:/index.jsp";
        }
    }
    

    通过SpringMVC来实现转发和重定向 - 有视图解析器;

    重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.

    @Controller
    public class ModelTest3 {
    
        @RequestMapping("m3/t1")
        public String test1(Model model) {
    
            model.addAttribute("msg", "通过SpringMVC,有视图解析器!");
    
            return "test";
        }
    
        @RequestMapping("/m3/t2")
        public String test2() {
    
            return "redirect:/index.jsp";
        }
    }
    

    5、数据处理

    5.1、获取前端提交的数据

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        // 1、提交的域名称和处理方法的参数名一致
        // http://localhost:8080/user/t1?name=zhangsan
        @RequestMapping("/t1")
        public String test1(String name, Model model) {
    
            // 1.接收前端参数
            System.out.println("接收到的前端参数为:" + name);
            // 2.业务代码
            model.addAttribute("msg", name);
            // 3.视图跳转
            return "test";
        }
    
        // 2、提交的域名称和处理方法的参数名不一致
        // http://localhost:8080/user/t2?username=zhangsan
        @RequestMapping("/t2")
        public String test2(@RequestParam("username") String name, Model model) {
            // 1.接收前端参数
            System.out.println("接收到的前端参数为:" + name);
            // 2.业务代码
            model.addAttribute("msg", name);
    
            return "test";
        }
    
        // 3.提交的是一个对象
        // http://localhost:8080/user/t3?name=zhangsan&id=1&age=20
        @RequestMapping("/t3")
        public String test3(User user) {
            System.out.println(user);
            return "test";
        }
    
        // RestFul风格
        // http://localhost:8080/user/t4/zhangsan
        @RequestMapping("/t4/{name}")
        public String test4(@PathVariable String name) {
    
            System.out.println("使用RestFu风格接收的前端参数为:" + name);
    
            return "test";
        }
    }
    

    说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。

    5.2、将数据显示到前端

    三种方式:

    1. 通过ModelAndView

    2. 通过Model

    3. 通过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";
      }
      

      从ModelMap源码可以看出,ModelMap 继承自 LinkedHashMap

      public class ModelMap extends LinkedHashMap<String, Object> {
      
      }
      

    区别:

    • Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

    • ModelMap 继承了 LinkedHashMap ,除了实现了自身的一些方法,同样的继承 LinkedHashMap 的方法和特性;

    • ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

    当然更多的以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。

    5.3、乱码问题

    1. 可以通过实现Servlet包下的接口Filter,来自定义过滤器,然后在web.xml中注册Filter

      public class EncodingFilter implements Filter {
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("==========================");
      
              HttpServletRequest request = (HttpServletRequest) servletRequest;
              request.setCharacterEncoding("utf-8");
      
              HttpServletResponse response = (HttpServletResponse) servletResponse;
              response.setCharacterEncoding("utf-8");
      
              // 放行
              filterChain.doFilter(request, response);
      
          }
      
          public void destroy() {
      
          }
      }
      
    2. 在web.xml中,使用Spring MVC 自带的过滤器,重启Tomcat服务器

          <!--使用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>
      

    有些极端情况下.这个过滤器对get的支持不好 .

    解决方案:

    1. 修改Tomcat配置文件:设置编码!

      <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
                 connectionTimeout="20000"
                 redirectPort="8443" />
      
    2. 自定义过滤器(终极大招!!!),别忘了,在web.xml中注册!

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

    ​ 这是在网上找的一些大神写的,一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!

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

    6、Json在Spring MVC中的使用

    • Jackson是Spring MVC内置的解析Json工具
    • 阿里的FastJson

    6.1、jackson

    环境搭建:

    1. 导入jar包

      <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.10.1</version>
      </dependency>
      
    2. 在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>
      
          <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>
      
    3. Spring MVC的配置文件

      <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
              http://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.kuang.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>
      
    4. User实体类

      // 此处需要导入lombok的jar包
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
          private String name;
          private int age;
          private String sex;
      }
      
    5. 编写一个Controller控制器,进行测试!

      /**
       * @Controller + @ResponseBody:改方法都只会返回 json 字符串了,不走视图解析器
       * @RestController: 该类上返回值为String的方法直接返回 json 字符串,不走视图解析器
       */
      // @Controller
      @RestController
      public class UserController {
      
          @RequestMapping("/j5")
          public String json5() throws JsonProcessingException {
              User user = new User("张三", 20, "男");
      
              ObjectMapper mapper = new ObjectMapper();
      
              String json_user = mapper.writeValueAsString(user);
      
              return json_user;
          }
      }
      

    抽取成JsonUtils工具类:

    /**
     * Json工具类,jackson版
     */
    public class JsonUtils {
    
        /**
         * @param object 要转成Json的对象
         * @return
         */
        public static String getJson(Object object) {
            return getJson(object, "yyyy-MM-dd HH-mm-ss");
        }
    
        /**
         * @param object     要转成Json的对象
         * @param dateFormat 自定义日期格式
         * @return
         */
        public static String getJson(Object object, String dateFormat) {
            ObjectMapper mapper = new ObjectMapper();
            // 关闭默认时间戳格式
            mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
            // 自定义日期格式
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            // 设置日期格式
            mapper.setDateFormat(sdf);
    
            try {
                return mapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    

    ObjectMapper 源码中 configure方法:

    lIY9Qx.md.png

    SerializationFeature 源码中:

    lIYpS1.md.png

    使用举例:

    /**
     * 普通java对象
     */
    // @ResponseBody
    @RequestMapping(value = "/j1", produces = "application/json;charset=utf-8")
    public String json1() throws JsonProcessingException {
        User user = new User("张三", 20, "男");
    
        return JsonUtils.getJson(user);
    }
    
    /**
     * java数组对象
     */
    @RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        User user1 = new User("张三", 20, "男");
        User user2 = new User("王五", 21, "男");
        User user3 = new User("赵六", 22, "男");
    
        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
    
        return JsonUtils.getJson(userList);
    }
    
    /**
     * 时间戳类型,在ObjectMapper层面解决日期格式化问题
     */
    @RequestMapping("/j4")
    public String json4() throws JsonProcessingException {
    
        Date date = new Date();
    
        return JsonUtils.getJson(date, "yyyy-MM-dd HH-mm-ss");
    }
    

    6.2、fastjson

    ​ fastjson是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。

    fastjson 的 pom依赖!

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.60</version>
    </dependency>
    

    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字符串之间的相互转化。

    代码测试,我们新建一个FastJsonDemo 类

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.kuang.pojo.User;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class FastJsonDemo {
        public static void main(String[] args) {
            //创建一个对象
            User user1 = new User("张三", 20, "男");
            User user2 = new User("王五", 21, "男");
            User user3 = new User("赵六", 22, "男");
    
            List<User> list = new ArrayList<User>();
            list.add(user1);
            list.add(user2);
            list.add(user3);
    
            System.out.println("*******Java对象 转 JSON字符串*******");
            String str1 = JSON.toJSONString(list);
            System.out.println("JSON.toJSONString(list)==>"+str1);
            String str2 = JSON.toJSONString(user1);
            System.out.println("JSON.toJSONString(user1)==>"+str2);
    
            System.out.println("
    ****** JSON字符串 转 Java对象*******");
            User jp_user1=JSON.parseObject(str2,User.class);
            System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
    
            System.out.println("
    ****** Java对象 转 JSON对象 ******");
            JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
            System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
    
            System.out.println("
    ****** JSON对象 转 Java对象 ******");
            User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
            System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
        }
    }
    

    这种工具类,我们只需要掌握使用就好了,在使用的时候在根据具体的业务去找对应的实现。和以前的commons-io那种工具包一样,拿来用就好了!

    6.3、Json乱码解决

    第一种方式,逐个解决:

    通过以上测试,发现出现了乱码问题,我们需要设置一下他的编码格式为utf-8,以及它返回的类型;

    通过@RequestMaping的produces属性来实现,修改下代码

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

    第二种方式,乱码统一解决:

    我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置!

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

    7、Ajax技术

    HTML + CSS:略懂 + JS(超级熟练)

    js:

    • 函数:闭包 ()()
    • Dom
      • id、name、tag
      • create、remove
    • Bom
      • window
      • document

    ES6:import,required

    8、拦截器

    8.1、概述

    Spring MVC 的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

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

    过滤器

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

    拦截器

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

    总结:

    1. 从拦截级别上来说,SpringMVC是方法级别的拦截,而 struts2是类级别的拦截。
    2. 数据独立性:SpringMVC是方法间独立,独享 request 和 response,而 struts2 虽然方法也是独立,但是所有的 action 变量是共享的
    3. 拦截机制:SpringMVC用的是独立的aop方式,struts2有自己的 interceptor机制,所以 struts2 的配置文件量要大于 SpringMVC

    8.2、自定义拦截器

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

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

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

    3. 编写一个拦截器

      package com.rainszj.config;
      
      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 {
      
          // 在请求处理的方法之前执行
          // return true; 执行下一个拦截器,代表放行
          // return false; 代表拦截
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      
              System.out.println("=============拦截前=============");
              return true;
          }
      
          // 在请求处理方法执行之后执行
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("=============拦截后=============");
      
          }
      
          // 在dispatcherServlet处理后执行,做清理工作
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("=============清理=============");
      
          }
      }
      
      
    4. 在springmvc的配置文件中配置拦截器

          <!--拦截器配置-->
          <mvc:interceptors>
              <mvc:interceptor>
                  <!--/** 包括路径及其子路径-->
                  <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
                  <!--/admin/** 拦截的是/admin/下的所有-->
                  <mvc:mapping path="/**"/>
                  <!--bean配置的就是拦截器-->
                  <bean class="com.rainszj.config.MyInterceptor"/>
              </mvc:interceptor>
          </mvc:interceptors>
      
    5. 编写一个Controller,接收请求

      package com.rainszj.controller;
      
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class TestController {
      
          @RequestMapping("/t1")
          public String test1() {
      
              System.out.println("TestController:test1=>执行了");
              return "hello";
          }
      
      }
      
    6. 启动tomcat,测试一下

    lz0LUf.png

    lz0qVP.png

    8.3、判断用户是否登录案例

    实现思路

    1. 有一个登陆页面,需要写一个controller访问页面。
    2. 登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
    3. 拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面
    • 编写一个登录页面 login.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>登录页面</title>
      </head>
      <body>
      
      <h1>登录页面</h1>
      
      <form action="${pageContext.request.contextPath}/user/login" method="post">
          <label>用户名:</label>
          <input type="text" name="username" required>
      
          <label>密码:</label>
          <input type="text" name="password" required>
          <span><input type="submit" value="提交"></span>
      </form>
      
      </body>
      </html>
      
    • 编写一个登录成功页面 main.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>登录成功</title>
      </head>
      <body>
      
      <h2>登录成功了!</h2>
      
      <div>欢迎您,${userLoginInfo}</div>
      
      <div><a href="${pageContext.request.contextPath}/user/goOut">注销</a></div>
      
      
      </body>
      </html>
      
    • 在 index 页面上测试跳转!启动Tomcat 测试,未登录也可以进入主页!

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
        <head>
          <title>$Title$</title>
        </head>
        <body>
      
        <a href="${pageContext.request.contextPath}/user/tologin">登录</a>
        <a href="${pageContext.request.contextPath}/user/tomain">首页</a>
      
        </body>
      </html>
      
    • 编写一个Controller处理请求

      package com.rainszj.controller;
      
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      
      import javax.servlet.http.HttpSession;
      
      @Controller
      @RequestMapping("/user")
      public class LoginController {
      
          // 跳转到登录页面
          @RequestMapping("/tologin")
          public String toLogin() {
      
              return "login";
          }
      
          // 跳转到首页
          @RequestMapping("/tomain")
          public String toMain() {
              return "main";
          }
      
          // 登录提交页面
          @RequestMapping("/login")
          public String login(HttpSession session, String username, String password) {
              System.out.println("login=>username:" + username + ",password=>" + password);
      
              if (username != null) {
                  // 用 Session记录用户信息
                  session.setAttribute("userLoginInfo", username);
              }
      
              return "main";
          }
      
          // 注销页面
          @RequestMapping("/goOut")
          public String goOut(HttpSession session) {
              // 移除Session中的信息
              session.removeAttribute("userLoginInfo");
      
              return "login";
          }
      }
      
    • 编写用户登录拦截器

      package com.rainszj.config;
      
      import org.springframework.web.servlet.HandlerInterceptor;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      
      public class LoginInterceptor implements HandlerInterceptor {
      
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      
              HttpSession session = request.getSession();
              System.out.println("LoginInterceptor:userLoginInfo=>"+session.getAttribute("userLoginInfo"));
      
              // 登录页面
              if (request.getRequestURI().contains("login")) {
                  return true;
              }
      
              // 有Session
              if (session.getAttribute("userLoginInfo") != null) {
                  return true;
              }
      
               // 用户没有登陆跳转到登陆页面
              request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
      
              return false;
          }
      }
      
      
    • 在Springmvc的配置文件中注册拦截器

      <?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:mvc="http://www.springframework.org/schema/mvc"
             xmlns:context="http://www.springframework.org/schema/context"
             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.rainszj.controller"/>
      
          <!--静态资源过滤问题-->
          <mvc:default-servlet-handler/>
          <!--注解驱动,自动创建 处理器映射器和处理器适配器-->
          <!--解决Json乱码问题-->
          <mvc:annotation-driven>
              <mvc:message-converters register-defaults="true">
                  <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                      <constructor-arg value="UTF-8"/>
                  </bean>
                  <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                      <property name="objectMapper">
                          <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                              <property name="failOnEmptyBeans" value="false"/>
                          </bean>
                      </property>
                  </bean>
              </mvc:message-converters>
          </mvc:annotation-driven>
      
          <!--视图解析器-->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <property name="suffix" value=".jsp"/>
          </bean>
      
          <!--拦截器配置-->
          <mvc:interceptors>
              <mvc:interceptor>
                  <mvc:mapping path="/user/**"/>
                  <bean class="com.rainszj.config.LoginInterceptor"/>
              </mvc:interceptor>
          </mvc:interceptors>
      
      </beans>
      
    • 重启Tomcat测试!

    9、文件的上传和下载

    9.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的组件

    9.2、文件上传

    一、导入文件上传的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>
    

    二、配置bean:multipartResolver

    注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!

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

    CommonsMultipartFile 的 常用方法:

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

    三、编写前端页面

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

    四、Controller

    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.multipart.commons.CommonsMultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    
    @Controller
    public class FileController {
        //@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";
        }
    }
    

    9.3、采用file.Transto 来保存上传的文件

    1. 编写Controller

      /*
       * 采用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";
      }
      
    2. 前端表单提交地址修改

    3. 访问提交测试,OK!

    9.4、文件下载

    文件下载步骤:

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

    前端

    <a href="/download">点击下载</a>
    
  • 相关阅读:
    Server Tomcat v8.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    用户画像——“打标签”
    python replace函数替换无效问题
    python向mysql插入数据一直报TypeError: must be real number,not str
    《亿级用户下的新浪微博平台架构》读后感
    【2-10】标准 2 维表问题
    【2-8】集合划分问题(给定要分成几个集合)
    【2-7】集合划分问题
    【2-6】排列的字典序问题
    【2-5】有重复元素的排列问题
  • 原文地址:https://www.cnblogs.com/rainszj/p/12187465.html
Copyright © 2011-2022 走看看