SpringMVC入门学习

105 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

第三章 SpringMVC

3.1、基本概述

  1. 三层架构

    1. 咱们开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序

    2. 使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构

    3. 三层架构

      1. 表现层:WEB层,用来和客户端进行数据交互的。表现层一般会采用MVC的设计模型
      2. 业务层:处理公司具体的业务逻辑的
      3. 持久层:用来操作数据库的
  2. MVC模型

    1. MVC全名是Model View Controller 模型视图控制器,每个部分各司其职。
    2. Model:数据模型,JavaBean的类,用来进行数据封装。
    3. View:指JSP、HTML用来展示数据给用户
    4. Controller:用来接收用户的请求,整个流程的控制器。用来进行数据校验等。

3.2、SpringMVC的入门

搭建环境

  1. 创建web工程,导入开发的jar包

image-20210808224126733

坐标导入

<!-- 版本锁定 -->
<properties>
    <spring.version>5.0.2.RELEASE</spring.version>
</properties><dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
​
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
​
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
​
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
​
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
  1. 配置核心的控制器(配置DispatcherServlet)

web.xml中

<!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>
​
  <!--配置前端控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  1. resources目录下创建springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
​
    <!--开启注解扫描-->
    <context:component-scan base-package="com.ywb"></context:component-scan>
​
    <!--配置视图解析对象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
​
    <!--开启SpringMVC框架注解的支持-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>
  1. 编写index.jsp和HelloController控制器类

    1. index.jsp

      <body>
          <h3>入门案例</h3>
          <a href="${ pageContext.request.contextPath }/hello">入门案例</a>
      </body>
      
    2. HelloController

      /**
       * @author yang
       * @date 2021年08月08日 23:21
       * 控制器的类
       */
      @Controller
      public class HelloController {
      ​
          /**
           * 接收请求
           * @return
           */
          @RequestMapping(path = "/hello")
          public String sayHello(){
              System.out.println("hello SpringMVC");
              return "success";
          }
      ​
      }
      
  2. 在WEB-INF目录下创建pages文件夹,编写success.jsp的成功页面

    <body>
        <h3>入门成功!!</h3>
    </body>
    
  3. 启动tomcat服务器,进行测试

过程分析

  1. 入门案例的执行流程

    1. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象, 就会加载springmvc.xml配置文件
    2. 开启了注解扫描,那么HelloController对象就会被创建
    3. 从index.jsp发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解 找到执行的具体方法
    4. 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的JSP文件
    5. Tomcat服务器渲染页面,做出响应
  2. 入门案例中的组件分析

    1. 前端控制器(DispatcherServlet)
    2. 处理器映射器(HandlerMapping)
    3. 处理器(Handler)
    4. 处理器适配器(HandlAdapter)
    5. 视图解析器(View Resolver)
    6. 视图(View)

3.3、RequestMapping注解详解

  1. RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系

  2. RequestMapping注解可以作用在方法和类上

    1. 作用在类上:第一级的访问目录
    2. 作用在方法上:第二级的访问目录
    3. 细节:路径可以不编写 / 表示应用的根目录开始
    4. ${ pageContext.request.contextPath }也可以省略不写,但是路径上不能写 /
  3. RequestMapping的属性

    1. path 指定请求路径的url
    2. value value属性和path属性是一样的
    3. method 指定该方法的请求方式
    4. params 指定限制请求参数的条件
    5. headers 发送的请求中必须包含的请求头

3.4、请求参数的绑定

  1. 请求参数的绑定说明

    1. 绑定机制

      1. 表单提交的数据都是k=v格式的 username=root&password=1234
      2. SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
      3. 要求:提交表单的name和参数的名称是相同的
    2. 支持的数据类型

      1. 基本数据类型和字符串类型
      2. 实体类型(JavaBean)
      3. 集合数据类型(List、map集合等)
  2. 基本数据类型和字符串类型

    1. 提交表单的name和参数的名称是相同的
    2. 区分大小写
  3. 实体类型(JavaBean)

    1. 提交表单的name和JavaBean中的属性名称需要一致
    2. 如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 例如: address.name
  4. 给集合属性数据封装

    1. JSP页面编写方式:list[0].属性
  5. 请求参数中文乱码的解决

    1. 在web.xml中配置Spring提供的过滤器类

      <!--配置解决中文乱码的过滤器-->
        <filter>
          <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>
      

