框架学习笔记 ---- SpringMVC

SpringMVC

官网:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

1. MVC 简介

  • MVC 是模型(Model),视图(View),控制器(Controller) 的简写,是一种软件设计规范,是将业务逻辑,数据,显示分离的方法来组织代码。
  • MVC 主要作用是降低了试图与业务逻辑间的双向耦合。
  • MVC 是一种设计模式,MVC 是一种架构模式,不同的 MVC 存在差异。
  1. Model:数据模型,提供要展示的数据,包含数据和行为,可以认为是领域模型或 JavaBean 组件(包含数据和行为),现在一般都分离开:Value Object (数据 Dao) 和服务层(行为 Service)。也就是模型提供了模型数据查询和数据的状态更新等功能,包括数据和业务。

  2. View:负责进行模型的展示,一般就是用户见到的页面。

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UduRESuI-1618129455494)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113112404992.png)]

发展:

  1. Model 1 时代

    早期使用,分为两层:视图层和模型层

    优点:架构简单,适合小型项目开发

    缺点:JSP 职责不单一,不易维护,责任过重。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GfYmHcr3-1618129455500)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113112710731.png)]

  2. Model 2 时代

    分为三部分:视图,控制,模型

    1. 用户发请求

    2. Servlet 接收请求数据,并调用对应的业务逻辑

    3. 业务处理完毕返回更新后的数据给servlet

    4. servlet 转向 jsp渲染页面

    5. 响应给前端更新后的页面

      职责分析:

      Controller:

      1. 获取表单数据,调用业务逻辑,转向指定页面

      Model:

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

      View:

      1. 显示页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ulUEhqX7-1618129455502)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113112808246.png)]

MVC 框架要做那些事情:

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

2. SpringMVC 概述

​ SpringMVC 是 SpringFramework 的一部分,是基于 java 实现 MVC 的轻量级 Web 框架。

Spring MVC 特点:

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

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

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

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

中心控制器 Controller

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Kg3AGBQ-1618129455504)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113125408632.png)]

3. SpringMVC 执行原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wYu4d2IW-1618129455507)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113125957966.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbd3apPc-1618129455509)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210113202152886.png)]

  • 上图中:实线表示 SpringMVC 框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

简要的执行流程

  1. DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心,用户发送请求 DispatcherServlet 接收请求并拦截请求。

​ 假设url:http://localhost:8080/springmvc/hello 表示:请求位于服务器 localhost:8080 上的 springmvc 站点的 hello 控制器。

  • http://localhost:8080 服务器域名
  • springmvc 部署在服务器上的 web 站点
  • hello 表示控制器
  1. HandlerMapping 为处理器映射,DispatcherServlet 调用 HandlerMapping,HandlerMapping 根据请求 url查找 Handler。

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

  3. HandlerException 将解析后的信息传递给 DispatcherServlet,如解析控制器映射等

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

  5. Handler 让具体的 Controller 执行

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

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

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

  9. 视图解析器将解析的逻辑视图名传递给 DispatcherServlet

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

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

