SpringMVC详解

23
五月
2021

SpringMVC 详解

1. 回顾 MVC

1.1 什么是 MVC

MVC 是模型(Model)视图(View)控制器(Controller)的简写,是一种软件设计规范。它不是一种设计模式,而是一种架构模式。它以将业务逻辑、数据、显示分离的方法来组织代码,主要作用是降低了视图与业务逻辑间的双向偶合

  • Model(模型): 数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或 JavaBean 组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据 Dao) 和服务层(行为 Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

  • View(视图): 负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。

  • Controller(控制器): 接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。

最典型的 MVC 就是 JSP + servlet + javabean 的模式。

1.2 Model1 时代

早期的 web 开发中,通常所采用的都是 Model1。在 Model1 中,主要分为两层:视图层模型层

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

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

1.3 Model2 时代

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

职责解析:

Controller:控制器

  1. 取得表单数据
  2. 调用业务逻辑
  3. 转向指定的页面

Model:模型

  1. 业务逻辑
  2. 保存数据的状态

View:视图

  1. 显示页面

1.4 回顾 Servlet

步骤:

  1. 新建一个 maven 工程作为父工程,并在 pom.xml 中导入依赖

    <!-- 导入依赖 -->
    <dependencies>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.7</version>
        </dependency>
        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <!-- jsp -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2.1-b03</version>
        </dependency>
        <!-- jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
    
  2. 在父工程中新建一个 Module,并添加 Web Application 的支持

  3. 编写一个 Servlet 类,用来处理用户的请求

    示例:

    HelloServlet.java:

    package com.wmwx.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1.获得前端参数
            String method = req.getParameter("method");
            if (method.equals("add")){
                req.getSession().setAttribute("msg","执行了add方法");
            }
            if (method.equals("delete")){
                req.getSession().setAttribute("msg","执行了delete方法");
            }
            //2.调用业务层(此处暂无)
            //3.视图转发或重定向
            req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  4. 编写一个 jsp 页面作为转发的目标

    示例:

    test.jsp:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  5. 在 web.xml 中注册 Servlet

    <?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">
    
        <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.wmwx.servlet.HelloServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  6. 配置 Tomcat,并通过以下地址进行测试

    地址:localhost:8080/user?method=add
    页面显示:执行了add方法
    
    地址:localhost:8080/user?method=delete
    页面显示:执行了delete方法
    

1.5 MVC 框架要做哪些事情

  1. 将 url 映射到 java 类或 java 类的方法
  2. 封装用户提交的数据
  3. 处理请求——调用相关的业务处理——封装响应数据
  4. 将响应的数据进行渲染 .jsp/.html 等表示层数据

1.6 常见的 MVC 框架

  • 后端:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF 等…
  • 前端:vue、angularjs、react、backbone 等…

2. 什么是 SpringMVC

2.1 概述

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

  • 官方文档:SpringMVC 官方文档

Spring MVC 的特点:

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

2.2 中心控制器

  • Spring 的 web 框架围绕 DispatcherServlet 设计。DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心,它的作用是将请求分发到不同的处理器

  • Spring MVC 框架与许多其他 MVC 框架一样,以请求为驱动围绕一个中心 Servlet 分派请求及提供其他功能DispatcherServlet 实质上同样是一个 Servlet (它继承自 HttpServlet 基类)。

  • 从 Spring 2.5 开始,使用 Java 5 或者以上版本的用户可以采用基于注解的 controller 声明方式。

2.3 第一个 SpringMVC 程序

步骤:

  1. 新建一个 Moudle 并添加 Web Application 的支持

  2. 导入 SpringMVC 的相关依赖(如果父工程中已导入则可以跳过此步骤)

  3. 配置 web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- 配置DispatcherServlet:这是SpringMVC的核心,即请求分发器,官方名为前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- DispatcherServlet要绑定spring的配置文件 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动级别:1——与服务器一同启动 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--
            在SpringMVC中,对于url-pattern:
            /:匹配除了jsp页面以外的所有请求
            /*:匹配所有请求,包括jsp页面
            使用“/*”会导致xxx.jsp.jsp.jsp...的出现,因此使用“/”
        -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  4. 编写 SpringMVC 的配置文件,并将其命名为 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>
    
  5. 添加处理映射器

    <?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">
    
        <!-- 添加处理映射器 -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    </beans>
    
  6. 添加处理器适配器

    <?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">
    
        <!-- 添加处理映射器 -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        <!-- 添加处理器适配器 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
    </beans>
    
  7. 添加视图解析器

    <?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">
    
        <!-- 添加处理映射器 -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        <!-- 添加处理器适配器 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
        <!--
            添加视图解析器:DispatcherServlet传给它的ModelAndView
            执行步骤如下:
            1.获取ModelAndView中的数据
            2.解析ModelAndView中视图的名字
            3.拼接视图名字,找到对应的视图
            4.将数据渲染到这个视图上
        -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
  8. 编写业务操作控制器

    HelloController.java:

    package com.wmwx.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 HelloController implements Controller {
    
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //ModelAndView 模型和视图
            ModelAndView mv = new ModelAndView();
    		//业务代码
            String result = "HelloSpringMVC!";
            //封装对象,放在ModelAndView中
            mv.addObject("msg", result);
            //视图跳转
            //封装要跳转的视图,放在ModelAndView中
            mv.setViewName("hello"); //实际跳转路径: /WEB-INF/jsp/hello.jsp
            return mv;
        }
    
    }
    
  9. 将控制器交给 SpringIOC 容器,注册 bean

    <?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">
    
        <!-- 添加处理映射器 -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        <!-- 添加处理器适配器 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
        <!--
            添加视图解析器:DispatcherServlet传给它的ModelAndView
            执行步骤如下:
            1.获取ModelAndView中的数据
            2.解析ModelAndView中视图的名字
            3.拼接视图名字,找到对应的视图
            4.将数据渲染到这个视图上
        -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!-- BeanNameUrlHandlerMapping需要一个Bean -->
        <bean id="/hello" class="com.wmwx.controller.HelloController" />
    
    </beans>
    
  10. 编写要跳转的 jsp 页面

    hello.jsp:

    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2021/5/22
      Time: 23:48
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  11. 启动 Tomcat 进行测试

  • 如果遇到访问出现404的问题,可以依次排查:

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

2.4 SpringMVC 运行原理

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 执行。

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

  8. HandlerAdapter 将视图逻辑名或模型传递给 DispatcherServlet。

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

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

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

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

2.5 使用注解开发

虽然 2.3 中的列出的步骤十分详细,逻辑也十分清晰,但在一般的开发中不会这么去做。更多地,我们还是会去使用注解来实现。

具体的实现步骤如下:

  1. 新建一个 Moudle 并添加 Web Application 的支持

  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. 导入 SpringMVC 的相关依赖(如果父工程中已导入则可以跳过此步骤)

  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">
    
        <!-- 配置DispatcherServlet:这是SpringMVC的核心,即请求分发器,官方名为前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- DispatcherServlet要绑定spring的配置文件 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动级别:1——与服务器一同启动 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--
            在SpringMVC中,对于url-pattern:
            /:匹配除了jsp页面以外的所有请求
            /*:匹配所有请求,包括jsp页面
            使用“/*”会导致xxx.jsp.jsp.jsp...的出现,因此使用“/”
        -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  5. 编写 SpringMVC 的配置文件,并将其命名为 springmvc-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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.wmwx.controller"/>
        <!-- 让Spring MVC不处理静态资源 -->
        <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>
    
  6. 创建 Controller

    HelloController.java:

    package com.wmwx.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class HelloController {
        @RequestMapping("/hello")   //拼接后的地址:localhost:8080/hello
        public String hello(Model model){
            //封装数据
            model.addAttribute("msg", "HelloSpringMVC!");
            //会被视图解析器解析
            return "hello";     //路径:/WEB-INF/jsp/hello.jsp
        }
    }
    
  7. 创建视图层

    hello.jsp:

    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2021/5/23
      Time: 8:21
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  8. 配置 Tomcat 并启动测试

2.6 小结

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

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

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

3. Controller 配置总结

3.1 控制器(Controller)

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

3.2 接口定义

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

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

因此,如果我们希望使用一个类来实现这个接口,那么只需要实现该方法即可。

步骤如下:

  1. 新建一个 Moudle 并添加 Web Application 的支持

  2. 导入 SpringMVC 的相关依赖(如果父工程中已导入则可以跳过此步骤)

  3. 配置 web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- 配置DispatcherServlet:这是SpringMVC的核心,即请求分发器,官方名为前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- DispatcherServlet要绑定spring的配置文件 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动级别:1——与服务器一同启动 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--
            在SpringMVC中,对于url-pattern:
            /:匹配除了jsp页面以外的所有请求
            /*:匹配所有请求,包括jsp页面
            使用“/*”会导致xxx.jsp.jsp.jsp...的出现,因此使用“/”
        -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  4. 编写 springmvc-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
    
        <!--
        支持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>
    
  5. 编写一个实现 Controller 的类

    package com.wmwx.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;
        }
    }
    
  6. 在 spring 配置文件中注册 bean

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
        
        <!--
        支持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>
    
        <bean class="com.wmwx.controller.ControllerTest1" id="/test1" />
    
    </beans>
    
  7. 编写待跳转的页面

    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2021/5/23
      Time: 9:03
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  8. 启动 Tomcat 测试

    在地址栏中输入:localhost:8080/test1
    页面中显示:ControllerTest1
    

说明:

  • 实现接口 Controller 定义控制器是较老的办法。它的缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个 Controller。因此,使用接口定义的方式比较麻烦,不推荐使用。

3.3 注解定义

@Controller 注解类型用于声明 Spring 类的实例是一个控制器。Spring 可以使用扫描机制来找到应用程序中所有基于注解的控制器类。为了保证 Spring 能找到你的控制器,需要在配置文件中声明组件扫描

<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.wmwx.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />

3.2 的代码基础上,将这段代码添加进 springmvc-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       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.wmwx.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <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>

    <bean class="com.wmwx.controller.ControllerTest1" id="/test1" />

</beans>

接着,增加一个 ControllerTest2.java,并使用 @Controller 注解:

package com.wmwx.controller;

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

//@Controller代表这个类会被Spring接管
//被其注解的类,如果有返回值为String的方法,那么将自动被视图解析器解析
@Controller
public class ControllerTest2 {
    @RequestMapping("/test2")
    public String test1(Model model){
        model.addAttribute("msg", "ControllerTest2");
        return "test";
    }

    @RequestMapping("/test3")
    public String test2(Model model){
        model.addAttribute("msg", "ControllerTest2 - test2");
        return "test";
    }
}

启动 Tomcat 进行测试,可以看到以下结果:

在地址栏中输入:localhost:8080/test2
页面显示:ControllerTest2
在地址栏中输入:localhost:8080/test3
页面显示:ControllerTest2 - test2

可以发现,在该类中,我们定义了两个方法,分别发送了两个请求。这两个请求都可以指向一个视图,但是页面结果的结果是不一样的。由此可以看出,视图是被复用的,而控制器与视图之间是弱耦合关系。

因此,注解定义是最常用的方式。

3.4 @RequestMapping

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

示例:

3.3 的代码基础上,新建一个 ControllerTest3.java:

package com.wmwx.controller;

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

@Controller
@RequestMapping("/ct3")
public class ControllerTest3 {
    @RequestMapping("/test1")
    public String test1(Model model){
        model.addAttribute("msg", "ControllerTest3");
        return "test";
    }
}

启动 Tomcat 测试,可以看到如下结果:

地址栏输入:localhost:8080/ct3/test1
页面显示:ControllerTest3

3.5 Restful 风格

3.5.1 概念

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

Restful 具有资源操作的功能:

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

  • 资源操作:POSTDELETEPUTGET,分别对应添加删除修改查询,使用不同方法对资源进行操作。

3.5.2 传统方式操作资源

在传统方式中,我们需要通过不同的参数来实现不同的效果。这种方式的方法单一,只有 POSTGET

示例:

  • http://127.0.0.1/item/queryItem.action?id=1:查询,GET

  • http://127.0.0.1/item/saveItem.action:增加,POST

  • http://127.0.0.1/item/updateItem.action:修改,POST

  • http://127.0.0.1/item/deleteItem.action?id=1:删除,GET 或 POST

3.5.3 Restful 风格操作资源

使用 Restful 风格,可以通过不同的请求方式来实现不同的效果。

示例:

  • http://127.0.0.1/item/1:查询,GET

  • http://127.0.0.1/item:新增,POST

  • http://127.0.0.1/item:更新,PUT

  • http://127.0.0.1/item/1:删除,DELETE

3.5.4 测试

3.4 的基础上,新建一个 RestfulController.java,并使用注解定义的方式配置 Controller:

package com.wmwx.controller;

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

@Controller
public class RestfulController {
    //映射访问路径
    //使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上
    @RequestMapping("/commit/{a}/{b}")
    public String restfulTest1(@PathVariable int a, @PathVariable int b, Model model){
        //对地址栏里的参数做加法运算
        model.addAttribute("msg", a+b);
        return "test";
    }
}

启动 Tomcat 测试,可以看到如下结果:

地址栏输入:localhost:8080/commit/2/3
页面显示:5

3.5.5 指定请求类型

使用 method 属性指定请求类型,可以收窄请求范围。指定请求谓词的类型如 GETPOSTHEADOPTIONSPUTPATCHDELETETRACE 等。

示例:

package com.wmwx.controller;

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

@Controller
public class RestfulController {
    //映射访问路径
    //使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上
    //限定POST请求才会访问
    @RequestMapping(value="/commit/{a}/{b}", method={RequestMethod.POST})
    public String restfulTest1(@PathVariable int a, @PathVariable int b, Model model){
        model.addAttribute("msg", a+b);
        return "test";
    }
}

通过 method 属性将请求类型限制为 POST,启动 Tomcat 测试,可以看到如下结果:

地址栏输入:localhost:8080/commit/2/3
页面显示:HTTP状态 405 - 方法不允许

注意:所有的地址栏请求默认都是 HTTP GET 类型的。

将 method 属性改为 GET:

package com.wmwx.controller;

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

@Controller
public class RestfulController {
    //映射访问路径
    //使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上
    //限定GET请求才会访问
    @RequestMapping(value="/commit/{a}/{b}", method={RequestMethod.GET})
    public String restfulTest1(@PathVariable int a, @PathVariable int b, Model model){
        model.addAttribute("msg", a+b);
        return "test";
    }
}

再次启动 Tomcat 测试,可以看到如下结果:

地址栏输入:localhost:8080/commit/2/3
页面显示:5

除此之外,方法级别的注解还有如下几个变体,它们所扮演的是 @RequestMapping(method =RequestMethod.XXX) 的一个快捷方式:

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

则前面的代码可以等价于如下:

package com.wmwx.controller;

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

@Controller
public class RestfulController {
    //映射访问路径
    //使用@PathVariable注解,让方法参数的值对应绑定到一个URI模板变量上
    //限定GET请求才会访问
    //等价于 @RequestMapping(value="/commit/{a}/{b}", method={RequestMethod.GET})
    @RequestMapping("/commit/{a}/{b}")
    @GetMapping
    public String restfulTest1(@PathVariable int a, @PathVariable int b, Model model){
        model.addAttribute("msg", a+b);
        return "test";
    }
}

4. 数据处理

4.1 处理提交的数据

处理从前端提交的数据,总共有三种情况,分别为:

  1. 提交的域名称和处理方法的参数名一致

    示例:

    UserController.java :

    package com.wmwx.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class UserController {
        @RequestMapping("/user/t1")
        public String test1(String name, Model model){
            //1.接收参数(已接收)
            //2.传回前端
            model.addAttribute("msg", name);
            //3.视图跳转
            return "test";
        }
    }
    

    启动 Tomcat 测试,可以看到如下结果:

    输入地址:http://localhost:8080/user?name=wmwx
    页面显示:wmwx
    
  2. 提交的域名称和处理方法的参数名不一致

    示例:

    UserController.java:

    package com.wmwx.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class UserController {
        @RequestMapping("user/t2")
        //使用注解@RequestParam
        public String test2(@RequestParam("username") String name, Model model){
            //1.接收参数(已接收)
            //2.传回前端
            model.addAttribute("msg", name);
            //3.视图跳转
            return "test";
        }
    }
    

    启动 Tomcat 测试,可以看到如下结果:

    输入地址:http://localhost:8080/user/t2?username=wmwx
    页面显示:wmwx
    

    事实上,无论前端传来的参数与方法中的参数名是否相同,都推荐加上 @RequestParam 注解来与后端传来的参数作区分

  3. 提交的是一个对象

    示例:

    User.java:

    package com.wmwx.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }
    

    UserController.java:

    package com.wmwx.controller;
    
    import com.wmwx.pojo.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class UserController {
        @RequestMapping("user/t3")
        public String test3(User user, Model model){
            //1.接收参数(已接收)
            //2.传回前端
            model.addAttribute("msg", user.toString());
            //3.视图跳转
            return "test";
        }
    }
    

    启动 Tomcat 测试,可以看到如下结果:

    输入地址:http://localhost:8080/user/t3?id=1&name=snow&age=23
    页面显示:User(id=1, name=snow, age=23)
    

    说明:如果使用对象传参的方式,就要求前端传递的参数名和对象的属性名必须一致,否则就会传 null。

4.2 数据显示到前端

将数据显示到前端,总共有三种方式,分别为:

  1. 通过 ModelAndView

    package com.wmwx.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    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

    package com.wmwx.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class ModelTestController {
        @RequestMapping("/mt/t1")
        public String test1(ModelMap map){
            map.addAttribute("msg", "ModelMap");
            return "test";
        }
    }
    
  3. 通过 Model

    package com.wmwx.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class ModelTestController {
        @RequestMapping("/mt/t2")
        public String test2(Model model){
            model.addAttribute("msg", "ModelMap");
            return "test";
        }
    }
    

这三种方式的区别为:

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

4.3 乱码问题

乱码问题是在我们的开发中十分常见。比如,当用户在表单中输入中文后,提交的数据就有可能出现乱码。好在 SpringMVC 给我们提供了一个过滤器,可以在 web.xml 中直接配置使用:

<!-- 配置spring的过滤器解决乱码问题 -->
<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 中,乱码问题便迎刃而解。

5. JSON

5.1 什么是 JSON

JSON(JavaScript Object Notation,JS 对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。它采用完全独立于编程语言的文本格式来存储和表示数据,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。其简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

在 JavaScript 语言中,一切都是对象。因此,任何 JavaScript 支持的类型都可以通过 JSON 来表示。例如,字符串、数字、对象、数组等。其语法格式如下:

  • 对象表示为键值对,数据由逗号分隔
  • 花括号为对象,方括号保存数组,可在数组中存放多个对象

示例:

{"name": "刘季恒"}
{"age": "18"}
{"sex": "男"}

5.2 JSON 和 JS对象

  • JSON --> JS 对象

    let obj = JSON.parse('{"a": "Hello", "b": "World"}');
    
  • JS 对象 --> JSON

    let json = JSON.stringify({a: 'Hello', b: 'World'});
    

5.3 Jackson

5.3.1 使用 Jackson

JSON 解析工具有很多,其中 Jackson 算是目前比较好用的一个了。如果想要使用 Jackson,需要导入它的 jar 包:

<!-- jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
</dependency>

测试步骤:

  1. 配置 web.xml :

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- 配置DispatcherServlet:这是SpringMVC的核心,即请求分发器,官方名为前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- DispatcherServlet要绑定spring的配置文件 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动级别:1——与服务器一同启动 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--
            在SpringMVC中,对于url-pattern:
            /:匹配除了jsp页面以外的所有请求
            /*:匹配所有请求,包括jsp页面
            使用“/*”会导致xxx.jsp.jsp.jsp...的出现,因此使用“/”
        -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!-- 配置spring的过滤器解决乱码问题 -->
        <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>
    
  2. 配置 springmvc-servlet.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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.wmwx.controller"/>
        <!-- 让Spring MVC不处理静态资源 -->
        <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>
    
  3. 编写实体类

    User.java:

    package com.wmwx.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }
    
  4. 编写 Controller,并改用 @RestController 注解

    UserController.java:

    package com.wmwx.controller;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.wmwx.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    //添加了这个注解不会走视图解析器,而是直接返回字符串
    //若要前后端分离,一般都会使用这个注解
    @RestController
    public class UserController {
        @RequestMapping("/j1")
        public String json1() throws JsonProcessingException {
            //jackson.objectMapper
            ObjectMapper mapper = new ObjectMapper();
            User user = new User(1, "刘季恒", 18);
            //这个方法将对象转化为字符串,需要抛出异常
            String userString = mapper.writeValueAsString(user);
            return userString;
        }
    }
    
  5. 配置 Tomcat 并启动测试

    输入地址:http://localhost:8080/j1
    页面显示:{"id":1,"name":"???","age":18}
    

5.3.2 解决乱码问题

虽然生成的 JSON 字符串已经成功显示,但是其中的中文部分还是出现了乱码。这个问题又该如何解决呢?

Spring 提供了一种统一配置的方式,可以自动处理 Jackson 的乱码问题:

<!-- 解决Jackson的乱码问题 -->
<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>

将这一段添加在 springmvc-servlet.xml 中,乱码问题就解决了!

配置 Tomcat 并启动测试

输入地址:http://localhost:8080/j1
页面显示:{"id":1,"name":"刘季恒","age":18}

5.3.3 集合的输出

5.3.2 的代码基础上,向 UserController.java 中新增一个方法:

@RequestMapping("/j2")
public String json2() throws JsonProcessingException {
    //创建多个对象
    User user1 = new User(1, "刘季恒", 18);
    User user2 = new User(2, "周松雅", 18);
    User user3 = new User(3, "吕沁", 23);
    User user4 = new User(4, "张鹏", 35);
    User user5 = new User(5, "余征", 20);
    //创建集合
    List<User> userList = new ArrayList<User>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);
    userList.add(user5);
    //将集合转化为字符串,需要抛出异常
    return new ObjectMapper().writeValueAsString(userList);
}

配置 Tomcat 并启动测试

输入地址:http://localhost:8080/j2
页面显示:[{"id":1,"name":"刘季恒","age":18},{"id":2,"name":"周松雅","age":18},{"id":3,"name":"吕沁","age":23},{"id":4,"name":"张鹏","age":35},{"id":5,"name":"余征","age":20}]

5.3.4 时间格式的输出

5.3.3 的代码基础上,向 UserController.java 中新增一个方法:

@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
    return new ObjectMapper().writeValueAsString(new Date());
}

配置 Tomcat 并启动测试:

输入地址:http://localhost:8080/j3
页面显示:1621779713335

刷新页面,可以看到显示的变化:

第一次刷新:1621779837915
第二次刷新:1621779843240
第三次刷新:1621779848888
... ...

不难看出,jackson 解析 Date 对象后的默认格式为 Timestamp,即时间戳格式。

如果要将时间戳格式转换成我们想要的格式,有两种方式可以解决:

  1. 纯粹使用 SimpleDateFormat

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return new ObjectMapper().writeValueAsString(sdf.format(new Date()));
    }
    

    配置 Tomcat 并启动测试:

    输入地址:http://localhost:8080/j3
    页面显示:"2021-05-23 22:29:27"
    
  2. 搭配 ObjectMapper

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        //配置ObjectMapper不使用时间戳
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        mapper.setDateFormat(sdf);
        return mapper.writeValueAsString(new Date());
    }
    

    配置 Tomcat 并启动测试:

    输入地址:http://localhost:8080/j3
    页面显示:"2021-05-23 22:35:09"
    

5.3.5 封装成工具类

日期的格式转换是一个经常使用的功能,最好封装为一个工具类去调用。

示例:

JsonUtils.java:

package com.wmwx.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

public class JsonUtils {
    //方法重载无需再写一遍内容,只需调用另一个方法即可
    public static String getJson(Object object) {
        return getJson(object, "yyyy-MM-dd HH:mm:ss");
    }

    public static String getJson(Object object, String sdfString) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        SimpleDateFormat sdf = new SimpleDateFormat(sdfString);
        mapper.setDateFormat(sdf);
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

这样以来,5.3.4 中的代码就可以改成如下方式:

@RequestMapping("/j3")
public String json3() {
    //调用编写好的工具类
    return JsonUtils.getJson(new Date());
}

配置 Tomcat 并启动测试:

输入地址:http://localhost:8080/j3
页面显示:"2021-05-23 22:47:12"

由此可见,现在只需一行代码,就可以做到日期格式的转换。

由于我们在工具类中定义了方法的重载,故可以将 5.3.15.3.3 中的代码一并改造:

package com.wmwx.controller;

import com.wmwx.pojo.User;
import com.wmwx.utils.JsonUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

//添加了这个注解不会走视图解析器,而是直接返回字符串
//若要前后端分离,一般都会使用这个注解
@RestController
public class UserController {
    @RequestMapping("/j1")
    public String json1() {
        User user = new User(1, "刘季恒", 18);
        return JsonUtils.getJson(user);
    }

    @RequestMapping("/j2")
    public String json2() {
        //创建多个对象
        User user1 = new User(1, "刘季恒", 18);
        User user2 = new User(2, "周松雅", 18);
        User user3 = new User(3, "吕沁", 23);
        User user4 = new User(4, "张鹏", 35);
        User user5 = new User(5, "余征", 20);
        //创建集合
        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);
        userList.add(user5);
        //将集合转化为字符串,需要抛出异常
        return JsonUtils.getJson(userList);
    }

    @RequestMapping("/j3")
    public String json3() {
        //调用编写好的工具类
        return JsonUtils.getJson(new Date());
    }
}

5.4 Fastjson

fastjson.jar 是阿里开发的一款专门用于 Java 开发的包,可以方便地实现 json 字符串与 JavaBean 对象的转换,以及 JavaBean 对象与 json 字符串的转换。

要想使用 fastjson,需要先导入 jar 包:

<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

接着,我们在 5.3.5 的代码基础上,向 UserController.java 中新增一个方法:

@RequestMapping("/j4")
public String json4() {
    User user1 = new User(1, "刘季恒", 18);
    User user2 = new User(2, "周松雅", 18);
    User user3 = new User(3, "吕沁", 23);
    User user4 = new User(4, "张鹏", 35);
    User user5 = new User(5, "余征", 20);
    List<User> userList = new ArrayList<User>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);
    userList.add(user5);

    System.out.println("*******Java对象 转 JSON字符串*******");
    String str1 = JSON.toJSONString(userList);
    System.out.println("JSON.toJSONString(userList)==>"+str1);
    String str2 = JSON.toJSONString(user1);
    System.out.println("JSON.toJSONString(user1)==>"+str2);

    System.out.println("\n****** JSON字符串 转 Java对象*******");
    User jp_user1=JSON.parseObject(str2,User.class);
    System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);

    System.out.println("\n****** Java对象 转 JSON对象 ******");
    JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
    System.out.println("(JSONObject) JSON.toJSON(user2).getString(\"name\")==>"+jsonObject1.getString("name"));

    System.out.println("\n****** JSON对象 转 Java对象 ******");
    User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
    System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);

    return null;
}

输出结果:

*******Java对象 转 JSON字符串*******
JSON.toJSONString(userList)==>[{"age":18,"id":1,"name":"刘季恒"},{"age":18,"id":2,"name":"周松雅"},{"age":23,"id":3,"name":"吕沁"},{"age":35,"id":4,"name":"张鹏"},{"age":20,"id":5,"name":"余征"}]
JSON.toJSONString(user1)==>{"age":18,"id":1,"name":"刘季恒"}

****** JSON字符串 转 Java对象*******
JSON.parseObject(str2,User.class)==>User(id=1, name=刘季恒, age=18)

****** Java对象 转 JSON对象 ******
(JSONObject) JSON.toJSON(user2).getString("name")==>周松雅

****** JSON对象 转 Java对象 ******
JSON.toJavaObject(jsonObject1, User.class)==>User(id=2, name=周松雅, age=18)
TAG

网友评论

共有访客发表了评论
请登录后再发布评论,和谐社会,请文明发言,谢谢合作! 立即登录 注册会员