实体类型(JavaBean)代码示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
​
    <%--请求参数绑定--%>
​
    <%--
    <a href="param/testParam?username=hehe&password=123">请求参数绑定</a>
    --%>
​
    <%--请求参数绑定封装到javaBean中
        把数据封装到Account类中,Account类中有基本类型和User引用类型
    --%>
    <form action="param/saveAccount" method="post">
        姓名:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        金额:<input type="text" name="money"><br>
        用户姓名:<input type="text" name="user.uname"><br>
        用户年龄:<input type="text" name="user.age"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

ParamController控制器类:

/**
 * @author yang
 * @date 2021年08月09日 16:23
 * 请求参数绑定
 */
@Controller
@RequestMapping("/param")
public class ParamController {
​
    /**
     * 请求参数绑定入门
     * @return
     */
    @RequestMapping("/testParam")
    public String testParam(String username,String password){
        System.out.println("testPram执行了。。。");
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        return "success";
    }
​
​
    /**
     * 请求参数绑定封装到JavaBean中
     * @return
     */
    @RequestMapping("/saveAccount")
    public String saveAccount(Account account){
        System.out.println("saveAccount执行了。。。");
        System.out.println(account);
        return "success";
    }
}

集合类型代码示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
​
    <%--请求参数绑定封装到javaBean中
        Account类型中有List集合和Map集合
    --%>
    <form action="param/saveAccount" method="post">
        姓名:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        金额:<input type="text" name="money"><br>
​
        用户姓名:<input type="text" name="users[0].uname"><br>
        用户年龄:<input type="text" name="users[0].age"><br>
​
        用户姓名:<input type="text" name="userMap['one'].uname"><br>
        用户年龄:<input type="text" name="userMap['one'].age"><br>
        <input type="submit" value="提交">
    </form></body>
</html>

Account类:

public class Account implements Serializable {
​
    private String username;
    private String password;
    private Double money;
​
    private List<User> users;
    private Map<String,User> userMap;
}
  1. 自定义类型转换器

    1. 表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明 Spring框架内部会默认进行数据类型转换。

    2. 如果想自定义数据类型转换,可以实现Converter的接口

      1. 自定义类型转换器

        /**
         * @author yang
         * @date 2021年08月10日 15:56
         * 自定义类型转换器
         */
        public class StringToDateConverter implements Converter<String,Date> {
        ​
            @Override
            public Date convert(String s) {
                if (s==null){
                    throw new RuntimeException("请您传入数据");
                }
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        ​
                try {
                    //把字符串转换为日期
                    return df.parse(s);
                }catch (Exception e){
                    throw new RuntimeException("数据类型转换出现错误");
                }
            }
        }
        
      2. 注册自定义类型转换器,在springmvc.xml配置文件中编写配置

        <!--配置自定义类型转换器-->
        <bean id="conversionService2" class="org.springframework.context.support.ConversionServiceFactoryBean">
            <property name="converters">
                <set>
                    <bean class="com.ywb.utils.StringToDateConverter"></bean>
                </set>
            </property>
        </bean><!--开启SpringMVC框架注解的支持-->
        <mvc:annotation-driven conversion-service="conversionService2"></mvc:annotation-driven>
        
  2. 在控制器中使用原生的ServletAPI对象

    1. 只需要在控制器的方法参数定义HttpServletRequest和HttpServletResponse对象

