SpringMVC学习笔记(完整)

SpringMVC

狂神说

1. 回顾MVC

1.1 什么是MVC

  • MVC:Model、View、Controller;模型、视图、控制器,是一种软件设计规范;

  • 将业务逻辑、数据、前端显示分离的方法来组织代码;

  • MVC主要作用是降低视图与业务逻辑的双向耦合;

  • MVC是一种架构模式,并不是设计模式。

  • Model模型:

    • 数据模型,提供要展示的数据,包含数据和行为,可以认为是领域模型或JavaBean组件,现在一般分离为:Value Object(数据Dao) 、服务层(行为Service)。即Model提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务;
  • View视图:

    • 负责进行模型的展示,一般是用户界面之类用户想看到的东西;
  • Controller控制器:

    • 接收用户请求,委托给Model进行处理(状态改变),处理完毕后把返回的模型数据返回给Views视图,由View负责展示,即Controller像是一个调度员。
  • 最典型的MVC就是JSP + Servlet + JavaBean模式。

1.2 回顾Servlet

  1. 创建父工程,导入依赖

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.10</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>
    
  2. 创建子工程, 添加web app支持

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

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

    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/hello.jsp").forward(req,resp);
        }
    
        @Override
        protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
  2. 编写jsp跳转页面hello.jsp, 在WEB-INF目录下

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>HelloServlet</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
  3. 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.ano.servlet.HelloServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>HelloServlet</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  4. 配置tomcat 启动并测试

    http://localhost:8080/hello?method=delete
    http://localhost:8080/hello?method=add
    

2. 什么是SpringMVC

2.1 概述

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

  • 查看官方文档:22. Web MVC framework (spring.io)

  • 特点:

    1. 轻量级、简单易学;
    2. 高效,基于请求响应的MVC框架;
    3. 与Spring无缝结合;
    4. 约定优于配置;
    5. 功能强大,支持RESTful、数据验证、格式化、本地化、主题等功能;
    6. 简洁灵活。
    7. 最重要的一点:用的人多。

2.2 中心控制器

  • Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解Controller进行开发,十分简洁;

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

    在这里插入图片描述

2.3 SpringMVC原理

  1. 原理如下图所示:

在这里插入图片描述

  1. SpringMVC执行原理如图所示:其中实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现的部分。

    在这里插入图片描述

  2. 简单分析以下以上执行原理流程

    • DispatcherServlet代表前置控制器,是整个SpringMVC的控制中心。用户发送请求,DispatcherServlet接收请求并拦截请求;
    • HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler;
    • HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器;
    • ④ HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等;
    • HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler;
    • ⑥ Handler让具体的Controller执行;
    • ⑦ Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView;
    • ⑧ HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet;
    • ⑨ DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名;
    • ⑩ 视图解析器ViewResolver将解析的逻辑视图名传给DispatcherServlet;
    • 11 DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图View
    • 12 最终视图View呈现给用户。

3. Hello, SpringMVC

3.1 配置版

  1. 新建module springmvc-02-hellomvc,添加web框架支持;

  2. 确定导入了相关依赖;

  3. 配置web.xml,注册Dispatcher;

    <?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-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--关联一个SpringMVC的配置文件:【servlet-name】-servlet.xml-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动级别设置为1  服务器启动的时候,这个程序跟着启动 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!--/ 匹配所有的请求: (不包括jsp)-->
        <!--/* 匹配所有的请求:(包括jsp) -->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. 编写spring MVC配置文件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"
           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"/>
        <!--添加 视图解析器-->
        <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:HelloController.java ,要么实现Controller接口,要么增加注解,需要返回一个ModelAndView,装数据,封视图;

    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 HelloController implements Controller {
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //创建ModelAndView 模型和视图
            ModelAndView mv = new ModelAndView();
            //封装对象,放在ModelAndView中
            mv.addObject("msg","HelloSpringMVC");
            //封装要跳转的视图,放在ModelAndView中
            mv.setViewName("hello");
            return mv;
        }
    }
    
  6. 将HelloController类注册在SpringIOC容器中,在springmvc-servlet.xml中注册bean

    <bean id="/hello" class="com.ano.controller.HelloController"/>
    
  7. 编写要跳转的页面,/WEB-INF/jsp/hello.jsp

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

    在这里插入图片描述

  9. 可能存在的问题:报404,可能需要在IDEA的发布项目中添加lib依赖,具体操作如下图所示:

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

3.2 注解版

  1. 新建一个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中引入相关依赖,在父依赖中已经引入了;

  4. 配置web.xml;

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--1.注册Servlet-->
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--2.初始化Spring配置文件的位置-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--3.启动级别设置为1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--4.所有请求都会被SpringMVC拦截-->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  5. 添加Spring MVC配置文件resources/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.ano.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>
    
  6. 创建Controller HelloController.java

    package com.ano.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/HelloController")
    public class HelloController {
        /**
         * 在类上注册了@RequestMapping方法就会自动下移,实际地址:项目名称/HelloController/hello
         * @param model
         * @return
         */
        @RequestMapping("/hello")
        public String sayHello(Model model) {
            // 向Model中添加属性msg和值,在JSP页面取出并渲染
            model.addAttribute("msg","HelloSpringMVCAnnotation");
            // WEB_INF/jsp/hello.jsp
            return "hello";
        }
    }
    

    说明:

    • @Controller 是为了让Spring IOC容器初始化的时候自动扫描到;

    • @RequestMapping 是为了映射请求路径,这里因为类和方法上都有映射,所以访问时的请求路径应该是/HelloController/hello;

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

    • 方法返回的结果是视图的名称hello,加上配置文件后的前后缀就变成WEB_INF/jsp/hello.jsp。

  7. 创建视图层 WEB_INF/jsp/hello.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>helloMvcAnnotation</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
  8. 配置Tomcat运行 请求http://localhost:8080/HelloController/hello
    在这里插入图片描述

  9. 小结

    使用SpringMVC必须要配置的三大件:处理器映射器、处理器适配器、视图解析器

    通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可。

4. 控制器和RestFul风格

4.1 控制器Controller

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

4.2 实现Controller接口

Controller接口在org.springframework.web.servlet.mvc包下,该接口只有一个方法:处理请求并返回一个模型与视图对象ModelAndView

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

测试:

  1. 新建一个Moudle,和之前一样,添加web配置;

  2. 确定导入相关依赖,并刷新;

  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">
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
    
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. 添加Spring MVC配置文件resources/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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <!--视图解析器-->
        <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类,ControllerTest1.java;

    public class ControllerTest1 implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","Hello ControllerTestOne");
            mv.setViewName("test");
            return mv;
        }
    }
    
  6. 在配置文件resources/springmvc-servlet.xml中注册请求的bean;

    <bean name="/t1" class="com.ano.controller.ControllerTest1"></bean>
    
  7. 编写前端页面WEB-INF/jsp/test.jsp;

    <%--
      Created by IntelliJ IDEA.
      User: wangjiao
      Date: 2022/9/13
      Time: 20:22
      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运行测试OK!
    在这里插入图片描述

实现Controller接口定义控制器是较老的方法;

缺点:一个控制器中只能有一个方法,如果多个方法则需要定义多个控制器,比较麻烦。