4. 代码实现上图原理(配置实现)

  1. 导入依赖

        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.1.9.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.2</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
        </dependencies>
    
  2. 在 web/WEB-INF/ 中配置 web.xml 文件,注册 DispatcherServlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--注册 DispatcherServlet:这个是 SpringMVC 的核心,又叫请求分发器,前端控制器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--DispatcherServlet 要绑定 Spring 的配置文件-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--设置启动级别为 1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--
        在 SpringMVC 中,/  /*
        / :只配匹所有的请求,不会去匹配 jsp 页面,(常用)
        /* :匹配所有的请求,包括 jsp 页面
        -->
        <!--所有请求都会被 SpringMVC 拦截-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  3. 在 resources 中创建 SpringMVC 配置文件,例:springmvc-servlet.xml

    并在文件中添加 1.处理器映射器,2.处理适配器,3.视图解析器

    • 注册 DispatcherServlet
    • 关联 SpringMVC 配置文件
    • 启动级别 1
    • 映射路径 /
    <?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">
    
        <!--1.添加处理器映射器-->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        <!--2.添加处理器适配器-->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
        <!--3.添加视图解析器 : 模板引擎 Thymeleaf Freemarker...-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!--由于该映射器需要注册 bean ,BeanNameUrlHandlerMapping:bean-->
        <bean id="/hello" class="edu.lfsfxy.controller.HelloController"/>
    
    </beans>
    
  4. 编写业务 Controller

    返回类型:ModelAndView

    封装数据,视图跳转

    springmvc-servlet.xml 中使用的处理器映射器是 DispatcherServlet 所以需要将该类注册进 springmvc-servlet.xml,上面代码已经注册。

    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView modelAndView = new ModelAndView();
            //业务代码
            String result = "HelloSpringMvc";
            modelAndView.addObject("msg",result);
            //视图跳转
            modelAndView.setViewName("test");
            return modelAndView;
        }
    }
    
  5. 视图页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
  6. 测试运行即可

    需要注意:如果有错 排查此处 jar 包是否存在。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYtWAboK-1618129455511)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210114122045519.png)]

5. 代码实现(使用注解)

  1. springmvc-servlet.xml 配置文件

    为了支持基于注解的IOC,设置了自动扫描包的功能。

    视图解析器中把所有的视图存放到 /WEB-INF/ 目录下,保证视图安全,因为这个目录下的文件客户端不能直接访问。

    • 让 IOC 的注解生效
    • 静态资源过滤:html .js .css .jpg .mp4 …
    • MVC 的注解驱动
    • 配置视图解析器
    <?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="edu.lfsfxy.controller"/>
        <!--让 Spring MVC 不处理静态资源,.css,.js,.mp3等-->
        <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>
    
  2. Controller

    • @Controller 为了让 Spring IOC 容器初始化时自动扫描
    • @RequestMapping 为了映射请求路径,类与方法都有映射访问时应该是 / 类 / 方法 /
    • 方法中声明 Model 类型的参数为了把 Action 中的数据带到视图中
    • 方法返回的结果时视图的名称 test,加上配置文件的前后缀:/WEB-INF/jsp/test.jsp
    @Controller
    @RequestMapping("/HController")
    public class HelloController {
    
        //真实访问地址:项目名/HController/hello
        @RequestMapping("/hello")
        public String hello(Model model){
            //向模型中添加属性 msg 的值,在页面进行取出渲染
            model.addAttribute("msg", "hello,springmvc");
            //返回一个路径,与 springmvc-servlet.xml 中的前缀后缀进行拼接:/WEB-INF/jsp/test.jsp
            return "test";
        }
    }
    
    

其他代码与 4 配置实现中的一样:jsp界面,web.xml 配置,然后测试运行即可

需要注意的是:由于 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>

使用SpringMVC必须配置的三大部分:

  1. 处理器映射器(HandlerMapping)
  2. 处理器适配器(HandlerAdapter)
  3. 视图解析器(ViewResolver)

通常只需要配置视图解析器,而处理器映射器/适配器只需要开启注解驱动就可以。

  • 总结
    1. 新建 web 项目
    2. 导入相关 jar 包
    3. 配置 web.xml ,注册 DispatcherServlet
    4. 编写 springmvc 配置文件
    5. 创建对应的控制类,Controller
    6. 完善视图和controller之间的关系
    7. 调试运行

6. Controller

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

6.1 实现 Controller 接口

//源码,Controller是一个函数式接口
@FunctionalInterface
//实现该接口的类获得控制器功能
public interface Controller {
    @Nullable
    //处理请求且返回一个模型与视图对象
    ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}

测试:

  1. HelloController.java

    //定义控制器
    //注意不要导错包,实现 Controller 接口,重写方法
    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //返回一个模型视图对象
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.addObject("msg","test1");
            modelAndView.setViewName("test");
            return modelAndView;
        }
    }
    
    
  2. 需要在 Spring 的配置文件中进行注册请求的 bean,name 对应请求路径,class 对应处理的类

    <bean name="/t1" class="edu.lfsfxy.controller.HelloController"/>
    
    
  3. 测试运行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9JXEbUC-1618129455512)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210114151224401.png)]

这种实现接口 Controller 定义控制器方法比较老,不常使用

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

6.2 使用注解 @Controller

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

Spring 可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为保证Spring 能找到控制器,需要在配置文件中声明自动扫描组件。

  1. 需要在配置文件中声明自动扫描组件

    <!--自动扫描指定包,包下面所有注解类交给 IOC 容器管理-->    
    <context:component-scan base-package="edu.lfsfxy.controller"/>
    
    
  2. HelloController.java

    //@Controller 注解的类会自动添加到 Spring 上下文中
    //代表这个类会被 Spring 接管,被这个注解的类中的所有方法,如果返回值是 String,并且具体页面可以跳转,那么就会被视图解析器解析
    @Controller
    public class HelloController{
        //映射访问的路径
        @RequestMapping("t2")
        public String hello(Model model){
            //Spring MVC 会自动化实例化一个 Model 对象用于向视图中传值
            model.addAttribute("msg","test2");
            //返回视图位置
            return "test";
        }
    }
    
    
    
  3. 测试运行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Y48Ld0p-1618129455513)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210114152156819.png)]

7. RequestMapping

@RequestMapping

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

    @Controller
    public class HelloController{
        @RequestMapping("/t2")
        public String hello(){
            return "test";
        }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34nxsgKJ-1618129455514)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210114154233576.png)]

  2. 在类和方法上面都加,访问时需要指定类的路径再指定方法的路径

    @Controller
    @RequestMapping("/controller")
    public class HelloController{
        @RequestMapping("/t2")
        public String hello(){
            return "test";
        }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mrHf0r9O-1618129455515)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210114154401526.png)]

8. RestFul 风格

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

功能:

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


 		2. 资源操作:POST,DELETE,PUT,GET,使用不同方法对资源进行操作
              		3. 分别对应 添加,删除,修改,查询

传统的 URL 地址信息:通过不同的参数来实现不同的效果,方法单一,post 和 get

  1. http://localhost:8080/query?id=1 查询,get
  2. http://localhost:8080/save 添加,post
  3. http://localhost:8080/update 更新,post
  4. http://localhost:8080/delete?id=1 删除,get 或 post

使用 RestFul 风格后的地址信息:通过不同的请求方式来实现不同的效果,地址一样,但功能可以不同

  1. http://localhost:8080/item/1 查询,get
  2. http://localhost:8080/item 添加,post
  3. http://localhost:8080/item 更新,put
  4. http://localhost:8080/item/1 删除,delete

测试

  1. 再 Spring MVC 中使用 @PathVariable 注解,让方法的参数的值对应绑定到一个URL 模板变量上
public class HelloController {

    @RequestMapping("/t2/{a}/{b}")
    public String hello1(@PathVariable int a, @PathVariable int b, Model model) {
        model.addAttribute("msg", a + b);
        return "test";
    }
}

  1. 测试运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H5ye5yjG-1618129455516)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210115122942877.png)]

  • 使用 method 属性指定请求的类型(GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE)

    这种方式才请求的时候必须与 method 指定的请求方式一致,否则报错

        @RequestMapping(value = "/t2/{a}/{b}", method = RequestMethod.GET)
        public String hello2(@PathVariable int a, @PathVariable int b, Model model) {
            model.addAttribute("msg", a + b + 9);
            return "test";
        }
    
    

总结:

Spring MVC 的 @RequestMapping 注解能够处理的 http 请求的方法:GET,PUT,POST,DELETE 等。所有的地址栏请求默认都是 GET 方式请求的。

方法的注解变体,可以指定访问的方式:    
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

 @RequestMapping(value = "/t2/{a}/{b}", method = RequestMethod.GET)
 @GetMapping("/t2/{a}/{b}")  //等同于上面 method 指定的 get 方式
 public String hello2(@PathVariable int a, @PathVariable int b, Model model) {
     model.addAttribute("msg", a + b + 9);
     return "test";
 }

9. Spring MVC 跳转(转发重定向)

9.1 ModelAndView

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

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

/WEB-INF/jsp/index.jsp

//Controller.java 类    
@RequestMapping("/cont/t1")
    public String test1(Model model){
        model.addAttribute("msg","test1");
        return "index";
}

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

9.2 ServletAPI

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

  1. 通过 HttpServletResponse 进行输出

  2. 通过 HttpServletResponse 实现重定向

  3. 通过 HttpServletResponse 实现转发

    @Controller
    public class ResultGo {
     
        @RequestMapping("/result/t1")
        public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
            rsp.getWriter().println("Hello,Spring BY servlet API");
        }
     
        @RequestMapping("/result/t2")
        public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
            rsp.sendRedirect("/index.jsp");
        }
     
        @RequestMapping("/result/t3")
        public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
            //转发
            req.setAttribute("msg","/result/t3");
            req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
        }
     
    }
    
    

9.3 SpringMVC 实现转发和重定向

9.3.1 不需要视图解析器

需要先将 Spring 配置文件中的视图解析器注释掉

@Controller
public class ModelTest {

    @RequestMapping("/cont/t1")
    public String test1(Model model){
        model.addAttribute("msg","test1");
        //转发
        return "/index.jsp";
    }
    @RequestMapping("/cont/t2")
    public String test2(Model model){
        model.addAttribute("msg","test2");
        //转发
        return "forward:/index.jsp";
    }
    @RequestMapping("/cont/t3")
    public String test3(Model model){
        model.addAttribute("msg","test3");
        //重定向
        return "redirect:/index.jsp";
    }
}

9.3.2 使用视图解析器

重定向不需要视图解析器的,本身就是重新请求一个新的页面,但是要注意路径准确的问题。

@Controller
public class ModelTest {

    @RequestMapping("/cont/t1")
    public String test1(){
        //转发
        return "test";
    }
    @RequestMapping("/cont/t2")
    public String test2(){
        //重定向
        return "redirect:/index.jsp";
    }
}

10. 数据处理

10.1 处理提交数据

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

    浏览器访问:http://localhost:8080/user/t1?name=study

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/t1")
        public String test1( String name, Model model){
            System.out.println(name);
            return "test";
        }
    }
    
    

    运行结果:study

  2. 提交的域名称与处理方法的参数名不一致

    浏览器访问:http://localhost:8080/user/t2?username=study

        //@RequestParam("username") :username提交的域的名称
    @RequestMapping("/t2")
        public String test2(@RequestParam("username") String name){
            System.out.println(name);
            return "test";
        }
    
    

    运行结果:study

  3. 提交的是一个对象

    要求提交的表单域和对象的属性名一致,参数使用对象即可

    注意:如果使用对象的话,url 中传递的参数名和对象的名字必须一致,否则就是 null。

    实体类:

    //使用了 Lombok 插件
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }
    
    

    浏览器访问:http://localhost:8080/user/t3?id=123&name=lisan&age=20

        @RequestMapping("/t3")
        public String test3(User user){
            System.out.println(user);
            return "test";
        }
    
    

    运行结果:User(id=123, name=lisan, age=20)

10.2. 数据显示到前端

  1. ModelAndView,实现 Controller 实现方法

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

        @RequestMapping("/t2")
        public String test2(@RequestParam("username") String name,Model model){
            //封装要显示到视图中的数据
            //相当于 req.setAttribute("name",name);
            model.addAttribute("msg",name);
            System.out.println(name);
            return "test";
        }
    
    
  3. ModelMap

        @RequestMapping("/t2")
        public String test2(@RequestParam("username") String name, ModelMap modelMap){
            //封装要显示到视图中的数据
            //相当于 req.setAttribute("name",name);
            modelMap.addAttribute("msg",name);
            System.out.println(name);
            return "test";
        }
    
    

简单的区别:

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

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

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

11. 乱码问题解决

编写测试环境:

  1. index.jsp

    <form action="/enc/t1" method="post">
        <input type="text" name="name">
        <input type="submit">
    </form>
    
    
  2. EncodingTest.java

    @Controller
    public class EncodingTest {
        @RequestMapping("/enc/t1")
        public String test(String name,Model model){
            model.addAttribute("msg",name);
            return "test";
        }
    }
    
    
  3. 运行测试,出现中文乱码情况

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oCxJDfPd-1618129455518)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210115195225801.png)]

解决方法:

  1. 传统的方法,继承 fliter

    Filter.class

    public class Filter implements javax.servlet.Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        public void destroy() {
    
        }
    }
    
    

    创建好 Filter 类之后需要再 web.xml中进行注册

        <filter>
            <filter-name>encoding</filter-name>
            <filter-class>edu.lfsfxy.controller.Filter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    

    然后测试运行就可以,这种方法可以解决乱码的问题

  2. Spring MVC 方法

    之前通过自定义的过滤器解决,SpringMVC 提供了一个过滤器,直接在 web.xml 中进行配置就可以了,但是在一些情况下可能对 get 支持不太好

    一般情况下这种这种方式可以解决乱码的问题了。

        <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>
    
    
  3. 网上大神写的过滤器,非常 nice ,完美解决

    使用自行在 web.xml 中进行注册就可以

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

12. Json 交互处理

JSON(JavaScript Object Notation, JS 对象标记)是一种轻量级的数据交换格式,使用比较广泛

完全独立于编程语言的文本格式来存储和表示数据,简介和清晰的层次结构使得 JSON 成为理想的数据交换语言,易于阅读和编写,同时也易于机器解析生成,有效地提升网络传输效率。

在 JS 语言中,一切都是对象,因此,任何 JS 支持的类型都可以通过 JSON 来表示,例如字符串,数字,对象,数组等

要求和语法格式:

  • 对象表示为键值对,数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

JSON 键值对是用来保存 JS 对象的一种方式,和 JS 对象的写法也大同小异,键值对 组合中的键名写在前面并用双引号 “” 包裹,使用冒号 :分隔,紧着这值。

{"name":"张三"}
{"age":3}

JSON 是 JS 对象的字符串表示方法,用文本表示一个 JS 对象的信息,本质是一个字符串。

var obj = {name:'wang',age:20};	//这是一个对象,键名也可以使用引号
var json = '{"name":"wang","age":"20"}';	//这时一个 JSON 字符串,本质是一个字符串

JSON 和 JS 对象转换:

  1. JS 字符串转换为 JSON 对象,JSON.stringify() 方法

        <script type="text/javascript">
            var user = {
                name:"wang",
                age:20
            };
            var parse = JSON.stringify(user);
            console.log(parse);
        </script>
    
    结果:'{"name":"wang","age":"20"}'
    
    
  2. JSON 字符串转换为 JS 对象,JSON.parse() 方法

        <script type="text/javascript">
            var user = {
                name:"wang",
                age:20
            };
            var parse = JSON.stringify(user);
            console.log(parse);
            console.log(JSON.parse(parse))
        </script>
    
    结果:{name:"wang",age:20}
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4KzlhOJ-1618129455519)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210116123229281.png)]

12.1 Jackson返回数据

  • Controller 返回的 JSON 数据,Jackson 应该是目前比较好的 json 解析工具

编写测试环境

  1. 使用前导入依赖
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.0</version>
        </dependency>

  1. 配置 SpringMVC 需要的配置,web.xml

    在这个文件中需要注册 DispatcherServlet,关联spring容器,设置顺序为 1,设置过滤器解决springmvc 的乱码问题。

        <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>
    
    
    
  2. 在 resources 下建立 springmvc-servlet.xml 容器文件

    在这个文件中配置自动扫描包使注解生效,配置视图解析器 ViewResolver。还有注解驱动和过滤静态资源,这两项可以省略。

    <?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
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <context:component-scan base-package="edu.lfsfxy.controller"/>
        <mvc:annotation-driven/>
        <mvc:default-servlet-handler/>
    
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
    
  3. User.java 测试类

    //这里使用了 lombok 插件,使用注解可以替代有 无/参构造,get/set 等方法
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private int id;
        private String name;
        private int age;
    
    }
    
    
  4. UserController.java

    • @ResponseBody ,使用后不经过xml中的视图解析器,返回字符串类型,针对于单个方法,作用于方法上,类为 Controller
    • @RestController,这个也可以不经过视图解析器,作用于类上,使用后这个类里面所有的方法只会返回 json 字符串了,不用每个方法都添加上面那个了。
    @Controller
    public class UserController {
        @RequestMapping("/j1")
        @ResponseBody //使用后不经过xml中的视图解析器,返回字符串类型
        public String json() throws JsonProcessingException {
            //创建一个 jackson 的对象映射器,用来解析数据
            ObjectMapper mapper = new ObjectMapper();
            User user = new User(1, "wang", 20);
            //将对象解析成 json 格式
            String str = mapper.writeValueAsString(user);
            return str;
        }
    }
    
    
  5. 测试运行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nw4HC8co-1618129455520)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210116134525598.png)]

  6. 至此测试完成,但是中文会由乱码问题

    设置编码格式为 utf-8,以及返回的类型,有效解决乱码问题

    @RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-at5sxG1k-1618129455521)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210116134808913.png)]

    但是如果项目中由许多请求需要在每一个方法都加一个 produces 来解决乱码会非常的繁琐,所以 SpringMVC 可以通过配置统一指定。

    在 springmvc-servlet.xml 文件中添加 StringHttpMessageConverter 转换配置 :

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

返回一个集合

@RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        //创建一个 jackson 的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        List<User> list = new ArrayList<User>();
        User user1 = new User(1, "李", 20);
        User user2 = new User(2, "李", 20);
        User user3 = new User(3, "李", 20);
        User user4 = new User(4, "李", 20);
        User user5 = new User(5, "李", 20);
        //将对象解析成 json 格式
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
        String str = mapper.writeValueAsString(list);
        return str;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZNXRqbTK-1618129455522)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210116144014716.png)]

返回时间的对象

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

这样输出的是一个时间戳

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hkzM0PtM-1618129455523)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210116144306795.png)]

  • 修改方法一

        @RequestMapping("/j3")
        public String json3() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            Date date = new Date();
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            String str = mapper.writeValueAsString(format.format(date));
            return str;
        }
    
    
  • 修改方法二

        @RequestMapping("/j3")
        public String json3() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            //不时间戳,设置不生效
            mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
            Date date = new Date();
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            //指定日期格式
            mapper.setDateFormat(format);
            String str = mapper.writeValueAsString(date);
            return str;
        }
    
    

拓展:

上面的代码中每个方法都需要建立一个 mapper 对象,可以将这个操作抽象为一个 until 类

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 dateFormat) {
       ObjectMapper mapper = new ObjectMapper();
       //不使用时间戳的方式
       mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
       //自定义日期格式对象
       SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
       //指定日期格式
       mapper.setDateFormat(sdf);
       try {
           return mapper.writeValueAsString(object);
       } catch (JsonProcessingException e) {
           e.printStackTrace();
       }
       return null;
   }
}

使用的时候直接调用自定义的 Utils 里面的 方法就可以:

@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
   Date date = new Date();
   String json = JsonUtils.getJson(date);
   return json;
}

12.2 fastjson

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

  1. 导入依赖

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

    fastjason 三个主要类:

    1. JSONObject 代表 json 对象

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

    2. JSONArray 代表 json 对象数组

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

    3. JSON 代表 JSONObject 和 JSONArray 的转化

      JSON 类源码分析使用,仔细观察这些方法,主要是实现 json 对象,json 对象数组,javabean 对象,json 字符串之间的相互转化。

  2. 代码测试

        public void test(){
            List<User> list = new ArrayList<User>();
            User user1 = new User(1, "李", 20);
            User user2 = new User(2, "李", 20);
            User user3 = new User(3, "李", 20);
            User user4 = new User(4, "李", 20);
            User user5 = new User(5, "李", 20);
            //将对象解析成 json 格式
            list.add(user1);
            list.add(user2);
            list.add(user3);
            list.add(user4);
            list.add(user5);
            System.out.println("Java对象转JSON字符串");
            String s = JSON.toJSONString(list);
            System.out.println(s);
    
            System.out.println("JSON字符串转Java对象");
            User jsonObject = JSON.parseObject(JSON.toJSONString(user1),User.class);
            System.out.println(jsonObject);
        }
    
    
  3. 输出结果

    Java对象转JSON字符串
    [{"age":20,"id":1,"name":"李"},{"age":20,"id":2,"name":"李"},{"age":20,"id":3,"name":"李"},{"age":20,"id":4,"name":"李"},{"age":20,"id":5,"name":"李"}]
    JSON字符串转Java对象
    User(id=1, name=李, age=20)
    
    

    这种工具类,只需要会用就可以,使用的时候能够根据具体的业务去找到对应的实现。

13.Ajax

13.1 简介

Ajax (Asynchronous JavaScript and XML 异步的 JS 和 xml),是一种在无需重新加载整个网页的情况下能够更新部分网页的技术。Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。

传统的网页即不用 ajax 的网页,想要更新内容或提交一个表单需要重新加载整个网页;使用 ajax 后,通过在后台服务器进行少量的数据交换就可以实现异步局部更新;使用 ajax 用户可以创建接近本地桌面应用的直接,高可用,更丰富,更动态的 Web 用户界面。

13.2 Ajax 可以做什么

  • 注册时,输入用户名自动检测用户是否已经存在
  • 登陆时,提示用户名或密码错误
  • 删除数据时不刷新页面完成指定操作 等等…

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

jQuery 提供多个与 Ajax 有关的方法,通过 jQuery Ajax 方法,能够使用 Http Get 和 Http Post 从远程服务器上请求文本,html,xml 或 json ,同时能够把这些外部数据直接载入网页的被选中元素中。

jQuery 不是生产者,本质是 XMLHttpRequest,对他进行了封装,方便调用

jQuery.ajax(...)
       部分参数:
              url:请求地址
             type:请求方式,GETPOST1.9.0之后用method)
          headers:请求头
             data:要发送的数据
      contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
            async:是否异步
          timeout:设置请求超时时间(毫秒)
       beforeSend:发送请求前执行的函数(全局)
         complete:完成之后执行的回调函数(全局)
          success:成功之后执行的回调函数(全局)
            error:失败之后执行的回调函数(全局)
          accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
         dataType:将服务器端返回的数据转换成指定类型
            "xml": 将服务器端返回的内容转换成xml格式
           "text": 将服务器端返回的内容转换成普通文本格式
           "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
         "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
           "json": 将服务器端返回的内容转换成相应的JavaScript对象
          "jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

13.2 代码测试

13.2.1. 简单值交互
  1. web.xml 和 applicationContext.xml 复制之前代码即可

  2. AjaxController.java

    @RestController
    public class AjaxController {
        @RequestMapping("/ajax")
        public void ajax(String name, HttpServletResponse response) throws IOException {
            if ("li".equals(name)){
                response.getWriter().print("true");
            }else {
                response.getWriter().print("false");
            }
        }
    }
    
    
    
  3. 在 jsp 页面中需要导入 jquery ,可以使用在线 CDN ,也可以下载导入

    这里注意,script 不能是自闭和标签

        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script>
    
    
  4. index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script>
        <script>
          function a1() {
            $.post({
                url:"${pageContext.request.contextPath}/ajax",
                data:{'name':$("#text").val()},
                success:function (data) {
                  alert(data);
                }
            });
          }
        </script>
      </head>
      <body>
      test: <input type="text" id="text" οnblur="a1()"/>
      </body>
    </html>
    
    
  5. 测试运行,f12 查看控制台访问情况,当文本框为空或不为 li 时返回false并弹出,为 li 时弹出 true。访问的类型是 xhr 的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZgzZOxe-1618129455523)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210120203644887.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9clARCo-1618129455525)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210120203727938.png)]

13.2.2 对象交互
  1. 简写主要步骤,其余同上

  2. User.java

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private String name;
        private int age;
        private String sex;
    }
    
    
  3. Controller.java

        @RequestMapping("/user")
        public List<User> user(){
            List<User> list = new ArrayList<User>();
            list.add(new User("张三",1,"男"));
            list.add(new User("王五",1,"男"));
            list.add(new User("李四",1,"女"));
            return list;//由于@RestController注解,将list转成json格式返回
        }
    
    
  4. test.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script>
        <script>
            $(function () {
                $("#btn").click(function () {
                    $.post({
                        url:"${pageContext.request.contextPath}/user",
                        success:function (data) {
                            var html;
                            for (var i = 0; i < data.length; i++) {
                                html += "<tr>" +
                                    "<td>" + data[i].name + "</td>" +
                                    "<td>" + data[i].age + "</td>" +
                                    "<td>" + data[i].sex+ "</td>" +
                                    "</tr>"
                            }
                            $("#dataBody").html(html);
                        }
                    });
                })
            });
        </script>
    </head>
    <input type="button" id="btn" value="添加数据">
    <body>
        <table>
            <thead>
            <tr>
                <td>姓名</td>
                <td>年龄</td>
                <td>性别</td>
            </tr>
            </thead>
            <tbody id="dataBody">
            </tbody>
        </table>
    </body>
    </html>
    
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KlnmzWag-1618129455526)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210120214134206.png)]

13.2.3 登录 login
  1. 简写主要步骤,其余同上

  2. Controller.java

        @RequestMapping("/login")
        public String login(String name,String pwd){
            String msg="";
            if (name !=null){
                if (name.equals("admin")){
                    msg = "OK";
                }else {
                    msg = "用户名输入有误!!!";
                }
            }
            if (pwd != null){
                if (pwd.equals("123456")){
                    msg = "OK";
                }else {
                    msg = "密码输入有误!!!";
                }
            }
            return msg;
        }
    
    
  3. login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script>
        <script>
            function a1() {
                $.post({
                   url:"${pageContext.request.contextPath}/login",
                   data:{'name':$("#name").val()},
                   success:function (data) {
                       if (data.toString() == 'OK'){
                           $("#userInfo").css("color","green");
                       }else {
                           $("#userInfo").css("color","red");
                       }
                       $("#userInfo").html(data);
                   }
                });
            }
            function a2() {
                $.post({
                   url:"${pageContext.request.contextPath}/login",
                   data:{'pwd':$("#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>
    
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HD5Agcv8-1618129455527)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121125535540.png)]

需要注意的是 Controller 返回的json 字符串,所以会有乱码现象需要在 applicationContext.xml 文件中进行解决

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

14. SpringMVC 拦截器

14.1 简介及代码实现

SpringMVC 中的拦截器类似于 Servlet 中的过滤器,用于对处理器进行预处理和后处理,开发者可以自己定义一些拦截器来实现特定的功能。拦截器是 AOP 思想的具体应用。

  • 过滤器

    Servlet 规范中的一部分,任何 javaweb 功能都可以使用,在 web.xml 中设置 url-pattern 中配置 /* 后可以对所有要访问的资源进行拦截

  • 拦截器

    拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的功能才能使用,拦截器只会拦截访问的控制器方法,对一些静态:jsp,html,css,image,js 是不会进行拦截的。

代码实现拦截器操作

  1. 创建项目,建立项目结构,配置 web.xml,applicationnContext.xml 文件

  2. 编写一个类实现 HandlerInterceptor 接口,即该类为一个拦截器

    public class MyInterCeptor implements HandlerInterceptor {
    
        //在请求处理之前执行
        //如果返回 true 执行下一个拦截器
        //如果返回 false 不执行下一个拦截器,即不能继续执行
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("====操作执行前====");
            return true;
        }
        //请求处理之后执行
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("====操作执行后====");
        }
        //在 DispatcherServlet 处理后执行,做清理工作
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("====清理====");
        }
    }
    
    
  3. 在 applicationContext.xml 中配置拦截器

        <!--配置拦截器-->
        <mvc:interceptors>
            <mvc:interceptor>
                <!--包括路径及其子路径-->
                <!--/con/*  拦截:/con/add 这种的,/con/add/t1 不会拦截-->
                <!--/con/**  拦截 /con 下的所有-->
                <mvc:mapping path="/**"/>
                <!--装载建立的拦截器类-->
                <bean class="edu.lfsfxy.config.MyInterCeptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
    
  4. TestController.java

    @RestController
    public class TestController {
        @RequestMapping("/t1")
        public void test(){
            System.out.println("====执行。。。操作====");
        }
    }
    
    
  5. 测试运行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QDKEDDRD-1618129455530)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121141717070.png)]

14.2 登录页面的拦截器实现

思路:

  1. 有一个登录页面,通过 controller 访问页面
  2. 登录页面有一个提交表单的动作,需要 controller里面处理,判断用户密码是否正确,正确向session 中写入信息,进入登录成功页面
  3. 拦截用户请求,判断用户是否登录,如果登录进入,否则跳转至登录页面
  1. index.jsp 页面,选择进入登录页面还是成功页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
    <h1>首页</h1>
    <hr>
    <a href="${pageContext.request.contextPath}/user/toLogin">登录页面</a>
    <a href="${pageContext.request.contextPath}/user/toSuccess">成功页面</a>
    </body>
    </html>
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DbeYMukW-1618129455531)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121152559803.png)]

  2. success.jsp ,登录成功页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <hr>
    <hr>
    <h1>登录成功</h1>
    ${user}
    <br>
    <a href="${pageContext.request.contextPath}/user/logOut">注销当前用户</a>
    <hr>
    <hr>
    
    </body>
    </html>
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WmSYi41k-1618129455533)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121152640504.png)]

  3. login.jsp ,登录页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <h1>登录页面</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用户名:<input type="text" name="name">
        <br>
        密码:<input type="text" name="pwd">
        <br>
        <input type="submit" value="登录">
    </form>
    </body>
    </html>
    
    
  4. LoginController.java

    @Controller
    @RequestMapping("/user")
    public class LoginController {
    
        //跳转到登录页面
        @RequestMapping("/toLogin")
        public String toLogin() {
            return "login";
        }
    
        //跳转到成功页面 首页
        @RequestMapping("/toSuccess")
        public String toSuccess(){
            return "success";
        }
    
        @RequestMapping("/login")
        public String login(HttpSession session,String name, String pwd){
            //登录后向 session、 添加用户信息
            session.setAttribute("user",name);
            return "success";
        }
    
        @RequestMapping("logOut")
        public String logOut(HttpSession session){
            //session 注销
            session.invalidate();
            return "login";
        }
    }
    
    
  5. LoginInterceptor.java , 拦截器

    public class LoginInterceptor implements HandlerInterceptor {
    
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            //如果是登录页面放行
            if (request.getRequestURI().contains("login")){
                return true;
            }
            //如果用户已登录也放行
            if (request.getSession().getAttribute("user")!=null){
                return true;
            }
            //如果没有登录跳转到登录页面
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
    }
    
    
  6. 在 applicationContext.xml 文件中配置拦截器

        <!--配置拦截器-->
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <bean class="edu.lfsfxy.config.LoginInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
    

15. 文件上传下载

15.1 文件上传

15.1.1 简介

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

前端:需要将表单的method 设置为 post,并且设置 enctype 设置为 multipart/from-data,只有这样浏览器才会把用户选择的文件以二进制数据发送给服务器。

enctype 属性

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 url 编码方式
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 + 号外,其他字符都不做编码处理,这种方式适合用直接通过表单发送邮件。

Spring MVC 提供了简单的封装,提供了直接的支持,这种支持是即插即用的 MultipartResolver 实现的,使用 Apache Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver

SpringMVC 的文件上传需要依赖 Apache Commons FileUpload 的组件。

15.1.2 代码实现
  1. 需要在 pom.xml 中导入依赖

            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.3</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
            </dependency>
    
    
  2. 配置 bean:multipartResolver

    bean 的 id 必须为:multipartResolver

        <!--文件上传配置-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!--请求的编码格式必须和 jsp 的pageEncoding 属性一致,以便正确读取表单的内容,默认 :ISO-8859-1-->
            <property name="defaultEncoding" value="utf-8"/>
            <!--上传文件大小上限,单位为字节-->
            <property name="maxUploadSize" value="10485760"/>
            <property name="maxInMemorySize" value="40960"/>
        </bean>
    
    

    CommonsMultipartFile 常用方法:

    1. String getOriginalFilename():获取上传文件的名
    2. InputStream getInputStream():获取文件流
    3. void transfer To(File dest):将上传文件保存到一个目录文件中
  3. index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
    <form action="/upload" enctype="multipart/form-data" method="post">
         <input type="file" name="file"/>
         <input type="submit" value="upload">
    </form>
    </body>
    </html>
    
    
  4. FileController.java

    @Controller
    public class FileController {
        //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
        //批量上传CommonsMultipartFile则为数组即可
        @RequestMapping("/upload")
        public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
    
            //获取文件名 : file.getOriginalFilename();
            String uploadFileName = file.getOriginalFilename();
    
            //如果文件名为空,直接回到首页!
            if ("".equals(uploadFileName)) {
                return "redirect:/index.jsp";
            }
            System.out.println("上传文件名 : " + uploadFileName);
    
            //上传路径保存设置
            String path = request.getServletContext().getRealPath("/upload");
            //如果路径不存在,创建一个
            File realPath = new File(path);
            if (!realPath.exists()) {
                realPath.mkdir();
            }
            System.out.println("上传文件保存地址:" + realPath);
    
            InputStream is = file.getInputStream(); //文件输入流
            OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); //文件输出流
    
            //读取写出
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
                os.flush();
            }
            os.close();
            is.close();
            return "redirect:/index.jsp";
        }
    }
    
    
    1. 采用 file.Transto 来保存上传的文件

      /*
       * 采用file.Transto 来保存上传的文件
       */
      @RequestMapping("/upload2")
      public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
       
          //上传路径保存设置
          String path = request.getServletContext().getRealPath("/upload");
          File realPath = new File(path);
          if (!realPath.exists()){
              realPath.mkdir();
          }
          //上传文件地址
          System.out.println("上传文件保存地址:"+realPath);
       
          //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
          file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
       
          return "redirect:/index.jsp";
      }
      
      
  5. 运行测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0DZWm7I9-1618129455534)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121164447907.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6XYYKWOJ-1618129455535)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121164524241.png)]

