哈士奇观狂神说-SpringMVC所记笔记

狂神说-SpringMVC 23.01.07

ssm:Mybatis + Spring + SpringMVC【MVC 三层架构】

JavaSE:认真学习,老师带,入门快

JavaWeb:认真学习,老师带,入门快

SSM 框架:研究官方文档,锻炼自学能力,锻炼笔记能力,锻炼项目能力

SpringMVC + Vue + SpringBoot + SpringCloud + Linux

SSM = JavaWeb做项目

Spring:IOC 和 AOP

SpringMVC:SpringMVC 的执行流程!

SpringMVC:SSM 框架整合

MVC:

MVVM: M V VM ViewModel:双向绑定

// 全栈:后台 + 前端 + 数据库 + 运维
// 前端:后台 + 前端
// Python != 人工智能

Bootstrap可视化布局:https://www.bootcss.com/p/layoutit/

js:

  • 函数:闭包() () 自己调自己
  • Dom
    • id, name, tag
    • create, remove
  • Bom
    • window
    • document

ES6:

import require

1、什么是MVC

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

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

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

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

最典型的 MVC 就是 JSP + servlet (转发,重定向) + javabean 的模式

前端 数据传输 实体类

实体类:用户名,密码,生日,爱好,…

前端:用户名,密码

pojo:User

vo:UserVo

面试问题:假设你的项目的架构,是设计好的,还是演进的? 答:演进的

  • Alibaba PHP
  • 随着用户量大,Java
  • 王坚 去 IOE, MySQL
  • MySQL:MySQL —> AliSQL、AliRedis
  • All in one —> 微服务

2、Model 1 时代

  • 在 web 早起的开发中,通常采用的都是 Model 1.
  • Model 1 中,主要分为两层,视图层和模型层。

优点:架构简单,比较是和小型项目开发。

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

3、Model 2 时代

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

1、用户发请求

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

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

4、servlet 转向到 JSP,由 JSP 来渲染页面

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

职责分析:

1)Controller:控制器

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

2)Model:模型

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

3)View:视图

  1. 显示页面

Model 2 这样不仅提高代码的复用率与项目的扩展性,且大大降低了项目的维护成本。Model 1 模型的实现比较简单,适用于开发小规模项目,Model 1 中的 JSP 页面身兼 View 和 Controller 两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。Model 2 消除了 Model 1 的缺点。

4、回顾 Servlet

1、新建一个 Maven 工程当做父工程! pom依赖!

<dependencies>
    <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.20</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

2、建立一个 Moudle:springMVC-day01-servlet,添加 Web app的支持!

3、导入 servlet 和 jsp 的 jar 依赖 【可不导,因为 父工程里面已经有了】

<dependencies>
    <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>
</dependencies>

4、编写一个 Servlet 类,用来处理用户的请求

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1、获取前端参数
        String method = req.getParameter("method");
        if (method.equals("add")) {
            req.getSession().setAttribute("msg", "执行了 add 方法");
        }
        if (method.equals("delete")) {
            req.getSession().setAttribute("msg", "执行了 delete 方法");
        }
        // 2、调用业务层
        // 3、视图转发或重定向
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

5、编写Hello.jsp,在WEB-INF 目录下新建一个 jsp 的文件夹,新建 hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

6、在 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>hello</servlet-name>
        <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>


<!--    <session-config>-->
<!--        <session-timeout>15</session-timeout>-->
<!--    </session-config>-->

<!--    <welcome-file-list>-->
<!--        <welcome-file>index.jsp</welcome-file>-->
<!--    </welcome-file-list>-->
</web-app>

7、配置 Tomcat,并启动测试

  • localhost:8080/user?method=add
  • localhost:8080/user?method=delete

MVC 框架爱要做哪些事情

1、将 url 映射到 java 类 或 java 类的方法

2、封装用户提交的数据

3、处理请求–调用相关的业务处理–封装响应数据

4、将响应的数据进行渲染,jsp / html 等表示层数据

说明:

常见的服务器端 MVC 框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;

常见前端 MVC 框架:vue、angularjs、react、backbone;

由 MVC 演化出了另外一些模式如:MVP、MVVM 等等…

5、SpringMVC

5.1、SpringMVC 的特点

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

Spirng 的 web 框架围绕 DispatcherServlet【调度 Servlet】 设计。

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

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

最重要一点还是用的人多,使用的公司多。

5.2、中心控制器(Controller)

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

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

SpringMVC 的原理如下图所示:

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

简要分析执行流程

1、DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心,用户发出请求,DispatcherServlet 接受者请求并拦截请求。

  • 我们假设请求的 url 为:http://localhost:8080/SpringMVC/hello
  • 如上 url 拆分成三部分
  • http://localhost:8080 服务器域名
  • SpringMVC 部署在服务器上的 web 站点
  • hello 表示控制器
  • 通过分析,如上 url 表示为:请求位于服务器 localhost:8080 上的 SpringMVC 站点的 hello 控制器。

2、HandlerMapping 为处理器映射。DispatcherServlet 调用HandlerMapping,HandlerMapping 根据请求 url 查找 Handler。

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

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

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

6、Handler 让具体的 Controller 执行。

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

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

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

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

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

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

6、HelloSpringMVC 配置版

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

2、确定导入了 SpringMVC 的依赖!
3、配置 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 关联一个 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、编写 SpringMVC 的配置文件!名称:springmvc-servlet.xml:【servletname】-servlet.xml说明,这里的名称要求要按照官方的。

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

5、添加 处理映射器

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

6、添加 处理器适配器

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

7、添加 视图解析器

<!-- 视图解析器:DispatcherServlet 给他的 ModelAndView
     1、获取了 ModelAndView 的数据
     2、解析 ModelAndView 的视图名字
     3、拼接视图名字,找到对应的视图
     4、将数据渲染到这个视图上
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前端 -->
    <property name="prefix" value="/WEB-INF/jsp/" />
    <!-- 后端 -->
    <property name="suffix" value=".jsp" />
</bean>

8、编写我们要操作的业务 Controller,要么实现 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 中。Model
        mv.addObject("msg","HelloSpringMVC!");
        // 封装要跳转的视图,放在 ModelAndView 中
        mv.setViewName("hello"); //:WEB-INF/jsp/hello.jsp
        return mv;
    }
}

9、将自己类交给 SpringIOC 容器,注册 bean

<!-- BeanNameUrlHandlerMapping:bean -->
<bean id="/hello" class="com.kuang.controller.HelloController" />

10、写要跳转的 jsp 页面,显示 ModelAndView 存放的数据,以及我们的正常页面;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

11、配置 Tomcat 启动测试!

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

1、查看控制台输出,看一下是不是缺少了什么 jar 包。

2、如果 jar 包存在,显示无法输出,就在 IDEA 的项木发布中,添加 lib 依赖!

3、重启 Tomcat 即可解决!