4.3 使用注解@Controller

  • @Controller注解类型用于声明Spring类的实例是一个控制器;

  • Spring可以使用扫描机制来找到应用程序中基于注解的控制类;

  • 为了保证Spring能找到相应控制器,需要在配置文件中声明组件扫描,即在springmvc-servlet.xml中声明组件扫描:

    <context:component-scan base-package="com.ano.controller"></context:component-scan>
    
  • 增加一个控制器类,使用注解实现,ControllerTest2.java

    @Controller
    public class ControllerTest2 {
    
        @RequestMapping("/t2")
        public String index(Model model) {
            model.addAttribute("msg","ControllerTest2");
            return "test";
        }
    }
    
  • 运行tomcat测试OK!

在这里插入图片描述

可以发现,两个请求都可以指向同一个视图,但是页面结果的结果是不一样的,可以看出视图是被复用的,而控制器与视图之间是弱偶合关系;

注解方式是平时使用的最多的方式。

4.4 @RequestMapping

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

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

    访问路径为:ip:port/项目名/t3 该测试示例的访问路径即 http://localhost:8080/t3

  2. 同时注解在类和方法上

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

    访问路径为:ip:port/项目名/admin/t3 该测试示例的访问路径即http://localhost:8080/admin/t3

4.5 RestFul风格

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

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

  • 传统方式操作资源:通过不同参数来实现不同的操作效果。方法比较单一,如下所示:

    http://127.0.0.1/item/queryUser.action?id=1   查询,GET 
    http://127.0.0.1/item/saveUser.action         新增,POST 
    http://127.0.0.1/item/updateUser.action       更新,POST 
    http://127.0.0.1/item/deleteUser.action?id=1  删除,GET或POST
    
  • 使用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
    
  • 测试示例

    @Controller
    public class RestFulController {
        /**
         * 原生的URL:http://localhost:8080/add?a=1&b=2
         */
        @RequestMapping("/add")
        public String addNumTest1(int a, int b, Model model) {
            model.addAttribute("msg",a+b);
            return "test";
        }
    
        /**
         * RestFul方式1:请求方式 method = GET
         * @GetMapping() = RequestMapping("/add/{a}/{b}",method=requestMethod.GET)
         * http://localhost:8080/add/2/3
         */
        @GetMapping("/add/{a}/{b}")
        public String addNumTest2(@PathVariable int a, @PathVariable int b, Model model) {
            model.addAttribute("msg",a+b);
            return "test";
        }
    
        /**
         * RestFul方式2:请求方式 method = POST
         * 复用了相同的URL
         * http://localhost:8080/add/2/3
         */
        @PostMapping("/add/{a}/{b}")
        public String addNumTest3(@PathVariable int a, @PathVariable int b, Model model) {
            model.addAttribute("msg",a+b);
            return "test";
        }
    }
    

5. 重定向与转发(结果跳转方式)

5.1 ModelAndView

  • 通过设置ModelAndView对象,并根据view的名称和视图解析器跳转到指定的页面;

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

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
    
  • Controller类通过实现Controller接口的方式实现

    public class ControllerTest1 implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","Hello ControllerTestOne");
            mv.setViewName("test");
    
            return mv;
        }
    }
    

5.2 ServletAPI

  • 通过设置ServletAPI的方式,不需要视图解析器。

  • 示例:

    @Controller
    public class ResultServletApi {
        /**
         * 输出
         */
        @RequestMapping("/result/t1")
        public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.getWriter().print("Hello, SpringMVC result by ServletAPI");
        }
        /**
         * 重定向
         */
        @RequestMapping("/result/t2")
        public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.sendRedirect("/index.jsp");
        }
        /**
         * 转发
         */
        @RequestMapping("/result/t3")
        public void test3(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
            request.setAttribute("msg","result/t3");
            request.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(request,response);
        }
    }
    
  • 通过HttpServletResponse进行输出

  • 通过HttpServletResponse实现重定向

  • 通过HttpServletRequest实现转发

5.3 SpringMVC

无需视图解析器的情况

  • 默认为转发,forward可加可不加;
  • 重定向redirect必须加上。
@Controller
public class ResultSpringMvc {
    /**
     * 转发
     */
    @RequestMapping("/resM/t1")
    public String test1(Model model) {
        model.addAttribute("msg","/resM/t1");
        return "/WEB-INF/jsp/test.jsp";
    }
    /**
     * 转发
     */
    @RequestMapping("/resM/t2")
    public String test2(Model model) {
        model.addAttribute("msg","/resM/t2");
        return "forward:/WEB-INF/jsp/test.jsp";
    }
    /**
     * 重定向
     */
    @RequestMapping("/resM/t3")
    public String test2() {
        return "redirect:/index.jsp";
    }
}

有视图解析器的情况

  • 默认为转发,forward不可以加;
  • 重定向redirect必须加上。
@Controller
public class ResultSpringMvc2 {
    /**
     * 转发
     */
    @RequestMapping("/resM2/t1")
    public String test1(Model model) {
        model.addAttribute("msg","resM2/t1");
        return "test";
    }

    /**
     * 重定向
     */
    @RequestMapping("/resM2/t2")
    public String test2() {
        return "redirect:/index.jsp";
    }
}

6. 接受请求参数及数据回显

6.1 数据处理三种情况

  • 提交的域名称和处理方法的参数名一致;
  • 提交的域名称和处理方法的参数名不一致;
  • 提交的数据是一个对象;

测试程序

@Controller
public class DataHandle {
    /**
     * 提交的域名称和处理方法的参数名一致
     * http://localhost:8080/test?name=ano
     */
    @RequestMapping("/test")
    public String test(String name, Model model){
        model.addAttribute("msg",name);
        return "test";
    }

    /**
     * 提交的域名称和处理方法的参数名不一致
     * http://localhost:8080/test2?username=ano2
     */
    @RequestMapping("/test2")
    public String test2(@RequestParam("username") String name, Model model){
        model.addAttribute("msg",name);
        return "test";
    }
    
    /**
     * 提交的参数是一个对象时,前端传的参数必须和对象名称一致,否则会null
     * http://localhost:8080/user?name=ano&id=1001&age=18
     * @param user
     * @param model
     * @return
     */
    @RequestMapping("/user")
    public String userInfo(User user, Model model) {
        System.out.println(user);
        model.addAttribute("msg",user.toString());
        return "test";
    }
}

实体类User

package com.ano.pojo;

/**
 * @author wangjiao
 * @version 1.0
 * @date 2022/9/21 17:45
 */
public class User {
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

6.2 数据回显到前端三种方式

  • 第一种方式:ModelAndView

  • 第二种方式:Model

  • 第三种方式:ModelMap

简单对比:

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

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

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

7. 乱码问题

一个简单的测试程序

  1. 在首页编写一个表单index.jsp

    <form action="/encoding/test" method="post">
        <input type="text" name="name">
        <input type="submit">
    </form>
    
  2. 编写响应的Controller类及处理方法

    @RequestMapping("/encoding/test")
    public String test(String name, Model model) {
        model.addAttribute("msg",name);
        return "test";
    }
    
  3. localhost:8080进入首页表单输入中文测试,发现会乱码

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

一般通过配置过滤器解决乱码问题。

7.1 自定义过滤器解决乱码

  1. 编写过滤器类EncodingFilter.java;
public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