3.5、常用的注解

  1. RequestParam注解

    1. 作用:把请求中的指定名称的参数传递给控制器中的形参赋值

    2. 属性

      1. value:请求参数中的名称
      2. required:请求参数中是否必须提供此参数,默认值是true,必须提供
    3. 代码如下

      /**
       * 测试RequestParam注解
       * @return
       */
      @RequestMapping("/testRequestParam")
      public String testRequestParam(@RequestParam(name = "name") String username){
          System.out.println("testRequestParam执行了。。。");
          System.out.println(username);
          return "success";
      }
      
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      ​
          <a href="anno/testRequestParam?name=哈哈">RequestParam</a></body>
      </html>
      
  2. RequestBody注解

    1. 作用:用于获取请求体的内容(注意:get方法不可以)

    2. 属性

      1. required:是否必须有请求体,默认值是true
    3. 注意点:在表单中需要加入enctype="text/plain",否则会中文乱码

    4. 代码如下

      /**
       * 测试RequestBody注解
       * @return
       */
      @RequestMapping("/testRequestBody")
      public String testRequestBody(@RequestBody String body){
          System.out.println("testRequestBody执行了。。。");
          System.out.println(body);
          return "success";
      }
      
      <form action="anno/testRequestBody" method="post" enctype="text/plain">
          用户姓名:<input type="text" name="username"><br>
          用户年龄:<input type="text" name="age"><br>
          <input type="submit" value="提交">
      </form>
      
  3. PathVariable注解

    1. 作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符

    2. 属性

      1. value:指定url中的占位符名称
    3. Restful风格的URL

      1. 请求路径一样,可以根据不同的请求方式去执行后台的不同方法

      2. restful风格的URL优点

        1. 结构清晰
        2. 符合标准
        3. 易于理解
        4. 扩展方便
    4. 代码如下

      /**
       * 测试PathVariable注解
       * @return
       */
      @RequestMapping("/testPathVariable/{sid}")
      public String testPathVariable(@PathVariable(value = "sid") String id){
          System.out.println("testPathVariable执行了。。。");
          System.out.println(id);
          return "success";
      }
      
      <a href="anno/testPathVariable/10">PathVariable</a>
      
  4. RequestHeader注解

    1. 作用:获取指定请求头的值

    2. 属性

      1. value:请求头的名称
    3. 代码如下

      /**
       * 测试RequestHeader注解
       * @return
       */
      @RequestMapping("/testRequestHeader")
      public String testRequestHeader(@RequestHeader("Accept") String header){
          System.out.println("testRequestHeader执行了。。。");
          System.out.println(header);
          return "success";
      }
      
      <a href="anno/testRequestHeader">RequestHeader</a>
      
  5. CookieValue注解

    1. 作用:用于获取指定cookie的名称的值

    2. 属性

      1. value:cookie的名称
    3. 代码如下

      /**
       * 测试CookieValue注解
       * @return
       */
      @RequestMapping("/testCookieValue")
      public String testCookieValue(@CookieValue(value = "JSESSIONID") String cookieValue){
          System.out.println("testCookieValue执行了。。。");
          System.out.println(cookieValue);
          return "success";
      }
      
      <a href="anno/testCookieValue">CookieValue</a>
      
  6. ModelAttribute注解

    1. 作用

      1. 出现在方法上:表示当前方法会在控制器方法执行前先执行。
      2. 出现在参数上:获取指定的数据给参数赋值。
    2. 应用场景

      1. 当提交表单数据不是完整的实体数据时,保证没有提交的字段使用数据库原来的数据。
    3. 具体的代码

      1. 修饰的方法有返回值

        /**
         * 测试ModelAttribute注解
         * @return
         */
        @RequestMapping("/testModelAttribute")
        public String testModelAttribute(User user){
            System.out.println("testModelAttribute执行了。。。");
            System.out.println(user);
            return "success";
        }
        ​
        /**
         * 该方法会先执行
         * @param uname
         * @return
         */
        @ModelAttribute
        public User showUser(String uname){
            System.out.println("showUser方法执行了。。。");
            //模拟数据库查询对象
            User user = new User();
            user.setUname(uname);
            user.setAge(20);
            user.setDate(new Date());
            return user;
        }
        
      2. 修饰的方法没有返回值

        /**
         * 测试ModelAttribute注解
         * @return
         */
        @RequestMapping("/testModelAttribute")
        public String testModelAttribute(@ModelAttribute("abc") User user){
            System.out.println("testModelAttribute执行了。。。");
            System.out.println(user);
            return "success";
        }
        ​
        /**
         * 没有返回值类型
         * @param uname
         * @return
         */
        @ModelAttribute
        public void showUser(String uname, Map<String,User> map){
            System.out.println("showUser方法执行了。。。");
            User user = new User();
            user.setUname(uname);
            user.setAge(20);
            user.setDate(new Date());
            map.put("abc",user);
        }
        
    4. SessionAttributes注解

      1. 作用:用于多次执行控制器方法间的参数共享

      2. 属性

        1. value:指定存入属性的名称
      3. 代码如下

        /**
         * @author yang
         * @date 2021年08月10日 16:27
         */
        @Controller
        @RequestMapping("/anno")
        @SessionAttributes(value = {"msg"}) //存入Session域的对象中
        public class AnnoController {
            /**
             * 向request中存入值
             * SessionAttributes注解
             * @return
             */
            @RequestMapping("/testSessionAttributes")
            public String testSessionAttributes(Model model){
                System.out.println("testSessionAttributes执行了。。。");
                model.addAttribute("msg","测试SessionAttributes");
                return "success";
            }
        ​
            /**
             * 从session中获取值
             * SessionAttributes注解
             * @return
             */
            @RequestMapping("/getSessionAttributes")
            public String getSessionAttributes(ModelMap modelMap){
                System.out.println("getSessionAttributes执行了。。。");
                String msg = (String) modelMap.get("msg");
                System.out.println("msg是:"+msg);
                return "success";
            }
        ​
            /**
             * 清除session
             * SessionAttributes注解
             * @return
             */
            @RequestMapping("/delSessionAttributes")
            public String delSessionAttributes(SessionStatus status){
                System.out.println("delSessionAttributes执行了。。。");
                status.setComplete();
                return "success";
            }
        }
        