15.2 文件下载

  1. 设置 response 响应头
  2. 读取文件,inputStream
  3. 写出文件,outputStream
  4. 执行
  5. 关闭流

代码实现:

@RequestMapping(value = "/download")
    public String downloads(HttpServletResponse response, HttpServletRequest request) throws Exception {
        //要下载的图片地址
        String path = request.getServletContext().getRealPath("/upload");
        String fileName = "t01d8f5666e21344ac5.jpg";
        //1、设置response 响应头
        response.reset(); //设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8"); //字符编码
        response.setContentType("multipart/form-data"); //二进制传输数据
        //设置响应头
        response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path, fileName);
        //2、 读取文件--输入流
        InputStream input = new FileInputStream(file);
        //3、 写出文件--输出流
        OutputStream out = response.getOutputStream();
        byte[] buff = new byte[1024];
        int index = 0;
        //4、执行 写出操作
        while ((index = input.read(buff)) != -1) {
            out.write(buff, 0, index);
            out.flush();
        }
        out.close();
        input.close();
        return null;
    }

jsp页面:

<a href="/download">下载</a>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46QA059q-1618129455536)(D:\notes\note\SpringMVC\SpringMVC.assets\image-20210121171259751.png)]
学习参考视频,所学内容根据前面网址视频学习,讲解通俗易懂,以上内容都是看一点视频然后写一点笔记记录下来的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值