  1. 在web.xml种配置乱码过滤器EncodingFilter;
<!--配置乱码过滤-->
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>com.ano.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 再次测试,成功解决乱码。

在这里插入图片描述

7.2 Spring框架提供的过滤器解决乱码

  1. 配置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>
  1. 再次测试,成功解决乱码。

一般情况下,Spring框架默认的这个过滤器就已经能够很好的解决乱码问题,但是有一些极端情况,Spring框架提供的过滤器对GET请求的支持不友好,处理方法如下:

  • 修改Tomcat配置文件,设置编码
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />
  • 自定义过滤器
package com.ano.filter;

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 init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 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);
        filterChain.doFilter(myrequest, response);
    }

    @Override
    public void destroy() {
    }
}
/**
 * 自定义request对象,HttpServletRequest的包装类
 */
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;
    /**
     * 是否编码的标记
     */
    private boolean hasEncode;
    /**
     * 定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
     * @param request
     */
    public MyRequest(HttpServletRequest request) {
        // super必须写
        super(request);
        this.request = request;
    }
    /**
     * 对需要增强方法 进行覆盖
     * @return
     */
    @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();
            // 确保get手动编码逻辑只运行一次
            if (!hasEncode) {
                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();
    }

    /**
     * 取一个值
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        // 取回参数的第一个值
        return values[0];
    }

    /**
     * 取所有值
     * @param name
     * @return
     */
    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }
}

  • 然后在web.xml中配置这个通用过滤器GenericEncodingFilter即可!

8. JSON(Jackson和Fastjson的使用)

8.1 什么是JSON

  • JSON, JavaScript Object Notation, JS对象标记。是一种轻量级的数据交换格式;

  • JSON采用完全独立于编程语言的文本格式来存储和表示数据;

  • 简洁和清晰的层次结构使得JSON成为理想的数据交换语言;

  • 易于阅读和编写,同时也易于机器解析和生成,有效提升网络传输效率;

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

    • 对象表示为键值对,数据由逗号分隔;
    • 花括号保存对象;{}
    • 方括号保存数组。[]
  • JSON键值对是用来保存JavaScript对象的一种方式,写法类似,键名在前面并用双引号""包裹,使用冒号:分隔,紧接着写值;

  • JSON是JavaScript对象的字符串表示法,它使用文本表示一个JS对象的信息,本质就是一个字符串;

    // 这是一个对象
    var obj = {"name":"ano","id":1001,"age":19};
    // 这是一个JSON字符串
    var json = '{"name":"ano","id":1001,"age":19}';
    
  • JSON和JavaScript对象互相转换

    JSON字符串转换为JavaScript对象,使用JSON.parse()方法;

    var obj = JSON.parse('{"name":"ano","id":1001,"age":19}');
    

    JavaScript对象转换为JSON字符串,使用JSON.stringify()方法。

    var json = JSON.stringify({"name":"ano","id":1001,"age":19});
    
  • 代码测试,新建一个module并添加web框架支持,在web目录下新建一个html文件json-01.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
      <script type="text/javascript">
          let user = {
              name:"杨和苏KeyNG",
              id:1001,
              age:19
          };
          console.log(user);
          console.log("===================");
          let userJson = JSON.stringify(user);
          console.log(userJson);
          console.log("===================");
          let user2 = JSON.parse(userJson);
          console.log(user2);
          console.log("===================");
          console.log(user2.name, user2.id, user2.age)
    
      </script>
    </head>
    <body>
    
    </body>
    </html>
    

    使用浏览器打开,查看Console:

    在这里插入图片描述

8.2 Controller返回Json

JSON解析工具:Jackson、Fastjson

8.2.1 Jackson

一个简单的测试,包括json乱码的处理

  1. 使用Jackson首先需要导入jar包:

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>
    
  2. 配置SpringMVC需要的配置文件,这里只展示代码,不详细说步骤了;

    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">
    
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    

    resources/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"
           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">
    
        <context:component-scan base-package="com.ano.controller"></context:component-scan>
        <!--视图解析器-->
        <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下新建文件夹jsp。

  3. 编写一个实体类User.java (添加lombok依赖)

    package com.ano.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private String name;
        private int id;
        private int age;
    }
    
  4. 编写一个Controller类UserController.java

    package com.ano.controller;
    
    import com.ano.pojo.User;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class UserController {
        @RequestMapping("/json/t1")
        @ResponseBody
        public String jacksonTest() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            User user = new User("杨和苏KeyNG",1001,26);
            String userStr = mapper.writeValueAsString(user);
            return userStr;
        }
    }
    
  5. 配置Tomcat,运行测试:
    在这里插入图片描述

  6. 发现这个乱码问题,简单处理一下:

    // 通过@RequestMaping的produces属性设置响应体返回的类型和编码
    @RequestMapping(value = "/json/t1", produces = "application/json;charset=utf-8")
    

    重新测试OK

    在这里插入图片描述

  7. 乱码问题统一解决方式

    用上述方式解决虽然简单,但是项目中的每一个请求都要添加,如果太多请求,这种方式很麻烦。因此可以通过Spring配置统一处理这种乱码问题。

    springmvc-servlet.xml添加StringHttpMessageConverter转换配置:

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"></constructor-arg>
            </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"></property>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    在Controller类上用**@RestController**r注解即可。

    @RestController
    public class UserController {
        @RequestMapping(value = "/json/t1")
        public String jacksonTest() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            User user = new User("杨和苏KeyNG",1001,26);
            String userStr = mapper.writeValueAsString(user);
            return userStr;
        }
    }
    

    再次运行测试,完美解决Json乱码问题。

测试集合的输出

  1. 增加一个新的请求方法在UserController.java

    @RequestMapping("/json/t2")
    public String jacksonTest2() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        User user1 = new User("杨和苏KeyNG",1001,26);
        User user2 = new User("车银优",1001,18);
        User user3 = new User("王嘉尔",1001,27);
        User user4 = new User("南柱赫",1001,26);
        ArrayList<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);
        String userListJson = mapper.writeValueAsString(userList);
        return userListJson;
    }
    
  2. 运行测试OK

    在这里插入图片描述