**小结:**这个是为了理解原理,实际开发不会这么搞,会疯的!,接下来看注解版实现,这才是 SpringMVC 的精髓,到底有多么简单,看下图就知道了!

7、注解版

**第一步:**新建一个 Moudle,添加 web 支持!建立包结构 com.kuang.controller

**第二步:**由于 Maven 可能存在资源过滤的问题,我们将配置完善

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

**第三步:**在 pom.xml 文件引入相关的依赖

主要有 Spring 框架核心库、SpringMVC、servlet、JSTL 等,我们在父依赖中已经引入了!

**第四步:**配置 web.xml

注意点:

  • 注意 web.xml 版本问题,要最新版,根据情况来吧
  • 注册 DispatcherServlet
  • 关联 SpringMVC 的配置文件
  • 启动级别为 1
  • 映射路径为 / 【不要用 /*,会404】
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置 DispatcherServlet:这个是 SpringMVC 的核心,请求分发器,前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- DispatcherServlet 关联一个 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>

**第五步:**添加 Spring MVC 配置文件

  • 让 IOC 的注解生效
  • 静态资源过滤:HTML、JS、CSS、图片、视频…
  • MVC 的注解驱动
  • 配置视图解析器

在 resource 目录下添加 springmvc-servlet.xml 配置文件,配置的形式与 Spring 容器配置基本类似,为了支持基于注解的 IOC,设置了自动扫描包的功能,具体配置信息如下:

<?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">

    <!-- 自动扫描包,让指定包下的注解生效,由 IOC 容器统一管理 -->
    <context:component-scan base-package="com.kuang.controller" />

    <!-- 让 SpringMVC 不处理静态资源   .css .js .html .mp3 .mp4-->
    <mvc:default-servlet-handler />

    <!--
    支持 mvc 注解驱动
        在 spring 中一般采用@RequestMapping 注解来完成映射关系
        要想使@RequestMapping 注解失效
        必须向上下文中注册DefaultAnnotationHandlerAdapter 实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入
     -->
    <mvc:annotation-driven />

    <!-- 视图解析器:DispatcherServlet 给他的 ModelAndView       模板引擎 Thymeleaf Freemarker...
     1、获取了 ModelAndView 的数据
     2、解析 ModelAndView 的视图名字
     3、拼接视图名字,找到对应的视图
     4、将数据渲染到这个视图上
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

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

**第六步:**创建 Controller

编写一个 Java 控制类:com.kuang.controller.HelloController,注意编码规范

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

@Controller
@RequestMapping("/HelloController")
public class HelloController {
    
    // 真实访问路径: localhost:8080/HelloController/hello  如果没有 @RequestMapping("/HelloController") 那就直接 localhost:8080/hello
    @RequestMapping("/hello")
    public String hello(Model model) {
        // 封装数据
        model.addAttribute("msg", "Hello,SpringMVCAnnotation!");
        return "hello"; // 会被视图解析器处理;
    }
}
  • @Controller 是为了让 Spring IOC 容器初始化时自动扫描到;
  • @RequestMapping 是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是 /hello/h1;
  • 方法中声明Model类型的参数是为了把 Action 中的数据带到视图中;
  • 方法返回的结果是视图的名称 hello,加上配置文件中的前后缀变成 WEB-INF/jsp/hello.jsp。

**第七步:**创建视图层

在 WEB-INF/jsp目录中创建 hello.jsp,视图可以直接取出并展示从 Controller 带回的信息;

可以通过 EL 表示取出 Model 中存放的值,或者对象;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

第八步:配置 Tomcat 运行

配置Tomcat,开启服务器,访问对应的请求路径!

OK,运行成功!

小结:实现步骤其实非常简单:

1、新建一个 web 项目

2、导入相关 jar 包

3、编写 web.xml,注册 DispatcherServlet

4、编写 springmvc 配置文件

5、接下来就是去创建对应的控制类,controller

6、最后完善前端视图和 controller 之间的对应

7、测试运行调试、

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

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

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

8、SpringMVC:Controller 及 RestFul 风格

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

看看哪些方法可以实现:

8.1、实现 Controller 接口 【不常用、麻烦】

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

// 实现该接口的类获得控制器功能
public interface Controller {
	// 处理请求且返回一个模型与视图对象
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

测试

1、新建一个 Moudle,springmvc-day04-controller。将刚才的 day03 拷贝一份,进行操作!

  • 删除 HelloCOntroller
  • mvc 的配置文件只留下 视图解析器!

2、编写一个 Controller 类,ControllerTest1

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 只要实现了 Controller 接口的类,说明这就是一个控制器了
public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "ControllerTest1");
        mv.setViewName("text");
        return mv;
    }
}

3、编写完毕后,去 Spring 配置文件中注册请求的 bean;name 对应请求路径,class 对应处理请求的类

<bean name="/t1" class="com.kuang.controller.ControllerTest1" />

4、编写前端text.jsp,注意在 WEB-INF/jsp 目录下编写,对应我们的视图解析器

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

5、配置 Tomcat 运行测试,这里没有项目发布名配置的就是一个 /, 所以请求不用家项目名,OK!

8.2、使用注解 @Controller 【常用】

  • @Controller 注解类型用于声明 Spring 类的实例是一个控制器;
  • Spring 可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证 Spring 能找到你的的控制器,需要配置文件中声明组件扫描。
<!-- 自动扫描包,让指定包下的注解生效,由 IOC 容器统一管理 -->
<context:component-scan base-package="com.kuang.controller" />
  • 增加一个 ControllerTest2 类,使用注解实现;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // 代表这个类会被 Spring 接管,被这个注解的类,类中的所有方法,如果返回值是 String,并且有具体页面可以跳转,那么就会被视图解析器解析
public class ControllerTest2 {

    @RequestMapping("/t2")
    public String test1(Model model) {

        model.addAttribute("msg", "ControllerTest2");
        return "text"; // WEB-INF/jsp/text.jsp
    }
}
  • 运行 Tomcat 测试

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

这种是平时用得最多的,除了这两种方式,还有其他方式!

9、@ RequestMapping

  • @RequestMapping 注解用于映射 url 到控制器类或一个特定的处理程序方法。可用于类或方法上,用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
  • 为了测试结论更加准确,我们可以加上一个项目名测试,myweb
  • 只注解在方法上面
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller()
public class ControllerTest3 {

    @RequestMapping("/t1")
    public String test1(Model model) {
        model.addAttribute("msg", "ControllerTest3");
        return "text";
    }
}

访问路径:http://localhost:8080/ 项目名 / t1

  • 同时注解类与方法
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller()
@RequestMapping("/c3")
public class ControllerTest3 {

    @RequestMapping("/t1")
    public String test1(Model model) {
        model.addAttribute("msg", "ControllerTest3");
        return "text";
    }
}

访问路径:http://localhost:8080/ 项目名 / c3 / t1,需要先指定类的路径再指定方法的路径;

10、RestFul 风格

概念

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

功能

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

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

  • http://127.0.0.1/item/queryItem.action?id=1 查询,GET
  • http://127.0.0.1/item/saveItem.action 新增,POST
  • http://127.0.0.1/item/updateItem.action 更新,POST
  • http://127.0.0.1/item/deleteItem.action?id=1 删除,GET 或 POST

使用 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/ 删除,DELETE

学习测试

1、在新建一个类 RESTFulController

@Controller
public class RestFulController {
}

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

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

@Controller
public class RestFulController {

    // 原来的 http://localhost:8080/add?a=1&b=2
    // RestFul: http://localhost:8080/add/a/b
    @RequestMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a,@PathVariable int b, Model model){
        int res = a + b;
        model.addAttribute("msg", "结果为" + res);
        return "text";
    }
}

3、测试请求查看如下

思考:使用路径变量的好处?

  • 使用路径变量更加简洁;感觉是安全呀!
  • 获得参数更加方便,框架会自动进行类型转换
  • 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问的路径是/add/1/a,则路径与方法不匹配,而不会是参数转换失败。

如果结果为 405 则为 不支持

如果结果为 500 则为代码出错

小结:

SpringMVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法,比如 GET,PUT,POST,DELETE 以及 PATCH。

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

方法级别的注解变体有如下几个:组合注解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一个组合注解

它所扮演的是 @RequestMapping (method= RequestMethod.GET) 的一个快捷方式。

平时用的比较多。

11、SpringMVC:结果跳转方式

11.1、ModelAndView 【方式老】

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

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

<!-- 视图解析器:DispatcherServlet 给他的 ModelAndView       模板引擎 Thymeleaf Freemarker...
     1、获取了 ModelAndView 的数据
     2、解析 ModelAndView 的视图名字
     3、拼接视图名字,找到对应的视图
     4、将数据渲染到这个视图上
     -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/" />
    <!-- 后缀 -->
    <property name="suffix" value=".jsp" />
</bean>

对应的 controller 类

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 只要实现了 Controller 接口的类,说明这就是一个控制器了
public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "ControllerTest1");
        mv.setViewName("text");
        return mv;
    }
}

11.2、ServletAPI

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

1、通过 HttpServletResponse 进行输出

2、通过 HttpServletResponse 实现重定向

3、通过 HttpServletResponse 实现转发

@Controller
public class ModelTest1 {

    @RequestMapping("/m1/t1")
    public String test(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        System.out.println(session.getId());
        return "text";
    }
}

11.3、SpringMVC

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

测试前,需要将视图解析器注释掉

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

@Controller
public class ModelTest1 {

    // return "/WEB-INF/jsp/text.jsp"
    // return "forward:/WEB-INF/jsp/text.jsp"; 转发
    // return "redirect:/WEB-INF/jsp/text.jsp"; 重定向
    @RequestMapping("/m1/t1")
    public String test(Model model) {
        model.addAttribute("msg", "ModelTest1");
        return "redirect:/WEB-INF/jsp/text.jsp";
    }
}

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

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

12、SpringMVC:数据处理

12.1、处理提交数据

1、提交的域名城和处理方法的参数名一致

提交数据:http://localhost:8080/hello?name=kuangshen

处理方法:

@RequestMapping("/hello")
public String hello(String name){
    System.out.println(name);
    return "hello";
}

后台输出:kuangshen

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

提交数据:http://localhost:8080/hello?username=kuangshen

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

3、提交的是一个对象

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

1)实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String name;
    private int age;
}

2)提交数据:http://localhost:8080/user/t2?id=1&name=小猫咪&age=1

3)处理方法:

import com.kuang.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/user")
public class UserController {

    // 前端接收的是一个对象:id,name,age
    /*
    1、接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
    2、假设传递的是一个对象User,匹配User对象中的字段名:如果名字一致则OK,否则,匹配不到
    */
    @GetMapping("/t2")
    public String test2(User user){
        System.out.println(user);
        return "text";
    }
}