3.6、响应数据和结果视图

3.6.1、返回值分类

  1. 返回字符串

    1. Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。

      @RequestMapping("/testString")
      public String testString(){
          System.out.println("testString方法执行了。。。");
          return "success";
      }
      
    2. 具体的应用场景

      @Controller
      @RequestMapping("/user")
      public class UserController {
      ​
      ​
          @RequestMapping("/testString")
          public String testString(Model model){
              System.out.println("testString方法执行了。。。");
              //模拟从数据库中查找数据
              User user = new User();
              user.setUsername("root");
              user.setPassword("1234");
              user.setAge(19);
              model.addAttribute("user",user);
              return "success";
          }
      }
      
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      ​
          <h3>执行成功</h3>
      ​
          ${user.username}
          ${user.password}
          ${user.age}
      </body>
      </html>
      
  2. 返回值是void

    1. 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。

      1. 默认会跳转到@RequestMapping(value="/initUpdate") initUpdate的页面。

      2. 可以使用请求转发或者重定向跳转到指定的页面

        @RequestMapping("/testVoid")
        public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("testVoid方法执行了。。。");
            //编写请求转发的程序
            //request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
        ​
            //重定向
            //response.sendRedirect(request.getContextPath()+"/index.jsp");
        ​
            //设置中文乱码
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
        ​
            //直接进行响应
            response.getWriter().print("你好");
            return;
        }
        
  3. 返回值是ModelAndView对象

    1. ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图

    2. 代码如下

      /**
       * 返回值是ModelAndView
       * @return
       */
      @RequestMapping("/testModelAndView")
      public ModelAndView testModelAndView(){
          System.out.println("testModelAndView方法执行了。。。");
          //创建ModelAndView对象
          ModelAndView mv = new ModelAndView();
          //模拟从数据库中查找数据
          User user = new User();
          user.setUsername("root");
          user.setPassword("1234");
          user.setAge(19);
          //把user对象存储到mv对象中,也会把user对象存入到request对象
          mv.addObject("user",user);
          //跳转到哪个页面
          mv.setViewName("success");
          return mv;
      }
      