测试时间对象的输出

  1. 增加一个新的请求方法

    @RequestMapping("/json/t3")
    public String jacksonTest3() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        Date date = new Date();
        String dateJson = mapper.writeValueAsString(date);
        return dateJson;
    }
    
  2. 运行测试,这里Jackson默认把Date对象转换为timestamps时间戳形式:

    在这里插入图片描述

  3. 不使用上面时间戳的形式,自定义时间格式:

    @RequestMapping("/json/t4")
    public String jacksonTest4() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        // 取消使用时间戳
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
        // 自定义时间格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 指定日期格式
        mapper.setDateFormat(dateFormat);
        Date date = new Date();
        String dateJson = mapper.writeValueAsString(date);
        return dateJson;
    }
    

    运行结果:

    在这里插入图片描述

  4. 如果经常使用,可以抽取为工具类:

    package com.ano.utils;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    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 dataFormat) {
            ObjectMapper mapper = new ObjectMapper();
            // 取消使用时间戳
            mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
            // 自定义时间格式
            SimpleDateFormat dateFormat = new SimpleDateFormat(dataFormat);
            // 指定日期格式
            mapper.setDateFormat(dateFormat);
            try {
                return mapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    使用该工具类,代码会更简洁:

    @RequestMapping("/json/t5")
    public String jacksonTest5() throws JsonProcessingException {
        Date date = new Date();
        return JsonUtils.getJson(date);
    }
    
8.2.2 Fastjson
  • fastjson.jar是阿里开发的一款专用于Java开发的包,可以方便实现Json对象与JavaBean对象的转换,实现JavaBean对象与Json字符串的转换,实现Json对象与Json字符串的转换。实现Json的转换方法很多,最后的实现结果都是一样的。
  • Fastjson有三个主要的类:JSONObject、JSONArray、JSON
  • JSONObject
    • 代表Json对象;
    • JSONObject实现了Map接口, JSONObject底层操作是由Map实现的;
    • JSONObject对应Json对象,通过各种形式的get()方法可以获取Json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键:值"对的个数和判断是否为空,其本质是通过实现Map接口并调用接口中的方法完成的。
  • JSONArray
    • 代表 Json对象数组;
    • 内部是List接口中的方法来完成操作的。
  • JSON
    • 代表 JSONObject和JSONArray的转化;
    • 主要是实现Json对象,Json对象数组,Javabean对象,Json字符串之间的相互转化。

简单测试一下

  1. 导入Fastjson包

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.12</version>
    </dependency>
    
  2. 简单测试一下四种转换方法的使用:

    public static void main(String[] args) {
        User user1 = new User("杨和苏KeyNG",1001,26);
        User user2 = new User("车银优",1001,18);
        ArrayList<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);
        System.out.println("Java对象转换为JSON字符串:");
        String userListJsonStr = JSON.toJSONString(userList);
        System.out.println(userListJsonStr);
        String userJsonStr = JSON.toJSONString(user1);
        System.out.println(userJsonStr);
    
        System.out.println();
        System.out.println("JSON字符串转换为Javas对象:");
        User user = JSON.parseObject(userJsonStr, User.class);
        System.out.println(user);
    
        System.out.println();
        System.out.println("Java对象转换为JSON对象:");
        JSONObject userJsonObj = (JSONObject)JSON.toJSON(user2);
        System.out.println(userJsonObj);
        System.out.println(userJsonObj.getString("name"));
    
        System.out.println();
        System.out.println("JSON对象转换为Java对象:");
        User userJavaObj = JSON.toJavaObject(userJsonObj, User.class);
        System.out.println(userJavaObj);
    }
    

    输出结果:

    Java对象转换为JSON字符串:
    [{"age":26,"id":1001,"name":"杨和苏KeyNG"},{"age":18,"id":1001,"name":"车银优"},{"age":27,"id":1001,"name":"王嘉尔"},{"age":26,"id":1001,"name":"南柱赫"}]
    {"age":26,"id":1001,"name":"杨和苏KeyNG"}
    
    JSON字符串转换为Javas对象:
    User(name=杨和苏KeyNG, id=1001, age=26)
    
    Java对象转换为JSON对象:
    {"name":"车银优","id":1001,"age":18}
    车银优
    
    JSON对象转换为Java对象:
    User(name=车银优, id=1001, age=18)
    
  3. 在Controller类中增加一个新的请求方法:

    @RequestMapping("/json/t6")
    public String fastjsonTest() {
        User user1 = new User("杨和苏KeyNG",1001,26);
        User user2 = new User("车银优",1002,18);
        User user3 = new User("王嘉尔",1003,27);
        User user4 = new User("南柱赫",1004,26);
        List<User> userList = new ArrayList<User>();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);
        String userListJsonStr = JSON.toJSONString(userList);
        return userListJsonStr;
    }
    
  4. 配置Tomcat运行测试:

    在这里插入图片描述

9. SSM整合

创建数据库表

CREATE DATABASE `ssmbuild`;

USE `ssmbuild`;

DROP TABLE IF EXISTS `books`;

CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT  INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');

搭建Maven项目环境

  1. 创建一个Maven工程,并添加Web支持;

  2. 在pom.xml中导入相关依赖;

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--数据库驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!--数据库连接池c3p0-->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.2</version>
    </dependency>
    
    <!--Servlet JSP-->
    <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>
    
    <!--Mybatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    
    <!--Spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.10</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.10</version>
    </dependency>
    
    <!--Lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
    
  3. 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>
    
  4. 创建基本的项目结构和配置框架;

    com.ano.controller

    com.ano.dao

    com.ano.pojo

    com.ano.service

    resources/mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
    </configuration>
    

    resources/applicationContext.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>
    

9.1 Mybaits层