后台输出:User(id=1, name=小猫咪, age=1)

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

12.2、数据显示到前端

第一种:通过 ModelAndView,前面一直是如此,就不过多解释。

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 只要实现了 Controller 接口的类,说明这就是一个控制器了
public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "ControllerTest1");
        mv.setViewName("text");
        return mv;
    }
}

第二种:通过 ModelMap

@Controller
@RequestMapping("/user")
public class UserController {

    @GetMapping("/t3")
    public String test3(@RequestParam("username") String name, ModelMap model) {
        // 封装要显示到视图中的数据
        // 相当于req.setAttribute("name", name);
        model.addAttribute("name", name);
        System.out.println(name);
        return "text";
    }
}

第三种:通过 Model 【常用】

@Controller
@RequestMapping("/user")
public class UserController {

    // localhost:8080/user/t1?name=xxx
    @RequestMapping("/t1")
    public String test1(@RequestParam("username") String name, Model model) {
        // 1、接收前端参数
        System.out.println("接收到前端的参数为:" + name);
        // 2、将返回的结果传递给前端,Model
        model.addAttribute("msg", name);
        // 3、视图跳转
        return "text";
    }
}
12.2.1、对比

就对于新手而言既简单来说使用区别就是:

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

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

温习提示:请使用 80% 的时间打好扎实的基础,剩下 18% 的时间研究框架,2% 的时间去学点英文,框架的官方文档永远是最好的教程。

12.2.2、乱码问题

测试步骤:

1、我们可以在首页编写一个提交的表单

<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>

</body>
</html>

2、后台编写对应的处理类

@Controller
public class EncodingController {

    @PostMapping("/e/t1")
    public String test1(String name, Model model) {
        model.addAttribute("msg", name);
        return "text";
    }
}

3、输入中文测试,发现乱码

不得不说,乱码问题可遇不可求啊,在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题【我可没这么说】

以前乱码问题通过过滤器解决,而SpringMVC 给我们提供了一个过滤器,可以在 web.xml 中配置,修改了 xml 文件需要重启服务器!

<!-- 2、配置 SpringMVC 的乱码过滤 -->
<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>

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

页面乱码解决

<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="gb2312" %>

处理方法:

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

2、自定义过滤器

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

13、SpringMVC:JSON

什么是 JSON?

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

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

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

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

{"name":"kuangshen"}
{"age":"3"}
{"sex":"男"}

前后端分离时代:后端部署后端,提供接口,提供数据;

json

前端独立部署,负责渲染后端的数据;

很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至连谁是谁都不清楚,其实,可以这么理解:

  • JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串
// 编写一个JavaScript对象
var obj = { name: "狂神", age: 3, sex: "男"}; // 这是一个对象,注意键名也是可以使用引号包裹的
var json = '{ name: "狂神", age: 3, sex: "男"}'; // 这是一个 JSON 字符串,本质是一个字符串

JSON 和 JavaScript 对象互转

  • 要实现从 JSON 字符串转换为 JavaScript 对象,使用 JSON.parse()方法:
var obj = JSON.parse(json);
// 结果是{name: '狂神', age: 3, sex: '男'}
  • 要实现从 JavaScript 对象转为 JSON 字符串,使用 JSON.stringify()方法:
var json = JSON.stringify(user);
// 结果是{"name":"狂神","age":3,"sex":"男"}

代码测试:

1、新建一个 module,springmvc-day05-json,添加 web 的支持

2、在 web 目录下新建一个 json-1.html,编写测试内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        // 编写一个JavaScript对象
        var user = {
            name: "狂神",
            age: 3,
            sex: "男"
        };

        // 将 js 对象转换为 json 对象
        var json = JSON.stringify(user);
        console.log(json);

        console.log("======================")

        // 将 json 对象 转换为 JavaScript 对象
        var obj = JSON.parse(json);
        console.log(obj);

    </script>
</head>
<body>

</body>
</html>

3、在 IDEA 中使用浏览器测试,查看控制台输出!

13.1、Controller 返回 JSON 数据

  • Jackson 应该是目前比较好的 json 解析工具了
  • 当然工具不止这一个,比如还有阿里巴巴的 fastjson 等等
  • 我们这里使用 Jackson,使用它需要导入它的 jar 包
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.4.2</version>
</dependency>
  • 配置 SpringMVC 需要的配置

web.xml

<!-- 配置 DispatcherServlet:这个是 SpringMVC 的核心,请求分发器,前端控制器 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet 关联一个 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>

<!-- 2、配置 SpringMVC 的乱码过滤 -->
<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>

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
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让指定包下的注解生效,由 IOC 容器统一管理 -->
    <context:component-scan base-package="com.kuang.controller" />

    <!-- 让 SpringMVC 不处理静态资源   .css .js .html .mp3 .mp4-->
    <mvc:default-servlet-handler />

    <!--
    支持 mvc 注解驱动
        在 spring 中一般采用@RequestMapping 注解来完成映射关系
        要想使@RequestMapping 注解失效
        必须向上下文中注册DefaultAnnotationHandlerAdapter 实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入
     -->
    <mvc:annotation-driven />

    <!-- 视图解析器:DispatcherServlet 给他的 ModelAndView       模板引擎 Thymeleaf Freemarker...
     1、获取了 ModelAndView 的数据
     2、解析 ModelAndView 的视图名字
     3、拼接视图名字,找到对应的视图
     4、将数据渲染到这个视图上
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>
  • 随便编写一个 User 的实体类,然后我们去编写测试 Controller
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

    @RequestMapping("/j1")
    @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
    public String json1() throws JsonProcessingException {

        // jackson, ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        // 创建一个对象
        User user = new User("憨憨", 3, "男");
        String str = mapper.writeValueAsString(user);

        return str;
    }
}
  • 配置 Tomcat,启动测试一下!

http://localhost:8080/j1

  • 发现出现了乱码问题,我们需要设置一下它的编码格式为 utf-8,以及它返回的类型;
  • 通过 @RequestMapping 的 produces 属性来实现,修改下代码
@RequestMapping(value = "/j1", produces = "application/json;charset=utf-8")
  • 再次测试,http://localhost:8080/j1,乱码问题 OK!

注意:使用 json 记得处理乱码问题

13.2、代码优化

乱码统一解决

上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加,可以通过 Spring 配置统一指定,这样就不用每次都去处理了!

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

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

返回 json 字符串统一解决

在类上直接使用 @RestController,这样子,里面所有的方法都只会返回 json 字符串了,不用再每一个都添加 @ResponseBody!我们在前后端分离开发中,一般都使用 @RestController,十分便捷!

@RestController
public class UserController {

    @RequestMapping("/j1")
    // @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
    public String json1() throws JsonProcessingException {

        // jackson, ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        // 创建一个对象
        User user = new User("憨憨", 3, "男");
        String str = mapper.writeValueAsString(user);
        System.out.println(user);

        return str;
    }
}

// 组合拳:@RestController 配合 @RequestMapping;				@Controller 配合 @ResponseBody

启动 Tomcat 测试,结果都正常输出!

13.3、测试集合输出

增加一个新的方法。

@RestController
public class UserController {

    @RequestMapping("/j2")
    // @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
    public String json2() throws JsonProcessingException {

        // jackson, ObjectMapper
        ObjectMapper mapper = new ObjectMapper();

        List<User> userList = new ArrayList<>();

        User user1 = new User("憨憨", 3, "男");
        User user2 = new User("憨憨", 4, "男");
        User user3 = new User("憨憨", 5, "男");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        String str = mapper.writeValueAsString(userList);

        return str;
    }
}

运行结果:[{“name”:“憨憨”,“age”:3,“sex”:“男”},{“name”:“憨憨”,“age”:4,“sex”:“男”},{“name”:“憨憨”,“age”:5,“sex”:“男”}]

13.4、输出时间对象

增加一个新的方法

@RestController
public class UserController {

    @RequestMapping("/j3")
    // @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
    public String json3() throws JsonProcessingException {

        // jackson, ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        // 创建时间一个对象,java.util.Date
        Date date = new Date();
        //将我们的对象解析成为 json 格式
        String str = mapper.writeValueAsString();

        return str;
    }
}

运行结果:

  • 默认日期格式会变成一个数字,是1970年1月1日到当前日期的毫秒数!
  • Jackson 默认是会把时间转成 timestamps 形式

解决方案:取消 timestamps 形式,自定义时间格式

// 工具类JsonUtils
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;
}
// UserController
@RequestMapping("/j3")
// @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
public String json3() throws JsonProcessingException {

    Date date = new Date();
    return JsonUtils.getJson(date);
}

13.5、FastJson

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

fastjson 的 pom 依赖!


fastjson 三个主要的类:

  • 【JSONObject 代表 json 对象】
    • JSONObject 实现了 Map 接口,猜想 JSONObject 底层操作是由 Map 实现的。
    • JSONObject 对应 json 对象,通过各种形式的 get() 方法可以获取 json 对象中的数据,也可利用诸如 size(),isEmpty() 等方法获取 “键 : 值” 对的个数和判断是否为空。其本质是通过实现 Map 接口并调用接口中的方法完成的。
  • 【JSONArray 代表 json 对象数组】
    • 内部是有 List 接口中的方法来完成操作的。
  • 【JSON 代表 JSONObject 和 JSONArray 的转化】
    • JSON 类源码分析与使用
    • 仔细观察这些方法,主要是实现 json 对象,json 对象数组,javabean 对象,json 字符串之间的相互转化

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

// @Controller
@RestController
public class UserController {

    @RequestMapping("/j4")
    // @ResponseBody // 它就不会走视图解析器,会直接返回一个字符串
    public String json4() throws JsonProcessingException {

        List<User> userList = new ArrayList<>();

        User user1 = new User("憨憨", 3, "男");
        User user2 = new User("憨憨", 4, "男");
        User user3 = new User("憨憨", 5, "男");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);

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

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

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

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

        return "test";
    }
}

14、SpringMVC:整合 SSM