3.6.2、SpringMVC框架提供的转发和重定向

  1. forward请求转发

    1. controller方法返回String类型,想进行请求转发也可以编写成

      /**
       * 使用关键字的方法进行转发或重定向
       * @return
       */
      @RequestMapping("/testForwardOrRedirect")
      public String testForwardOrRedirect(){
          System.out.println("testForwardOrRedirect方法执行了。。。");
      ​
          //请求转发
          return "forward:/WEB-INF/pages/success.jsp";
      }
      
  2. redirect重定向

    1. controller方法返回String类型,想进行重定向也可以编写成

      /**
       * 使用关键字的方法进行转发或重定向
       * @return
       */
      @RequestMapping("/testForwardOrRedirect")
      public String testForwardOrRedirect(){
          System.out.println("testForwardOrRedirect方法执行了。。。");
      ​
          //重定向
          return "redirect:/index.jsp";
          // return "redirect:/user/findAll";
      }
      

3.6.3、ResponseBody响应json数据

  1. DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而 不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置

    1. mvc:resources标签配置不过滤

      1. location元素表示webapp目录下的包下的所有文件
      2. mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b

      springmvc.xml:

      <!--告诉前端配置器,哪些静态资源不拦截-->
      <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
      <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
      <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
      
    2. 使用@RequestBody获取请求体数据

      <script>
          //页面加载,绑定单击事件
          $(function () {
              $("#btn").click(function () {
                  //alert("hello btn")
                  //发送ajax请求
                  $.ajax({
                      //编写json格式,设置属性和值
                      url:"user/testAjax",
                      contentType:"application/json;charset=UTF-8",
                      data:'{"username":"测试ajax","password":"12345","age":19}',
                      dataType:"json",
                      type:"post",
                      success:function (data) {
                          
                      }
                  })
              })
          })
      </script>
      
      @RequestMapping(value = "/testAjax")
      public void testAjax(@RequestBody String body){
          System.out.println("testAjax方法执行了。。。");
          System.out.println(body);
      }
      
    3. 使用@RequestBody注解把json的字符串转换成JavaBean的对象,并把对象返回到data

      /**
       * 模拟异步请求响应
       */
      @RequestMapping(value = "/testAjax")
      public @ResponseBody User testAjax(@RequestBody User user){
          System.out.println("testAjax方法执行了。。。");
          //客户端发送的ajax请求,传的是json字符串,spring框架把json字符串封装到user对象中
          System.out.println(user);
          //做响应,模拟查找数据库
          user.setUsername("查找的用户");
          user.setAge(30);
          //做响应
          return user;
      }
      
      <script>
          //页面加载,绑定单击事件
          $(function () {
              $("#btn").click(function () {
                  //alert("hello btn")
                  //发送ajax请求
                  $.ajax({
                      //编写json格式,设置属性和值
                      url:"user/testAjax",
                      contentType:"application/json;charset=UTF-8",
                      data:'{"username":"测试ajax","password":"12345","age":19}',
                      dataType:"json",
                      type:"post",
                      success:function (data) {
                          //data为服务器响应的json数据,进行解析
                          alert(data);
                          alert(data.username);
                          alert(data.password);
                          alert(data.age);
                      }
                  })
              })
          })
      </script>
      
    4. json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包

        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.0</version>
        </dependency>
      ​
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
          <version>2.9.0</version>
        </dependency>
      ​
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.9.0</version>
        </dependency>
      

3.7、SpringMVC实现文件上传