基于以上写一个简单的测试

  1. 数据库配置文件resources/database.properties

    jdbc.driver= com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
    jdbc.username=root
    jdbc.password=123456
    
  2. IDEA关联数据库表

    在这里插入图片描述

  3. Mybatis核心配置文件mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <package name="com.ano.pojo"/>
        </typeAliases>
    
    </configuration>
    
  4. 编写数据库表对应的实体类Books.java,(用了lombok插件)

    package com.ano.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Books {
        /**
         * 书籍ID
         */
        private int bookID;
        /**
         * 书籍名称
         */
        private String bookName;
        /**
         * 书籍数量
         */
        private int bookCounts;
        /**
         * 书籍简介
         */
        private String detail;
    }
    
  5. 编写Dao层Mapper接口BooksMapper.java

    package com.ano.dao;
    
    import com.ano.pojo.Books;
    import org.apache.ibatis.annotations.Param;
    
    import java.util.List;
    
    public interface BooksMapper {
        /**
         * 增加一本书
         * @param book
         * @return
         */
        int addBook(Books book);
    
        /**
         * 根据ID删除一本书
         * @param id
         * @return
         */
        int deleteBookById(@Param("bookID") int id);
    
        /**
         * 修改一本书
         * @param book
         * @return
         */
        int updateBook(Books book);
    
        /**
         * 查询一本书
         * @param id
         * @return
         */
        Books queryBookById(@Param("bookID") int id);
    
        /**
         * 查询所有书
         * @return
         */
        List<Books> queryAllBooks();
    }
    
  6. 编写Mapper接口对应的Mapper.xml文件,BooksMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.ano.dao.BooksMapper">
    
        <insert id="addBook" parameterType="Books">
            insert into ssmbuild.books(bookName, bookCounts, detail)
            values (#{bookName}, #{bookCounts}, #{detail})
        </insert>
    
        <delete id="deleteBookById" parameterType="int">
            delete from ssmbuild.books
            where bookID = #{bookID}
        </delete>
    
        <update id="updateBook" parameterType="Books">
            update ssmbuild.books
            set bookName = #{bookName}, bookCounts = #{bookCounts}, detail = #{detail}
            where bookID = #{bookID}
        </update>
    
        <select id="queryBookById" parameterType="int" resultType="Books">
            select * from ssmbuild.books
            where bookID = #{bookID}
        </select>
    
        <select id="queryAllBooks" resultType="Books">
            select * from ssmbuild.books
        </select>
    </mapper>
    
  7. 这里!记得在Mybatis核心配置文件mybatis-config.xml中注册mapper;

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <package name="com.ano.pojo"/>
        </typeAliases>
        
        <mappers>
            <mapper class="com.ano.dao.BooksMapper"></mapper>
        </mappers>
    
    </configuration>
    
  8. 编写Service层的接口及对应实现类

    BooksService.java

    package com.ano.service;
    
    import com.ano.pojo.Books;
    import java.util.List;
    
    public interface BooksService {
        /**
         * 增加一本书
         * @param book
         * @return
         */
        int addBook(Books book);
    
        /**
         * 根据ID删除一本书
         * @param id
         * @return
         */
        int deleteBookById(int id);
    
        /**
         * 修改一本书
         * @param book
         * @return
         */
        int updateBook(Books book);
    
        /**
         * 查询一本书
         * @param id
         * @return
         */
        Books queryBookById(int id);
    
        /**
         * 查询所有书
         * @return
         */
        List<Books> queryAllBooks();
    }
    

    BooksServiceImpl.java

    package com.ano.service;
    
    import com.ano.dao.BooksMapper;
    import com.ano.pojo.Books;
    import java.util.List;
    
    public class BooksServiceImpl implements BooksService{
    
        /**
         * service调用dao层 set接口方便Spring管理
         */
        private BooksMapper booksMapper;
        public void setBooksMapper(BooksMapper booksMapper) {
            this.booksMapper = booksMapper;
        }
    
        @Override
        public int addBook(Books book) {
            return booksMapper.addBook(book);
        }
    
        @Override
        public int deleteBookById(int id) {
            return booksMapper.deleteBookById(id);
        }
    
        @Override
        public int updateBook(Books book) {
            return booksMapper.updateBook(book);
        }
    
        @Override
        public Books queryBookById(int id) {
            return booksMapper.queryBookById(id);
        }
    
        @Override
        public List<Books> queryAllBooks() {
            return booksMapper.queryAllBooks();
        }
    }
    

9.2 Spring层

  1. 编写Spring整合Mybatis (dao层) 的配置文件spring-dao.xml

    这里使用的数据库连接池是c3p0。特别注意第4步骤!

    <?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"
           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">
        <!--整合Mybatis配置-->
        <!--1.关联数据库文件配置-->
        <context:property-placeholder location="classpath:database.properties"/>
    
        <!--2.数据库连接池配置-->
        <!--这里使用c3p0连接池-->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    
            <!--c3p0连接池私有属性-->
            <!--最大连接数-->
            <property name="maxPoolSize" value="30"/>
            <!--最小连接数-->
            <property name="minPoolSize" value="10"/>
            <!--连接关闭时默认将所有未提交的操作回滚。若为true,则未提交设置为待提交而不是回滚-->
            <property name="autoCommitOnClose" value="false"/>
            <!--当连接池用完时调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。-->
            <property name="checkoutTimeout" value="10000"/>
            <!--定义在从数据库获取新连接失败后重复尝试的次数-->
            <property name="acquireRetryAttempts" value="2"/>
        </bean>
    
        <!--3.SqlSessionFactory配置-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
        <!--4.扫描Dao接口包的配置,动态实现Dao接口注入到Spring容器中-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.ano.dao"/>
        </bean>
    
    </beans>
    
  2. 配置Spring整合service层,编写spring-service.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"
           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">
    
        <!--扫描service下的包-->
        <context:component-scan base-package="com.ano.service"/>
    
        <!--将所有业务类注入到Spring,可以通过配置或者注解实现-->
        <bean id="booksServiceImpl" class="com.ano.service.BooksServiceImpl">
            <property name="booksMapper" ref="booksMapper"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
    </beans>
    
  3. 到此,Spring层配置完成,可以看出Spring就是一个容器,很形象了!

9.3 SpringMVC层

记得项目要添加Web框架支持!

  1. 配置文件web/WEB-INF/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-->
        <absolute-ordering/>
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <!--注意这里是Spring整合配置文件!-->
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!--encodingFilter配置 解决中文乱码-->
        <filter>
            <filter-name>encodingFilter</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>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <session-config>
            <session-timeout>15</session-timeout>
        </session-config>
    </web-app>
    
  2. 编写配置文件resources/spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
    
        <!--1.开启SpringMVC注解驱动-->
        <mvc:annotation-driven/>
        
        <!--2.静态资源默认Servlet配置-->
        <mvc:default-servlet-handler/>
    
        <!--3.配置视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!--4.自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
        <context:component-scan base-package="com.ano.controller"/>
    </beans>
    
  3. Spring配置整合文件resources/applicationContext.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">
    
        <import resource="spring-dao.xml"/>
        <import resource="spring-service.xml"/>
        <import resource="spring-mvc.xml"/>
    
    </beans>
    
  4. 到此,配置完成!

9.4 编写Controller层和视图层

  1. 编写Controller层,BooksController.java ,首先写一个请求方法查询所有书籍列表

    @Controller
    @RequestMapping("/book")
    public class BooksController {
        @Autowired
        @Qualifier("booksServiceImpl")
        private BooksService booksService;
    
        /**
         * 查询所有书籍
         * @param model
         * @return
         */
        @RequestMapping("/allBooks")
        public String bookList(Model model) {
            List<Books> bookList = booksService.queryAllBooks();
            for (Books book : bookList) {
                System.out.println(book.getBookID());
                System.out.println(book.getBookName());
            }
            model.addAttribute("bookList",bookList);
            return "allBooks";
        }
    }
    
  2. 编写首页index.jsp; 注意路径!就是Controller中查询所有书籍方法的请求路径;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>首页</title>
      </head>
      <body>
      <a href="${pageContext.request.contextPath}/book/allBooks">点击进入书籍列表页面</a>
      </body>
    </html>
    
  3. 书籍列表页面WEB-INF/jsp/allBooks.jsp;

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    <html>
    <head>
        <title>新增书籍</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 Bootstrap -->
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>新增书籍</small>
                    </h1>
                </div>
            </div>
        </div>
        <form action="${pageContext.request.contextPath}/book/addBook" method="post">
            书籍名称:<input type="text" name="bookName"><br><br><br>
            书籍数量:<input type="text" name="bookCounts"><br><br><br>
            书籍详情:<input type="text" name="detail"><br><br><br>
            <input type="submit" value="添加">
        </form>
    </div>
    
  4. 配置Tomcat并运行测试,先看一下效果:
    在这里插入图片描述

  5. 接下来就是根据allBooks.jsp书籍列表页的相关路径和参数,实现新增、修改、删除数据功能,不展开写了,看整体代码:

    BooksController.java

    package com.ano.controller;
    
    import com.ano.pojo.Books;
    import com.ano.service.BooksService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    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 java.util.List;
    
    @Controller
    @RequestMapping("/book")
    public class BooksController {
        @Autowired
        @Qualifier("booksServiceImpl")
        private BooksService booksService;
    
        /**
         * 查询所有书籍
         * @param model
         * @return
         */
        @RequestMapping("/allBooks")
        public String bookList(Model model) {
            List<Books> bookList = booksService.queryAllBooks();
            for (Books book : bookList) {
                System.out.println(book.getBookID());
                System.out.println(book.getBookName());
            }
            model.addAttribute("bookList",bookList);
            return "allBooks";
        }
    
        /**
         * 新增书籍
         * @return
         */
        @RequestMapping("/toAddBook")
        public String toAddBook() {
            return "addBook";
        }
        @RequestMapping("/addBook")
        public String addBook(Books book) {
            booksService.addBook(book);
            return "redirect:/book/allBooks";
        }
    
        /**
         * 修改书籍
         * @param id
         * @param model
         * @return
         */
        @RequestMapping("/toUpdateBook")
        public String toUpdateBook(int id,Model model) {
            Books book = booksService.queryBookById(id);
            model.addAttribute("book",book);
            return "updateBook";
        }
        @RequestMapping("/updateBook")
        public String updateBook(Books book) {
            booksService.updateBook(book);
            return "redirect:/book/allBooks";
        }
    
        /**
         * 删除书籍
         * @param id
         * @return
         */
        @RequestMapping("/del/{bookID}")
        public String deleteBook(@PathVariable("bookID") int id) {
            booksService.deleteBookById(id);
            return "redirect:/book/allBooks";
        }
    }
    

    新增书籍页面WEB-INF/jsp/addBook.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    <html>
    <head>
        <title>新增书籍</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 Bootstrap -->
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>新增书籍</small>
                    </h1>
                </div>
            </div>
        </div>
        <form action="${pageContext.request.contextPath}/book/addBook" method="post">
            书籍名称:<input type="text" name="bookName"><br><br><br>
            书籍数量:<input type="text" name="bookCounts"><br><br><br>
            书籍详情:<input type="text" name="detail"><br><br><br>
            <input type="submit" value="添加">
        </form>
    </div>
    

    修改书籍页面WEB-INF/jsp/updateBook.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    <html>
    <head>
        <title>修改书籍信息</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 Bootstrap -->
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>修改书籍信息</small>
                    </h1>
                </div>
            </div>
        </div>
        <form action="${pageContext.request.contextPath}/book/updateBook" method="post">
            <input type="hidden" name="bookID" value="${book.bookID}"/>
            书籍名称:<input type="text" name="bookName" value="${book.bookName}"/><br><br><br>
            书籍数量:<input type="text" name="bookCounts" value="${book.bookCounts}"/><br><br><br>
            书籍详情:<input type="text" name="detail" value="${book.detail}"/><br><br><br>
            <input type="submit" value="添加">
        </form>
    </div>
    

最后,整体看一下项目结构!

在这里插入图片描述

9.5 配置Mybatis日志功能

  1. 添加log4j依赖;

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 编写配置文件resources/log4j.properties

    ### set log levels ###
    log4j.rootLogger = DEBUG , C , D , E 
    
    ### console ###
    log4j.appender.C = org.apache.log4j.ConsoleAppender
    log4j.appender.C.Target = System.out
    log4j.appender.C.layout = org.apache.log4j.PatternLayout
    log4j.appender.C.layout.ConversionPattern = [ssm_study][%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
    
    ### log file ###
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = ../logs/mybatis_study.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = INFO 
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = [mybatis_study][%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
    
    ### exception ###
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.E.File = ../logs/mybatis_study_error.log 
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR 
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = [mybatis_study][%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
    
    ###mybatis show sql###
    log4j.logger.com.ibatis=debug
    log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
    log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
    log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
    
    log4j.logger.java.sql.Connection=debug
    log4j.logger.java.sql.Statement=debug
    log4j.logger.java.sql.PreparedStatement=debug
    
  3. 在Mybatis核心配置文件mybatis-config.xml中添加settings配置日志,注意顺序!

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            <setting name="logImpl" value="LOG4J"/>
        </settings>
        <typeAliases>
            <package name="com.ano.pojo"/>
        </typeAliases>
    
        <mappers>
            <mapper class="com.ano.dao.BooksMapper"></mapper>
        </mappers>
    </configuration>
    
  4. 运行测试OK

在这里插入图片描述

10. Ajax

10.1 什么是Ajax

  • Ajax= Asynchronous JavaScript and XML(异步的 JavaScript 和 XML);
  • Ajax不是新的编程语言,而是一种使用现有标准的新方法,是一种用于创建更好更快以及交互性更强的Web应用程序的技术
  • Ajax最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容;
  • Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行;
  • 传统的网页(即不用Ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页;
  • 使用Ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新;
  • 使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。

10.2 XMLHttpRequest

  • XMLHttpRequest 是 Ajax 的基础和核心。

  • XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

  • 创建XMLHttpRequest对象

    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
        xmlhttp=new XMLHttpRequest();
    }
    else
    {
        // IE6, IE5 浏览器执行代码
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    
  • XMLHttpRequest 对象用于和服务器交换数据。

  • 如需将请求发送到服务器,使用 XMLHttpRequest 对象的 open()send() 方法:

    方法描述
    open(method,url,async)规定请求的类型、URL 以及是否异步处理请求。method:请求的类型;GET 或 POSTurl:文件在服务器上的位置async:true(异步)或 false(同步)
    send(string)将请求发送到服务器。string:仅用于 POST 请求
  • 如需像 HTML 表单那样 POST 数据,使用setRequestHeader() 方法来添加 HTTP 头。然后在 send() 方法中规定希望发送的数据:

    方法描述
    setRequestHeader(header,value)向请求添加 HTTP 头。header: 规定头的名称value: 规定头的值
    xmlhttp.open("POST","/try/ajax/demo_post2.php",true); 
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("fname=Henry&lname=Ford");
    
  • 如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。

    属性描述
    responseText获得字符串形式的响应数据。
    responseXML获得 XML 形式的响应数据。
  • onreadystatechange 事件:

    当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件。readyState 属性存有 XMLHttpRequest 的状态信息。

    下面是 XMLHttpRequest 对象的三个重要的属性:

    属性描述
    onreadystatechange存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
    readyState存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪
    status200: “OK” 404: 未找到页面

    在 onreadystatechange 事件中,规定当服务器响应已做好被处理的准备时所执行的任务。当 readyState 等于 4 且状态为 200 时,表示响应已就绪:

    xmlhttp.onreadystatechange=function() {    
        if (xmlhttp.readyState==4 && xmlhttp.status==200)    
        {        
            document.getElementById("myDiv").innerHTML=xmlhttp.responseText;    
        } 
    }
    

    注意: onreadystatechange 事件被触发 4 次(0 - 4), 分别是: 0-1、1-2、2-3、3-4,对应着 readyState 的每个变化。

  • 回调函数

    回调函数是一种以参数形式传递给另一个函数的函数。

    如果网站上存在多个 AJAX 任务,应该为创建 XMLHttpRequest 对象编写一个标准的函数,并为每个 AJAX 任务调用该函数。

    该函数调用应该包含 URL 以及发生 onreadystatechange 事件时执行的任务(每次调用可能不尽相同):

    function myFunction() {    
        loadXMLDoc("/try/ajax/ajax_info.txt",function()  {        
            if (xmlhttp.readyState==4 && xmlhttp.status==200)        
            {           
                document.getElementById("myDiv").innerHTML=xmlhttp.responseText;        
            }    
        }); 
    }
    

10.3 jQuery Ajax方法的使用

  • Ajax的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。

  • jQuery 提供多个与 AJAX 有关的方法。

  • 通过 jQuery AJAX 方法,能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON ,同时能够把这些外部数据直接载入网页的被选元素中。

  • jQuery Ajax本质就是 XMLHttpRequest,对其进行了封装,方便调用!

  • 语法:

    $.ajax({name:value, name:value, ... })
    
  • 参数

    名称值/描述
    url请求的 URL。默认是当前页面。
    type请求方式,GET、POST(1.9.0之后用method)
    headers请求头
    data规定要发送到服务器的数据。
    contentType发送数据到服务器时所使用的内容类型。默认是:“application/x-www-form-urlencoded”。
    async布尔值,请求是否异步处理。默认是 true。
    timeout设置本地的请求超时时间(以毫秒计)。
    beforeSend(xhr)发送请求前运行的函数。
    complete(xhr,status)请求完成时运行的回调函数(在请求成功或失败之后均调用,即在 success 和 error 函数之后)。
    success(result,status,xhr)当请求成功时运行的函数。
    error(xhr,status,error)如果请求失败要运行的函数。
    accepts通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
    dataType将服务器端返回的数据转换成指定类型
    “xml”将服务器端返回的内容转换成xml格式
    “text”将服务器端返回的内容转换成普通文本格式
    “html”将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
    “script”尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
    “json”将服务器端返回的内容转换成相应的JavaScript对象
    jsonp在一个 jsonp 中重写回调函数的字符串。
    jsonpCallback在一个 jsonp 中规定回调函数的名称。
    cache布尔值,表示浏览器是否缓存被请求页面。默认是 true。
    context为所有 AJAX 相关的回调函数规定 “this” 值。
    dataFilter(data,type)用于处理 XMLHttpRequest 原始响应数据的函数。
    global布尔值,规定是否为请求触发全局 AJAX 事件处理程序。默认是 true。
    ifModified布尔值,规定是否仅在最后一次请求以来响应发生改变时才请求成功。默认是 false。
    password规定在 HTTP 访问认证请求中使用的密码。
    processData布尔值,规定通过请求发送的数据是否转换为查询字符串。默认是 true。
    scriptCharset规定请求的字符集。
    traditional布尔值,规定是否使用参数序列化的传统样式。
    username规定在 HTTP 访问认证请求中使用的用户名。
    xhr用于创建 XMLHttpRequest 对象的函数。

简单测试一下:

  1. 编写springmvc的配置文件resources/spring-mvc.xml;

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
    
        <context:component-scan base-package="com.ano.controller"/>
        <mvc:default-servlet-handler/>
        <mvc:annotation-driven/>
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
  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">
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encodingFilter</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>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    </web-app>
    
  3. 编写一个Controller类AjaxControler.java

    package com.ano.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Controller
    public class AjaxController {
        @RequestMapping("/a1")
        public void ajaxTest(String name, HttpServletResponse response) throws IOException {
            if("admin".equals(name)) {
                response.getWriter().print(true);
            } else {
                response.getWriter().print(false);
            }
    
        }
    }
    
  4. 下载jquery

    在官网下载jquery,jquery-3.6.1.js, 路径为web\statics\js\jquery-3.6.1.js

  5. 编写index.jsp进行测试

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
        <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.1.js"></script>
        <script>
          function a1() {
            $.post({
              url:"${pageContext.request.contextPath}/a1",
              data:{'name':$("#txtName").val()},
              success:function (data,status) {
                alert(data);
                alert(status)
              }
            })
          }
        </script>
      </head>
      <body>
      用户名:<input type="text" id="txtName" οnblur="a1()">
      </body>
    </html>
    
  6. 配置tomcat运行测试,会看到鼠标离开输入框时会发出一个ajax请求

    在这里插入图片描述

10.4 Ajax实现异步加载数据

一个简单测试 SpringMVC实现

  1. 编写一个实体类User.java, 这里使用lombok插件;

    package com.ano.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private String name;
        private int age;
        private String gender;
    }
    
  2. 编写AjaxController类的一个测试接口AjaxController.java

    @RestController
    public class AjaxController {
        @RequestMapping("/a2")
        public List<User> ajaxTest2()  {
            List<User> userList = new ArrayList<User>();
            userList.add(new User("KeyNG",26,"男"));
            userList.add(new User("Ano",26,"女"));
            userList.add(new User("西西PERSISTING",19,"女"));
            return userList;
        }
    }
    
  3. 编写前端页面,就在index.jsp页面修改

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.1.js"></script>
        <script>
            $(function () {
                $("#btn").click(function () {
                    $.post("${pageContext.request.contextPath}/a2",function (data) {
                        console.log(data)
                        console.log(data[0].name);
                        let html =  "";
                        for (let i = 0; i < data.length; i++) {
                            html += `<tr>
                                        <td>${"${data[i].name}"}</td>
                                        <td>${"${data[i].age}"}</td>
                                        <td>${"${data[i].gender}"}</td>
                                    </tr>`
                        }
                        $("#content").html(html)
                        console.log(html)
                    })
                })
            })
        </script>
    </head>
    <body>
    <input type="button" value="加载数据" id="btn">
    <table>
        <thead>
        <tr>
            <td>姓名</td>
            <td>年龄</td>
            <td>性别</td>
        </tr>
        </thead>
        <tbody id="content">
    
        </tbody>
    </table>
    </body>
    </html>
    
  4. 导入依赖jackson-databind,否则会报错

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>
    
  5. 配置Tomcat运行测试

    在这里插入图片描述

  6. 附上spring配置文件spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
    
        <context:component-scan base-package="com.ano.controller"/>
        <mvc:default-servlet-handler/>
        <!--JSON乱码问题配置-->
        <mvc:annotation-driven>
            <mvc:message-converters>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <constructor-arg value="UTF-8"/>
                </bean>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper">
                        <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                            <property name="failOnEmptyBeans" value="false"/>
                        </bean>
                    </property>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>
    
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    

10.5 Ajax实现用户登录验证Demo

一个简单测试 SpringMVC实现,基于10.4章节,配置不用动

  1. 编写Controller类

    @RequestMapping("/a3")
        public String ajaxTest3(String name, String password) {
            String msg = "";
            if(name != null) {
                if("admin".equals(name)) {
                    msg="Ok";
                }else {
                    msg="用户名输入错误";
                }
            }
            if(password != null) {
                if("123456".equals(password)) {
                    msg="Ok";
                }else {
                    msg="密码输入错误";
                }
            }
            return msg;
        }
    
  2. 登录前端页面login.jsp,为了便于测试,与index.jsp同路径

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.1.js"></script>
        <script>
            function a1(){
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{'name':$("#name").val()},
                    success:function (data) {
                        console.log(data)
                        if (data.toString()=='Ok'){
                            $("#userInfo").css("color","green");
                        }else {
                            $("#userInfo").css("color","red");
                        }
                        $("#userInfo").html(data);
                    }
                });
            }
            function a2(){
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{'password':$("#pwd").val()},
                    success:function (data) {
                        if (data.toString()=='Ok'){
                            $("#pwdInfo").css("color","green");
                        }else {
                            $("#pwdInfo").css("color","red");
                        }
                        $("#pwdInfo").html(data);
                    }
                });
            }
        </script>
    </head>
    <body>
    <p>
        用户名:<input type="text" id="name" οnblur="a1()"/>
        <span id="userInfo"></span>
    </p>
    <p>
        密码:<input type="text" id="pwd" οnblur="a2()"/>
        <span id="pwdInfo"></span>
    </p>
    </body>
    </html>
    
  3. 测试

    在这里插入图片描述