14.1、环境要求

  • IDEA
  • MySQL 5.7.19
  • Tomcat 9
  • Maven 3.6

要求:

  • 需要熟练掌握 MySQL 数据库,Spring,JavaWeb 及 MyBatis 只是,简单的前端知识。

14.2、数据库环境

创建一个存放书籍数据的数据库表

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, '从进门到进牢');

14.3、基本环境搭建

1、新建一 Maven 项目! ssmbuild,添加 web 的支持

2、导入相关的 pom 依赖!

<!-- 依赖 junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring -->
<dependencies>
    <!-- junit -->
    <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; dbcp -->
    <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.6</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>

    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.20</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.20</version>
    </dependency>
</dependencies>

3、Maven 资源过滤设置

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>

        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

4、建立基本结构和配置框架!

  • com.kuang.pojo
  • com.kuang.dao
  • com.kuang.service
  • com.kuang.controller
  • mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration核心配置文件 -->
<configuration>

    <!--  引入外部配置文件  -->
    <properties resource="db.properties" />

    <settings>
        <!-- 标准日志  注意中间的i是大写  -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 是否开启自动驼峰命名规则(camel case) 映射  -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!--  可以给实体类起别名  -->
    <typeAliases>
        <package name="com.kuang.pojo" />
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.kuang.dao.BlogMapper" />
    </mappers>

</configuration>
  • applicationContext.html
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 引入 Mybatis 核心配置 -->
    <import resource="spring-dao.xml" />

    <!---->
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSession" />
    </bean>

    <bean id="userMapper2" class="com.kuang.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>

</beans>

14.4、Mybatis 层编写

1、数据库配置文件 database.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8
jdbc.username=root
jdbc.password=hashiqi

2、IDEA 关联数据库

3、编写 MyBatis 的核心配置文件

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

    <settings>
        <!--  标准日志工厂实现 注意中间的i是大写 建议官网复制  -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    <!-- 配置数据源,交给 Spring 去做 -->
    <typeAliases>
        <package name="com.kuang.pojo"/>
    </typeAliases>

    <mappers>
        <mapper class="com.kuang.dao.BookMapper" />
    </mappers>

</configuration>

4、编写数据库对应的实体类 com.kuang.pojo.Books,偷懒就使用 lombok插件!

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {

    private int bookID;
    private String bookName;
    private int bookCounts;
    private String detail;
}

5、编写 Dao 层的 Mapper 接口!

public interface BookMapper {

    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookById(@Param("bookId") int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookById(@Param("bookId") int id);

    // 查询全部的书
    List<Books> queryAllBook();

    // 通过书籍名称查询书籍
    Books queryBookByBookName(@Param("bookName") String bookName);
}

6、编写接口对应的 Mapper.xml 文件,需要导入 MyBatis 的包;

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.dao.BookMapper">
    <insert id="addBook" parameterType="Books">
        insert into mybatis.books (bookName, bookCounts, detail) values (#{bookName}, #{bookCounts}, #{detail})
    </insert>
    
    <delete id="deleteBookById" parameterType="int">
        delete from mybatis.books where bookID = #{bookId}
    </delete>
    
    <update id="updateBook" parameterType="Books">
        update mybatis.books set bookName=#{bookName},bookCounts=#{bookCounts},detail=#{detail} where bookID=#{bookID}
    </update>

    <select id="queryBookById" parameterType="int" resultType="Books">
        select * from mybatis.books where bookID=#{bookId}
    </select>

    <select id="queryAllBook" resultType="Books">
        select * from mybatis.books
    </select>

    <select id="queryBookByBookName" parameterType="String" resultType="Books">
        select * from mybatis.books where bookName=#{bookName}
    </select>
</mapper>

7、编写 Service 层的接口和实现类

接口:

import com.kuang.pojo.Books;

import java.util.List;

public interface BookService {

    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookById(int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookById(int id);

    // 查询全部的书
    List<Books> queryAllBook();

    // 通过书籍名称查询书籍
    Books queryBookByBookName(String bookName);
}

实现类:

import com.kuang.dao.BookMapper;
import com.kuang.pojo.Books;

import java.util.List;

public class BookServiceImpl implements BookService {

    // service层 调用 dao层:组合 Dao
    private BookMapper bookMapper;

    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    @Override
    public int addBook(Books books) {
        return bookMapper.addBook(books);
    }

    @Override
    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    @Override
    public int updateBook(Books books) {
        return bookMapper.updateBook(books);
    }

    @Override
    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    @Override
    public List<Books> queryAllBook() {
        return bookMapper.queryAllBook();
    }

    @Override
    public Books queryBookByBookName(String bookName) {
        return bookMapper.queryBookByBookName(bookName);
    }
}

OK,到此,底层需求操作编写完毕!

14.5、Spring 层编写

1、配置 Spring 整合 MyBaits,我们这里数据源使用 c3p0 连接池;

2、编写 Spring 整合 MyBAtis 的相关的配置文件:spring-dao.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Dao层 1 2 3 4 -->
    <!-- 1、管理数据库配置文件 -->
    <context:property-placeholder location="classpath:database.properties" />

    <!-- 2、连接池
        dbcp: 半自动化操作,不能自动连接
        c3p0: 自动化操作(自动化的加载配置文件,并且可以自动设置到对象中!)
        druid: 后续讲解
        hikari: 后续讲解
    -->
    <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" />
        <!-- 关闭连接后不自动 commit -->
        <property name="autoCommitOnClose" value="false" />
        <!-- 获取连接超时时间 -->
        <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" />
        <!-- 绑定 Mybatis 的配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
    </bean>

    <!-- 4、配置 dao 接口扫描包,动态的实现了 Dao 接口可以注入到 Spring 容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入 sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <!-- 要扫描的 dao 包 -->
        <property name="basePackage" value="com.kuang.dao" />
    </bean>

</beans>

3、Spring 整合 service 层

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 1、扫描 service 下的包 -->
    <context:component-scan base-package="com.kuang.service" />

    <!-- 2、将我们的所有业务类,注入到 Spring,可以通过配置,或者注解实现 -->
    <bean id="BookServiceImpl" class="com.kuang.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper" />
    </bean>

    <!-- 3、声明式事务配置 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="datasource" />
    </bean>

    <!-- 4、aop 事务支持 -->
    <!-- 结合 AOP 实现事务的织入 -->
    <!-- 配置事务通知:-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 给那些方法配置事务 -->
        <!-- 配置事务的传输特性:new       propagation="REQUIRED" 默认  -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置事务切入 -->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.kuang.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
    </aop:config>

</beans>

Spring 层搞定!再次理解一下,Spring 就是一个大杂烩,一个容器!

13.6、SpringMVC 层编写

1、web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- DispatcherServlet -->
    <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: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>

    <!-- 乱码过滤 -->
    <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 -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

2、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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1、注解驱动 -->
    <mvc:annotation-driven />

    <!-- 2、静态资源过滤 -->
    <mvc:default-servlet-handler />

    <!-- 3、扫描包:controller -->
    <context:component-scan base-package="com.kuang.controller" />

    <!-- 4、视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

3、Spring 配置整合文件,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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="classpath:spring-dao.xml" />
    <import resource="classpath:spring-service.xml" />
    <import resource="classpath:spring-mvc.xml" />

</beans>

配置文件,暂时结束!Controller 和 视图层编写

1、BOOKController 类编写,方法一:查询全部书籍

@Controller
@RequestMapping("/book")
public class BookController {

    // controller层 调 service层
    @Autowired
    @Qualifier("BookServiceImpl")
    private BookService bookService;

    // 查询全部的书籍,并且返回到一个书籍展示页面
    @RequestMapping("/allBook")
    public String list(Model model) {
        List<Books> booksList = bookService.queryAllBook();
        model.addAttribute("booksList", booksList);
        return "allBook";
    }
}

2、编写首页 index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
    <style>
      a {
        text-decoration: none;
        color: black;
        font-size: 18px;
      }
      h3 {
        width: 180px;
        height: 38px;
        margin: 100px auto;
        text-align: center;
        line-height: 38px;
        background: hotpink;
        border-radius: 5px;
      }
    </style>
  </head>
  <body>
    <h3>
      <a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
    </h3>
  </body>
</html>

3、书籍列表页面 allBook.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>
    <%-- BootStrap 美化界面 --%>
    <link href="https://cdn.staticfile.org/twitter-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 class="row">
                <div class="col-md-4 column">
                    <%-- toAddBook --%>
                    <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
                    <%-- 显示所有书籍 --%>
                    <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">全部书籍</a>
                </div>
                <div class="col-md-8 column">
                    <%-- 查询书籍 --%>
                    <form action="${pageContext.request.contextPath}/book/queryBook" method="" style="float: right" class="form-inline">
                        <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称" required>
                        <input type="submit" value="查询" class="btn btn-primary">
                    </form>
                </div>
            </div>
        </div>

        <div class="row clearfix">
            <div class="col-md-12 column">
                <table class="table table-hover table-striped">
                    <thead>
                        <tr>
                            <th>书籍编号</th>
                            <th>书籍名称</th>
                            <th>书籍数量</th>
                            <th>书籍详情</th>
                            <th>操作</th>
                        </tr>
                    </thead>

                    <%--  书籍从数据库中查询出来,从这个List 中遍历出来:foreach  --%>
                    <tbody>
                        <c:forEach var="book" items="${booksList}">
                            <tr>
                                <td>${book.bookID}</td>
                                <td>${book.bookName}</td>
                                <td>${book.bookCounts}</td>
                                <td>${book.detail}</td>
                                <td>
                                    <a class="btn btn-default" href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.bookID}">修改</a>
                                    &nbsp; | &nbsp;
                                    <a class="btn btn-danger" href="${pageContext.request.contextPath}/book/deleteBook/${book.bookID}">删除</a>
                                </td>
                            </tr>
                        </c:forEach>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</body>
</html>

4、BookController 类编写, 方法二:添加书籍

// 跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPage(){
    return "addBook";
}

// 添加书籍的请求
@RequestMapping("/addBook")
public String addBook(Books books) {
    System.out.println("addBook=>" + books);
    bookService.addBook(books);
    return "redirect:/book/allBook"; // 重定向到我们的@RequestMapping("/allBook")请求;
}

5、添加书籍页面: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>
    <link href="https://cdn.staticfile.org/twitter-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">
            <div class="form-group">
                <label>书籍名称:</label>
                <input type="text" name="bookName" class="form-control" required>
            </div>
            <div class="form-group">
                <label>书籍数量:</label>
                <input type="text" name="bookCounts" class="form-control" required>
            </div>
            <div class="form-group">
                <label>书籍描述:</label>
                <input type="text" name="detail" class="form-control" required>
            </div>
            <div class="form-group">
                <input type="submit" class="form-control" value="添加">
            </div>
        </form>
    </div>
</body>
</html>

6、BookController 类编写,方法三:修改书籍