3.7.1、文件上传的回顾

  1. 导入文件上传的jar包

    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency><dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    
  2. 编写文件上传的JSP页面

    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    ​
        <h3>文件上传</h3>
    ​
        <form action="user/fileupload1" method="post" enctype="multipart/form-data">
            选择文件<input type="file" name="upload"><br>
            <input type="submit" value="上传">
        </form>
    </body>
    </html>
    
  3. 编写文件上传的Controller控制器

    /**
     * 文件上传
     * @return
     */
    @RequestMapping("/fileupload1")
    public String fileupload1(HttpServletRequest request) throws Exception {
        System.out.println("fileupload1执行了。。。");
        //使用fileupload组件帮助我们实现文件上传
        //上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //判断该路径是否存在
        File file = new File(path);
        if (!file.exists()){
            file.mkdirs();
        }
        //解析request对象,获取上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        //解析request
        List<FileItem> items = upload.parseRequest(request);
        //遍历
        for (FileItem item : items) {
            //判断当前item对象是否为上传文件项
            if (item.isFormField()){
                //普通表单项
                System.out.println("普通表单项");
            }else {
                //上传文件项
                System.out.println("上传文件项");
                //获取上传文件的名称
                String fileName = item.getName();
                //完成文件上传
                item.write(new File(path,fileName));
                //删除临时文件
                item.delete();
            } 
        }
        return "success";
    }
    

3.7.2、SpringMVC传统方式文件上传

  1. SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的 name属性名称相同。

  2. 配置文件解析器对象

    <!--配置文件解析器对象-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760"></property>
    </bean>
    
  3. 代码如下

    /**
     * SpringMVC文件上传
     * @return
     */
    @RequestMapping("/fileupload2")
    public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("springmvc文件上传。。。");
        //使用fileupload组件帮助我们实现文件上传
        //上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //判断该路径是否存在
        File file = new File(path);
        if (!file.exists()){
            file.mkdirs();
        }
        //获取上传文件的名称
        String filename = upload.getOriginalFilename();
        upload.transferTo(new File(path,filename));
        return "success";
    }
    

3.7.3、SpringMVC跨服务器方式文件上传

  1. 搭建图片服务器

    1. 根据文档配置tomcat8的服务器,现在是2个服务器
    2. 创建多一个项目fileuploadserver作为图片服务器
  2. 实现SpringMVC跨服务器方式文件上传

    1. 导入开发需要的jar包

      <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-core</artifactId>
        <version>1.18.1</version>
      </dependency><dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.18.1</version>
      </dependency>
      
    2. 编写文件上传的JSP页面

      <h3>跨服务器方式文件上传</h3><form action="user/fileupload3" method="post" enctype="multipart/form-data">
          选择文件<input type="file" name="upload"><br>
          <input type="submit" value="上传">
      </form>
      
    3. 编写控制器

      /**
       * 跨服务器文件上传
       * @return
       */
      @RequestMapping("/fileupload3")
      public String fileupload3(MultipartFile upload) throws Exception {
          System.out.println("跨服务器文件上传。。。");
          //定义上传文件服务器路径
          String path = "http://localhost:9090/uploads/";
          //获取上传文件的名称
          String filename = upload.getOriginalFilename();
          //创建客户端服务器
          Client client = Client.create();
          //和图片服务器进行连接
          WebResource resource = client.resource(path + filename);
          //上传文件
          resource.put(upload.getBytes());
          return "success";
      }
      

3.8、SpringMVC的异常处理

  1. 异常处理思路

    1. Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进 行异常的处理。
  2. SpringMVC的异常处理

    1. 自定义异常类

      /**
       * @author yang
       * @date 2021年08月12日 16:04
       * 自定义异常类
       */
      public class SysException extends Exception{
      ​
          private String msg;
      ​
          public String getMsg() {
              return msg;
          }
      ​
          public void setMsg(String msg) {
              this.msg = msg;
          }
      ​
          public SysException(String msg) {
              this.msg = msg;
          }
      }
      
    2. 自定义异常处理器

      /**
       * @author yang
       * @date 2021年08月12日 16:12
       * 异常处理器
       */
      public class SysExceptionResolver implements HandlerExceptionResolver {
      ​
          /**
           * 处理异常业务逻辑
           * @param httpServletRequest
           * @param httpServletResponse
           * @param o
           * @param e
           * @return
           */
          @Override
          public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
              //获取到异常对象
              SysException sysException = null;
              if (e instanceof SysException){
                  sysException = (SysException) e;
              }else {
                  sysException = new SysException("系统正在维护...");
              }
              //创建ModelAndView对象
              ModelAndView mv = new ModelAndView();
              mv.addObject("errorMsg",sysException.getMsg());
              //跳转的页面
              mv.setViewName("error");
              return mv;
          }
      }
      
    3. 配置异常处理器

      <!--配置异常处理器对象-->
      <bean id="sysExceptionResolver" class="com.ywb.exception.SysExceptionResolver"></bean>
      