11.拦截器

11.1 什么是拦截器

  • SpringMVC的处理器拦截器(HanderInterceptor)类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理;

  • 开发者可以自己定义一些拦截器来实现特定的功能。

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

  • 过滤器

    • servlet规范中的一部分,任何javaweb工程都可以使用;

    • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截。

  • 拦截器

    • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用;

    • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的。

11.2 自定义拦截器

一个简单的测试

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

  2. 编写springmvc配置文件resources/spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           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">
    
        <context:component-scan base-package="com.ano.controller"/>
        <mvc:default-servlet-handler/>
        <mvc:annotation-driven/>
    
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
  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">
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encodingFilter</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>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    </web-app>
    
  4. 自定义一个拦截器

    package com.ano.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;拦截,不执行下一个拦截器
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("================处理前===============");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("================处理后===============");
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("================清理===============");
        }
    }
    
  5. 在spring配置文件中配置拦截器resources/spring-mvc.xml

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.ano.config.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  6. 编写一个Controller简单测试一下

    @RestController
    public class TestController {
        @RequestMapping("/interceptor")
        public String test() {
            System.out.println("TestController.java");
            return "ok";
        }
    }
    
  7. 配置Tomcat运行测试OK

    在这里插入图片描述

    ================处理前===============
    TestController.java
    ================处理后===============
    ================清理===============
    