    // 跳转到更新书籍页面
    @RequestMapping("/toUpdateBook")
    public String toUpdatePage(int id, Model model) {
        Books books = bookService.queryBookById(id);
        model.addAttribute("QBooks", books);
        return "updateBook";
    }

    // 修改书籍的请求
    @RequestMapping("/updateBook")
    public String updateBook(Books books) {
        System.out.println("updateBook=>" + books);
        bookService.updateBook(books);
        return "redirect:/book/allBook";
    }

7、修改书籍页面 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>
    <link href="https://cdn.staticfile.org/twitter-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">
        <div class="form-group">
            <input type="hidden" name="bookID" class="form-control" value="${QBooks.bookID}" >
        </div>
        <div class="form-group">
            <label>书籍名称:</label>
            <input type="text" name="bookName" class="form-control" value="${QBooks.bookName}" required>
        </div>
        <div class="form-group">
            <label>书籍数量:</label>
            <input type="text" name="bookCounts" class="form-control" value="${QBooks.bookCounts}" required>
        </div>
        <div class="form-group">
            <label>书籍描述:</label>
            <input type="text" name="detail" class="form-control" value="${QBooks.detail}" required>
        </div>
        <div class="form-group">
            <input type="submit" class="form-control" value="修改">
        </div>
    </form>
</div>
</body>
</html>

8、BookController 类编写,方法四:删除书籍

// 删除书籍
@RequestMapping("/deleteBook/{bookId}")
public String deleteBook(@PathVariable("bookId") int id) {
    bookService.deleteBookById(id);
    return "redirect:/book/allBook";
}

配置 Tomcat,进行运行!

到目前为止,这个 SSM 项目整合已经完全的OK了,可以直接运行进行测试!

13.7、小结

SSM 只是入门罢了!

15、SpringMVC:Ajax技术

15.1、简介

  • Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
  • Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
  • Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。
  • 在 2005 年,Google 通过其 Google Suggest 使 Ajax 变得流行起来。Google Suggest 能够自动帮你完成搜索单词。
  • Google Suggest 使用 Ajax 创造出来动态性极强的 web 界面:当您在谷歌的搜索框输入关键字使,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
  • 就和国内百度的搜索框一样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRsWsN2t-1678615703029)(C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20230110095401185.png)]

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

15.2、伪造 Ajax (太难听了 是借鉴)

我们可以使用前端的一个标签来伪造一个 Ajax 的样子,iframe 标签

1、新建一个 moudle:springmvc-day06-ajax,导入 web 支持!

2、编写一个 ajax-frame.html 使用 iframe 测试,感受下效果!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe测试体验页面无刷新</title>
    <script>
      function go() {
        // 所有的值变量,提前获取
        var url = document.getElementById("url").value;
        document.getElementById("iframe1").src=url;
      }
    </script>