3.9、SpringMVC框架中的拦截器

  1. 拦截器的概述

    1. SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。

    2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链 中的拦截器会按着定义的顺序执行。

    3. 拦截器和过滤器的功能比较类似,有区别

      1. 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。
      2. 拦截器是SpringMVC框架独有的。
      3. 过滤器配置了/*,可以拦截任何资源。
      4. 拦截器只会对控制器中的方法进行拦截。
    4. 拦截器也是AOP思想的一种实现方式

    5. 想要自定义拦截器,需要实现HandlerInterceptor接口。

  2. 自定义拦截器步骤

    1. 创建类,实现HandlerInterceptor接口,重写需要的方法

      /**
       * @author yang
       * @date 2021年08月12日 16:48
       * 自定义拦截器
       */
      public class MyInterceptor1 implements HandlerInterceptor {
      ​
          /**
           * 预处理,controller方法执行前
           * return true   放行,执行下一个拦截器,没有则执行controller方法
           * return false  不放行
           * @param request
           * @param response
           * @param handler
           * @return
           * @throws Exception
           */
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("MyInterceptor1执行了...预处理111");
              //request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
              //return false;
              return true;
          }
      ​
          /**
           * 后处理方法,controller方法执行后,success.jsp执行之前
           * @param request
           * @param response
           * @param handler
           * @param modelAndView
           * @throws Exception
           */
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("MyInterceptor1执行了...后处理111");
              //request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
          }
      ​
          /**
           * success.jsp页面执行后,该方法会执行
           * @param request
           * @param response
           * @param handler
           * @param ex
           * @throws Exception
           */
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("MyInterceptor1执行了...最后111");
          }
      }
      
    2. 在springmvc.xml中配置拦截器类

      <!--配置拦截器-->
      <mvc:interceptors>
          <!--配置第一个拦截器-->
          <mvc:interceptor>
              <!--要拦截的具体方法-->
              <mvc:mapping path="/user/*"/>
              <!--不要拦截的方法
              <mvc:exclude-mapping path=""/>
              -->
              <!--配置拦截器对象-->
              <bean class="com.ywb.interceptor.MyInterceptor1"></bean>
          </mvc:interceptor>
          <!--配置第二个拦截器-->
          <mvc:interceptor>
              <!--要拦截的具体方法-->
              <mvc:mapping path="/user/*"/>
              <!--不要拦截的方法
              <mvc:exclude-mapping path=""/>
              -->
              <!--配置拦截器对象-->
              <bean class="com.ywb.interceptor.MyInterceptor2"></bean>
          </mvc:interceptor>
      </mvc:interceptors>
      
    3. HandlerInterceptor接口中的方法

      1. preHandle方法是controller方法执行前拦截的方法

        1. 可以使用request或者response跳转到指定的页面
        2. return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
        3. return false不放行,不会执行controller中的方法。
      2. postHandle是controller方法执行后执行的方法,在JSP视图执行前。

        1. 可以使用request或者response跳转到指定的页面
        2. 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
      3. postHandle方法是在JSP执行后执行

        1. request或者response不能再跳转页面了

3.10、JSON

  1. 导入包
  2. 在springmvc-servlet.xml中配置json乱码问题

用法:

ObjectMapper mapper = new ObjectMapper();
User user = new User();
String str = mapper.writeValueAsString(user);

JSON乱码问题配置(springmvc-servlet.xml):

image-20220317220831329

JSON返回时间

image-20220317221159056