11.3 用户登录验证Demo

通过拦截器实现验证用户是否登录,如果登录则进入首页,否则无法进入首页。

  1. 编写前端登录页面web/WEB-INF/jsp/login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    
    <h1>登录页面</h1>
    <hr>
    
    <body>
    <form action="${pageContext.request.contextPath}/user/login">
        用户名:<input type="text" name="username"> <br>
        密码:<input type="password" name="password"> <br>
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    
    
  2. 编写前端登录后的首页页面web/WEB-INF/jsp/main.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <h1>首页</h1>
    ${user}
    <a href="${pageContext.request.contextPath}/user/logout">注销</a>
    </body>
    </html>
    
  3. 编写一个Controller类处理请求UserController.java

    @Controller
    @RequestMapping("/user")
    public class UserController {
        /**
         * 进入首页
         */
        @RequestMapping("/main")
        public String main() {
            return "main";
        }
        /**
         * 跳转到登录页面
         */
        @RequestMapping("/goLogin")
        public String goLogin() {
            return "login";
        }
    
        /**
         * 登录
         */
        @RequestMapping("/login")
        public String login(HttpSession session, String username, String password, Model model) {
            System.out.println("from前端=="+username);
            // 把用户的信息存在session中
            session.setAttribute("user", username);
            model.addAttribute("user",username);
            return "main";
        }
    
        /**
         * 退出登录
         */
        @RequestMapping("/logout")
        public String logout(HttpSession session) {
    		// session.invalidate();
            session.removeAttribute("user");
            return "login";
        }
    }
    
  4. index.jsp页面测试跳转

    <%--
      Created by IntelliJ IDEA.
      User: wangjiao
      Date: 2022/10/9
      Time: 14:29
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      <a href="${pageContext.request.contextPath}/user/goLogin">登录</a>
      <a href="${pageContext.request.contextPath}/user/main">首页</a>
      </body>
    </html>
    
  5. 编写一个拦截器LoginInterceptor.java

    package com.ano.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 如果是登录页面则放行
            if(request.getRequestURI().contains("login")) {
                return true;
            }
            // 如果用户已登录也放行
            HttpSession session = request.getSession();
            if(session.getAttribute("user") != null) {
                return true;
            }
            // 如果用户没有登录则跳转到登录页面
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }
    
  6. 此时运行测试,会发现即使用户没有登录也能进入首页;

  7. 在spring配置文件中配置拦截器resources/spring-mvc.xml

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.ano.config.MyInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
            <bean class="com.ano.config.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  8. 再次测试登录拦截功能OK!

    在这里插入图片描述