</head>
<body>
  <div>
    <p>请输入地址:</p>
    <p>
      <input type="text" id="url" value="https://docs.spring.io/spring-framework/docs/">
      <input type="button" value="提交" onclick="go()">
    </p>
  </div>
  <div>
    <iframe id="iframe1" style="width: 100%;height: 500px;"></iframe>
  </div>
</body>
</html>

3、使用 IDEA 打开浏览器测试一下

利用 Ajax 可以做:

  • 注册时,输入用户名自动监测用户是否已经存在
  • 登录时,提示用户名密码错误
  • 删除数据行时,将行 ID 发送到后台,后台在数据库中删除,数据库删除成功后,在页面 DOM 中将数据行也删除。
  • … and so on!

15.3、JQuery.ajax

  • 纯 JS 原生实现 Ajax 这里我们不去讲解,直接使用 jquery 提供的,方便学习和使用,避免重复造轮子,但是可以去了解下 JS 原生 XMLHttpRequest!
  • Ajax 的核心是 XMLHTTPRequest 对象(XHR),XHR 为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。
  • jQuery 提供多个与 Ajax 有关的方法
  • 通过 jQuery Ajax 方法,能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON - 同时能够使这些外部数据直接载入网页的被选元素中。
  • jQuery 不是生产者,而是大自然搬运工。
  • jQuery Ajax 本质就是 XMLHttpRequest,对它进行了封装,方便调用!
jQuery.ajax(...) 
    部分常用参数:
       	 url:"发送请求(提交或读取数据)的地址", 
         dataType:"预期服务器返回数据的类型",  
         type:"请求方式", 
         async:"true/false",
         data:{发送到/读取后台(服务器)的数据},
         success:function(data){请求成功时执行},      
         error:function(){请求失败时执行}

我们来个简单的测试,使用最原始的 HttpServletResponse 处理,最简单,最通用

1、配置 web.xml 和 SpringMVC 的配置文件,复制上面案例即可【记得静态资源过滤和注解驱动配置上】

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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">

    <!-- 自动扫描包,让指定包下的注解生效,由 IOC 容器统一管理 -->
    <context:component-scan base-package="com.kuang.controller" />

    <!-- 静态资源过滤:让 SpringMVC 不处理静态资源   .css .js .html .mp3 .mp4-->
    <mvc:default-servlet-handler />

    <mvc:annotation-driven />

    <!-- 视图解析器:DispatcherServlet 给他的 ModelAndView       模板引擎 Thymeleaf Freemarker...
     1、获取了 ModelAndView 的数据
     2、解析 ModelAndView 的视图名字
     3、拼接视图名字,找到对应的视图
     4、将数据渲染到这个视图上
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

2、编写一个 AjaxController

import com.kuang.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@RestController
public class AjaxController {

    @RequestMapping("/t1")
    public String test() {
        return "hello";
    }

    @RequestMapping("/a1")
    public void a1(String name, HttpServletResponse response) throws IOException {
        System.out.println("a1:param=>" + name);
        if ("kuangshen".equals(name)) {
            response.getWriter().print("true");
        } else {
            response.getWriter().print("false");
        }
    }

    @RequestMapping("/a2")
    public List<User> a2() {
        List<User> userList = new ArrayList<>();

        // 添加数据
        userList.add(new User("狂神说Java", 1, "男"));
        userList.add(new User("狂神说前端", 1, "女"));
        userList.add(new User("狂神说全栈", 1, "男"));

        return userList;
    }
}

3、编写一个 test2.jsp 页面进行测试

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                /*
                $.post (url, param[可以省略], success)
                 */
                $.post("${pageContext.request.contextPath}/a2" ,function (data) {
                    // console.log(data);
                    var html = "<>";

                    for (let i = 0; i < data.length; i++) {
                        html += "<tr>" +
                            "<td>" + data[i].name + "</td>" +
                            "<td>" + data[i].age + "</td>" +
                            "<td>" + data[i].sex + "</td>" +
                            "</tr>"
                    }

                    $("#content").html(html)
                })
            })
        })
    </script>
</head>
<body>
    <input type="button" value="加载数据" id="btn">
    <table>
        <tr>
            <td>姓名</td>
            <td>年龄</td>
            <td>性别</td>
        </tr>
        <tbody id="content">
        <%-- 数据:后台--%>

        </tbody>
    </table>
</body>
</html>

成功!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ywgohi4q-1678615703030)(C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20230110144248384.png)]

15.4、Ajax验证用户名体验

1、先创建一个用户

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String name;
    private int age;
    private String sex;
}

2、在 applicationContext.xml 配置 JSON 乱码问题

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

3、创建一个 AjaxController

@RestController
public class AjaxController {
    @RequestMapping("/a3")
    public String a3(String name, String pwd) {
        String msg = "";
        if (name != null) {
            // admin 这些数据应该在数据库中查
            if ("admin".equals(name)) {
                msg = "OK";
            } else {
                msg = "用户名有误";
            }
        }
        if (pwd != null) {
            if ("123456".equals(pwd)) {
                msg = "OK";
            } else {
                msg = "密码有误";
            }
        }
        return msg;
    }
}

4、创建一个 login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.js"></script>
    <script>
        function a1() {
            $.post({
                url: "${pageContext.request.contextPath}/a3",
                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}/a3",
                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>

【记得处理 json 乱码问题】

测试一下效果,动态请求响应,局部刷新,就是如此!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEyWuc08-1678615703030)(C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20230110153707306.png)]

16、SpringMVC:拦截器

16.1、概述

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

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

过滤器

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

拦截器

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

16.2、自定义拦截器

那如何实现拦截器呢?

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

1、新建一个 Moudle,springmvc-day07-interceptor,添加 web 支持

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

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: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>

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

springmvc-servlet.xml 这里用 applicationContext.xml

<!-- 拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 包括这个请求下面的所有的请求!-->
        <mvc:mapping path="/user/**"/>
        <mvc:exclude-mapping path="/user/login"/>
        <bean class="com.kuang.config.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

注意这里的 <mvc:exclude-mapping path=“/user/login”/>