12. 文件上传和下载

简单起见,使用index.jsp作为前端页面进行测试

12.1 文件上传

一个简单的测试

  1. 新建一个module,并添加web支持;

  2. 导入以下依赖:

    <!--文件上传-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    <!--servlet-api高版本的-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    
  3. 配置spring配置文件resources/spring-mvc.xml,这里同时配置了文件上传的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">
        
        <context:component-scan base-package="com.ano.controller"/>
        <mvc:default-servlet-handler/>
        <mvc:annotation-driven/>
    
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
        
        <!--文件上传配置-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="defaultEncoding" value="utf-8"/>
            <property name="maxUploadSize" value="10485760"/>
            <property name="maxInMemorySize" value="40960"/>
        </bean>
    </beans>
    
  4. 配置web.xml;这个写了很多遍了,基本上是固定写法了。这里省略了防止乱码的filter

    <?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>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  5. 编写前端页面index.jsp

    <form action="/upload2" enctype="multipart/form-data" method="post">
        <input type="file" name="file"/>
        <input type="submit" value="upload">
      </form>
    
  6. 编写Controller类FileController.java处理请求,有两种方式

    package com.ano.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 javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    
    @Controller
    public class FileController {
        /**
         * 上传文件
         */
        @RequestMapping("/upload")
        public String uploadFile(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
            String uploadFileName = file.getOriginalFilename();
            if("".equals(uploadFileName)) {
                return "redirect:/index.jsp";
            }
            String path = request.getServletContext().getRealPath("/upload");
            File realPath = new File(path);
            if(!realPath.exists()) {
                realPath.mkdirs();
            }
            System.out.println(realPath);
            InputStream inputStream = file.getInputStream();
            OutputStream outputStream = new FileOutputStream(new File(realPath, uploadFileName));
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
                outputStream.flush();
            }
            // 先开后关
            outputStream.close();
            inputStream.close();
            return "redirect:/index.jsp";
        }
        
        /**
         * 上传文件 使用CommonsMultipartFile的transferTo方法
         */
        @RequestMapping("/upload2")
        public String uploadFile2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
            String path = request.getServletContext().getRealPath("/upload");
            File realPath = new File(path);
            if(!realPath.exists()) {
                realPath.mkdirs();
            }
            System.out.println(realPath);
    
            file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
            return "redirect:/index.jsp";
        }
    }
    
  7. 配置Tomcat运行测试,两种方式测试时,改一下index.jsp中表单的action属性值即可

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

12.2 文件下载

  1. 基于12.1上传文件的基础上,配置不懂,写一个处理文件下载的请求即可FileController.java

    /**
     * 下载文件
     */
    @RequestMapping("/download")
    public String downloadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取下载文件的地址
        String path = request.getServletContext().getRealPath("/upload");
        String filename = "readMe.txt";
        // 1. 设置response响应头
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("multipart/form-data");
        response.setHeader("Content-Disposition","attachment;fileName=" + URLEncoder.encode(filename,"UTF-8"));
        // 2. 读取文件 输入流
        File file = new File(path,filename);
        InputStream inputStream = new FileInputStream(file);
        // 3. 写出文件 输出流
        OutputStream outputStream = response.getOutputStream();
        // 4. 执行写出操作
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer,0,len);
            outputStream.flush();
        }
        // 5. 关闭流
        outputStream.close();
        inputStream.close();
        return null;
    }
    
  2. 前端页面index.jsp写一个下载文件的链接

     <a href="/download">下载文件</a>
    
  3. 测试

    在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值