3、编写一个拦截器

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class MyInterceptor implements HandlerInterceptor {

    // return true:  执行下一个拦截器:放行
    // return true: 不执行下一个拦截器
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        // 放行:判断什么情况下登录

        // 登录页面也放行
        if (request.getRequestURI().contains("goLogin")) {
            return true;
        }
        // 第一次登录,也是没有 session
        if (session.getAttribute("userLoginInfo") != 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 {
//        System.out.println("============== 处理后 ================");
//    }
}

4、创建 /WEB-INF/jsp 下的 login.jsp 和 main.jsp 页面

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%-- 在 WEB-INF 下的所有页面或者资源,只能通过Controller,或者 servlet 进行访问 --%>
<h1>登录页面</h1>

<form action="${pageContext.request.contextPath}/user/login" method="post">
    用户名: <input type="text" name="username" />
    密码: <input type="text" name="password" />
    <input type="submit" value="提交">
</form>
</body>
</html>

main.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

配置 Tomcat 并导入 lib 进行测试!

狂神的拦截 感觉没听好! 是我的问题!!!先看看尚硅谷的 尚硅谷没讲到实例… 自己又看其他的视频,理解了下,

拦截器的三个方法:
preHandle(): 在控制器方法执行之前执行,其返回值表示对控制器方法的拦截(false)或放行(true)
postHandle(): 在控制器方法执行之后执行
afterComletion(): 在控制器方法执行之后,且渲染视图完毕之后执行

多个拦截器的执行顺序:和在 applicationContext.xml 中配置文件的顺序有关
preHandle() 按照配置的属性执行,而postHandle()和afterComletion按照配置的反序执行

若拦截器中有某个拦截器的preHandle()返回了false
拦截器的preHandle()返回false和它之前的拦截器的preHandle()都会执行
所有拦截器的postHandle()都不执行
拦截器的preHandle()返回false之前的拦截器的afterCompletion()会执行

17、SpringMVC:文件上传和下载

17.1、准备工作

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

前端表单要求:为了能上传文件,必须将表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

对表单中的 enctype 属性做个详细的说明:

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

一旦设置了 enctype 为 multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的 HTTP 响应。在 2003 年,Apache Software Foundation 发布了开源的 Commons FileUpload 组件,其很快成为 Servlet / JSP 程序员上传文件的最佳选择。

  • Servlet 3.0 规范已经提供方法来处理文件上传,但这种上传需要在 Servlet 中完成。
  • 而 SpringMVC 则提供了更简单的封装
  • SpringMVC 为文件上传提供了直接的支持,这种支持是用即插即用的 MultipartResolver 实现的。
  • SpringMVC 使用 Apache Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver。因此,SpringMVC 的文件上传还需要依赖 Apache Commons FileUpload 的组件

17.2、文件上传

一、导入文件上传的 jar 包,commons-fileupload,Maven 会自动帮我们导入它的依赖包 commons-io 包;

<dependencies>
    <!-- 文件上传 -->
    <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>
</dependencies>

二、配置 bean:multipartResolver

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

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

CommonsMultipartFile 的常用方法:

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

我们去实际测试一下

三、编写前端页面

<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file" />
    <input type="submit" value="upload">
</form>

四、Controller

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;

@RestController
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";
    }
}

五、测试上传文件,OK!

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

1、编写 Controller

@RestController
public class FileController {

    /*
    采用 file.Transto 来保存上传的文件
     */
    @RequestMapping("/upload2")
    public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        // 上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        // 上传文件地址
        System.out.println("上传文件保存地址:" + realPath);

        // 通过 CommonsMultipartFile 的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));

        return "redirect:/index.jsp";
    }
}

2、前端表单提交地址修改

3、访问提交测试,OK!

17.4、文件下载

文件下载步骤:

1、设置 response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流(先开后关)

代码实现:

@RestController
public class FileController {
    
    @RequestMapping(value = "/download")
    public String downloads(HttpServletResponse response, HttpServletRequest request) throws IOException {

        // 要下载的图片地址
        String path= request.getServletContext().getRealPath("/upload");
        String fileName = "蝴蝶忍.jpg";

        // 1、设置 response 响应头
        response.reset(); // 设置页面不缓存,清空 buffer
        response.setCharacterEncoding("UTF-8"); // 字符编码
        response.setContentType("multipart/form-data"); //二级制传输数据
        // 设置响应头
        response.setHeader("Content-Disposition",
                "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path, fileName);
        // 2、读取文件 -- 输入流
        InputStream is = new FileInputStream(file);
        // 3、写出文件 -- 输出流
        OutputStream os = response.getOutputStream();

        byte[] buffer = new byte[1024];
        int index = 0;
        // 4、执行 写出操作
        while ((index = is.read(buffer)) != -1) {
            os.write(buffer, 0, index);
            os.flush();
        }
        os.close();
        is.close();
        return null;
    }
}

SSM 复习重点:

  • 第一个 Mybatis 程序
  • ResultMap 结果集映射
  • DI
  • 代理模式
  • JavaConfig
  • 整合 Mybatis:事务
  • springMVC 执行流程
  • RestFul
  • 整合 SSM 项目

前端:

#逻辑

  • 判断
  • 循环

#事件

  • 浏览器事件:window document

  • Dom 事件: 增、删、遍历、修改节点元素内容

  • jQuery

  • Vue.js

  • React

#视图

  • html
  • css

#通信

  • xhr
  • ajax
  • axios

Java 全栈工程师

  • 后台开发:主打

  • 前端开发:htm、css、js、jQuery、Vue、React
    ring 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";
    

    }
    }


五、测试上传文件,OK!



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

1、编写 Controller

````java
@RestController
public class FileController {

    /*
    采用 file.Transto 来保存上传的文件
     */
    @RequestMapping("/upload2")
    public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        // 上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        // 上传文件地址
        System.out.println("上传文件保存地址:" + realPath);

        // 通过 CommonsMultipartFile 的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));

        return "redirect:/index.jsp";
    }
}

2、前端表单提交地址修改

3、访问提交测试,OK!

17.4、文件下载

文件下载步骤:

1、设置 response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流(先开后关)

代码实现:

@RestController
public class FileController {
    
    @RequestMapping(value = "/download")
    public String downloads(HttpServletResponse response, HttpServletRequest request) throws IOException {

        // 要下载的图片地址
        String path= request.getServletContext().getRealPath("/upload");
        String fileName = "蝴蝶忍.jpg";

        // 1、设置 response 响应头
        response.reset(); // 设置页面不缓存,清空 buffer
        response.setCharacterEncoding("UTF-8"); // 字符编码
        response.setContentType("multipart/form-data"); //二级制传输数据
        // 设置响应头
        response.setHeader("Content-Disposition",
                "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path, fileName);
        // 2、读取文件 -- 输入流
        InputStream is = new FileInputStream(file);
        // 3、写出文件 -- 输出流
        OutputStream os = response.getOutputStream();

        byte[] buffer = new byte[1024];
        int index = 0;
        // 4、执行 写出操作
        while ((index = is.read(buffer)) != -1) {
            os.write(buffer, 0, index);
            os.flush();
        }
        os.close();
        is.close();
        return null;
    }
}

SSM 复习重点:

  • 第一个 Mybatis 程序
  • ResultMap 结果集映射
  • DI
  • 代理模式
  • JavaConfig
  • 整合 Mybatis:事务
  • springMVC 执行流程
  • RestFul
  • 整合 SSM 项目

前端:

#逻辑

  • 判断
  • 循环

#事件

  • 浏览器事件:window document

  • Dom 事件: 增、删、遍历、修改节点元素内容

  • jQuery

  • Vue.js

  • React

#视图

  • html
  • css

#通信

  • xhr
  • ajax
  • axios

Java 全栈工程师

  • 后台开发:主打
  • 前端开发:htm、css、js、jQuery、Vue、React
  • 运维:项目发布;服务器如何运行一个项目?Linux
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值