Spring MVC 全解析:从核心原理到 SSM 整合实战 (附完整源码)
文章目录
- 写在前面
- 1. 回顾MVC架构
- 1.1 什么是MVC
- 1.2 Model1 与 Model2 时代
- Model1 时代
- Model2 时代
- 1.3 回顾Servlet
- 1.4 架构演进:MVVM 扫盲
- 2. 什么是Spring MVC
- 2.1 概述
- 2.2 中心控制器:DispatcherServlet
- 2.3 Spring MVC 执行原理
- 3. 第一个 Spring MVC 程序:HelloSpring
- 3.1 配置版实现
- 3.2 注解版实现
- 3.3 小结
- 4. Controller 与 RESTful 风格
- 4.1 `Controller` 接口实现
- 4.2 `@Controller` 注解
- 4.3 `@RequestMapping` 注解
- 4.4 RestFul 风格详解
- 4.5 程序员的锦囊妙计:小黄鸭调试法
- 5. 数据处理与页面跳转
- 5.1 ModelAndView
- 5.2 原生 Servlet API
- 5.3 Spring MVC 的转发与重定向
- 5.4 处理前端提交数据
- 5.5 数据显示到前端
- 5.6 乱码问题解决方案
- 6. JSON 交互处理
- 6.1 什么是 JSON
- 6.2 Jackson 框架使用
- 6.3 FastJson 框架简介
- 7. 整合 SSM 框架实战
- 7.1 基本环境搭建
- 7.2 MyBatis 层配置
- 7.3 Spring 业务层配置
- 7.4 Spring MVC 表现层配置
- 7.5 案例功能实现
- 8. Ajax 技术
- 8.1 初识 Ajax
- 8.2 结合 jQuery 使用 Ajax
- 8.3 Ajax异步加载数据
- 4.Ajax验证用户名体验
- 5.获取baidu接口Demo
- 9. 拦截器 (Interceptor)
- 9.1 概述与区别
- 9.2 自定义拦截器
- 9.3 实战:用户登录验证
- 10. 文件上传和下载
- 10.1 准备工作
- 10.2 文件上传
- 报错:Cannot resolve method ‘getServletContext’ in ‘HttpServletRequest’
- 3.文件下载
- 参考
写在前面
你好,我是 ZzzFatFish。
这份笔记记录了从 Web 开发最基础的 MVC 架构模式,到 Spring MVC 核心原理与实战的全过程。内容涵盖了 Servlet 回顾、Spring MVC 的执行流程、注解式开发、RESTful 风格、JSON 数据交互、SSM 框架整合、AJAX 异步通信、拦截器应用以及文件上传下载等关键技术点。
这不仅是一份按部就班的学习记录,更是一份包含了大量实战代码、常见问题排查和个人心得(比如有趣的小黄鸭调试法)的实践总结。
无论你是正在入门 Spring MVC 的新手,还是希望系统性回顾知识点的开发者,都希望这份详尽的笔记能为你提供有价值的参考和帮助。
💡 配套源码已上传至 Gitee
为了方便大家学习和实践,本文涉及的所有代码和配置都已整理并上传至我的 Gitee 仓库。建议大家 clone 下来,边看文章边动手敲一遍,效果更佳!
仓库地址:https://gitee.com/zzzfatfish/springmvc-test
1. 回顾MVC架构
1.1 什么是MVC
MVC 是一种经典的软件架构模式,其核心思想在于解耦,即将应用程序的不同职责分离,使代码结构更清晰、更易于维护。
MVC = Model (模型) + View (视图) + Controller (控制器)
- 模型 (Model):负责应用程序的数据和业务逻辑。它既包含用于存储数据的实体对象(JavaBean),也包含处理具体业务的服务类。
- 视图 (View):负责数据的展示,即用户界面。它向用户展示模型中的数据,并将用户的操作(如点击按钮、填写表单)传递给控制器。
- 控制器 (Controller):作为模型和视图之间的协调者。它接收来自视图的请求,决定调用哪个模型进行处理,然后将处理结果更新到视图上。
MVC 的主要作用是降低视图与业务逻辑之间的耦合度。它并非一种具体的设计模式,而是一种宏观的架构思想。
在 Java Web 中,基于 Servlet 的 MVC 模式实现如下:
- 模型:由 JavaBean(实体模型)和普通的 Service/DAO 类(业务模型)组成。
- 视图:通常由 JSP 页面承担,负责数据显示和用户交互。
- 控制器:由 Servlet 承担,接收 HTTP 请求,并进行分发和协调。
其经典流程如下图所示:
最典型的原生 MVC 就是 JSP + Servlet + JavaBean 的模式。
1.2 Model1 与 Model2 时代
Model1 时代
在 Web 开发早期,普遍采用 Model1 架构。它主要分为视图层和模型层,JSP 页面同时承担了视图(View)和控制器(Controller)的双重职责。
- 优点:架构简单,适合小型项目快速开发。
- 缺点:JSP 职责过重,视图逻辑与控制逻辑混杂,导致代码重用性低,难以维护和扩展。
Model2 时代
为了解决 Model1 的弊端,Model2 架构应运而生。它引入了独立的控制器(Servlet),实现了彻底的 MVC 分离。
职责分析:
- Controller (控制器):获取表单数据,调用业务逻辑,并决定转向哪个页面。
- Model (模型):处理业务逻辑,保存数据状态。
- View (视图):纯粹负责页面显示。
Model2 模式提高了代码的复用率和项目的扩展性,大大降低了维护成本,是现代 Web 框架的基础。
1.3 回顾Servlet
为了更好地理解 Spring MVC,我们先用原生的 Servlet 来实现一个简单的 MVC 程序。
新建一个Maven工程当做父工程!pom依赖!
<!-- 导入依赖 -->
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</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>
- 建立一个Moudle:springmvc-01-servlet , 添加Web app的支持!
在 idea 中没有找到 add framework support (添加框架支持) 选项-CSDN博客
1. 项目搭建与依赖
引入 servlet-api
和 jsp-api
依赖。
<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>
2. 编写 Servlet (Controller)
创建一个 HelloServlet
类,用于接收请求、处理逻辑并转发到视图。
package com.github.subei.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 {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1. 获取前端参数String method = req.getParameter("method");if ("add".equals(method)) {req.getSession().setAttribute("msg", "执行了 add 方法");}if ("delete".equals(method)) {req.getSession().setAttribute("msg", "执行了 delete 方法");}// 2. 调用业务逻辑层 (此处省略)// 3. 视图转发req.getRequestDispatcher("/WEB-INF/jsp/Hello.jsp").forward(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
3. 编写 JSP (View)
在 WEB-INF/jsp/
目录下创建 jsp 用于显示结果。
Hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Servlet Result</title>
</head>
<body><h3>${msg}</h3>
</body>
</html>
Form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><form action="/hello" method="post"><input type="text" name="method"><input type="submit">
</form></body>
</html>
4. 注册 Servlet (web.xml
)
在 web.xml
中配置 Servlet 的映射关系。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.github.subei.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping></web-app>
5. 启动测试
配置 Tomcat 并启动,访问 http://localhost:8080/your_project/hello?method=add
,即可看到 Servlet 处理后的结果。
- http://localhost:8080/spring_01/hello?method=add
- http://localhost:8080/spring_01/hello?method=delete
现代 MVC 框架的核心任务就是简化上述流程,包括:
- 将 URL 映射到 Java 类或方法。
- 自动封装用户提交的数据。
- 解耦业务处理与视图渲染。
- 将响应数据渲染到 JSP/HTML 等视图上。
1.4 架构演进:MVVM 扫盲
在 MVC 的基础上,前端领域又演化出了 MVVM(Model-View-ViewModel)模式,它本质上是 MVC 的改进版。
在传统 MVC 中,Controller 的职责过于繁重,既要处理业务逻辑,又要负责数据转换和视图更新,导致 Controller 变得臃肿不堪。
MVVM 引入了 ViewModel
这一关键角色,它充当了视图(View)和模型(Model)之间的“适配器”和“粘合剂”。
- View 层:包含视图(UIView)和控制器(UIViewController),职责更纯粹,只负责展示和交互。
- ViewModel 层:持有 Model,并暴露属性与 View 中的 UI 元素一一对应。它处理所有与 UI 相关的逻辑,极大地为 Controller 减负。
- Model 层:依然是纯粹的数据模型。
- Binder:MVVM 的灵魂。它在 View 和 ViewModel 之间建立了双向数据绑定,当 ViewModel 的数据变化时,View 会自动更新;反之,当用户的操作导致 View 变化时,ViewModel 的数据也会同步改变。
Vue.js、Angular 等现代前端框架就是 MVVM 模式的典型实现。
2. 什么是Spring MVC
2.1 概述
Spring MVC 是 Spring Framework 的一部分,是一个基于 Java 实现 MVC 思想的轻量级 Web 框架。
Spring MVC 的核心特点:
- 轻量级:核心库小,易于学习和使用。
- 高效:基于请求-响应模式的 MVC 框架。
- 无缝集成:与 Spring IoC/AOP 完美结合,享受 Spring 生态的全部优势。
- 约定优于配置:提供大量默认配置,简化开发。
- 功能强大:支持 RESTful、数据验证、格式化、国际化、拦截器等高级功能。
- 简洁灵活:支持基于注解的开发,代码非常简洁。
最重要的一点:社区庞大,生态成熟,使用者和使用公司众多。
2.2 中心控制器:DispatcherServlet
Spring MVC 框架是以请求为驱动,围绕一个中心 Servlet——DispatcherServlet
来设计和工作的。DispatcherServlet
继承自 HttpServlet
,是整个流程的入口和调度中心,负责接收所有请求并将其分发给不同的处理器(Controller)。
所有请求都会先经过这个前置控制器,再由它来协调各个组件完成后续工作。
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。
2.3 Spring MVC 执行原理
下图展示了 Spring MVC 处理一个请求的完整流程。实线表示由框架自动完成,虚线表示需要开发者编写的部分。
执行流程简要分析:
- 用户发送请求,
DispatcherServlet
(前置控制器)接收请求。 DispatcherServlet
调用HandlerMapping
(处理器映射器),根据请求 URL 查找对应的Handler
(即 Controller 方法)。HandlerMapping
返回一个HandlerExecutionChain
(执行链,包含 Handler 和拦截器)给DispatcherServlet
。DispatcherServlet
调用HandlerAdapter
(处理器适配器)。HandlerAdapter
按照特定规则去执行Handler
(调用 Controller 方法)。- Controller 方法执行业务逻辑,并返回一个
ModelAndView
对象(包含模型数据和视图名)。 HandlerAdapter
将ModelAndView
返回给DispatcherServlet
。DispatcherServlet
调用ViewResolver
(视图解析器),将逻辑视图名解析为具体的视图对象(如 JSP)。ViewResolver
返回具体的View
对象给DispatcherServlet
。DispatcherServlet
对View
进行渲染(将模型数据填充到视图中)。DispatcherServlet
将最终渲染好的视图响应给用户。
3. 第一个 Spring MVC 程序:HelloSpring
3.1 配置版实现
这是早期 Spring MVC 的开发方式,通过实现 Controller
接口和 XML 配置来工作。
1. 项目搭建
创建一个 Maven Web 模块,并确保已引入 spring-webmvc
依赖。
springmvc-02-hellomvc
2. 配置 web.xml
注册 DispatcherServlet
,并指定 Spring MVC 配置文件的位置。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!-- 1. 注册 DispatcherServlet --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联 Spring MVC 配置文件 --><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><!-- 2. 映射所有请求到 DispatcherServlet --><!-- / 匹配所有请求 (不包括 .jsp) --><!-- /* 匹配所有请求 (包括 .jsp,不推荐) --><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
3. 编写 springmvc-servlet.xml
编写SpringMVC 的 配置文件!名称:springmvc-servlet.xml : [servletname]-servlet.xml
配置 Spring 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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 1. 处理器映射器: 按 bean 的 name (URL) 查找 Handler --><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><!-- 2. 处理器适配器: 执行实现了 Controller 接口的 Handler --><bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/><!-- 3. 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!-- 视图前缀 --><property name="prefix" value="/WEB-INF/jsp/"/><!-- 视图后缀 --><property name="suffix" value=".jsp"/></bean><!-- 4. 注册 Handler (Controller) --><!-- bean 的 name "/hello" 就是请求的 URL --><bean name="/hello" class="com.github.subei.controller.HelloController"/>
</beans>
4. 编写 Controller
创建一个实现 Controller
接口的类。
编写要操作业务Controller ,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图;
package com.github.subei.controller;import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class HelloController implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {// 创建模型和视图对象ModelAndView mv = new ModelAndView();// 封装数据 (Model)mv.addObject("msg", "Hello, SpringMVC! (Config Version)");// 设置视图名 (View)mv.setViewName("hello"); // 对应 /WEB-INF/jsp/hello.jspreturn mv;}
}
5. 编写 JSP 视图 (hello.jsp
)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>HelloSpringMVC</title>
</head>
<body><h3>${msg}</h3>
</body>
</html>
6. 启动测试
配置 Tomcat,启动后访问 http://localhost:8080/your_project/hello
即可看到结果。
常见问题与排错
java: 程序包org.springframework.web.servlet不存在
:通常是 Maven 依赖问题或 IDEA 配置问题。确保spring-webmvc
依赖已正确下载,并检查项目的 Artifacts 配置,确保依赖包已添加到WEB-INF/lib
目录下。- 404 Not Found:原因很多,常见于 URL 拼写错误、Controller 未注册为 Bean、
web.xml
配置错误、Tomcat 发布路径配置问题等。- 笔者心得:因为 IDEA 2020.1 版本不稳定导致 404 问题排查了半个月,更换到 2020.2 版本后问题迎刃而解!工具链的稳定性至关重要。
3.2 注解版实现
注解是现代 Spring MVC 开发的主流方式,它极大地简化了配置。
1. web.xml
配置 (与配置版相同)
同样需要注册 DispatcherServlet
并关联配置文件。
新建一个Moudle,springmvc-03-hello-annotation ,添加web支持!
2. 配置 Maven 资源过滤
为防止 .xml
等资源文件在打包时被忽略,建议在 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>
在pom.xml文件引入相关的依赖:主要有Spring框架核心库、Spring MVC、servlet , JSTL等。在父依赖中已经引入了!
配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!--1.注册servlet--><servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--通过初始化参数指定SpringMVC配置文件的位置,进行关联--><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><!--所有请求都会被springmvc拦截 --><servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
- 注意/ 和 /* 的区别:< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。< url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
- 注意web.xml版本问题,要最新版!
- 注册DispatcherServlet
- 关联SpringMVC的配置文件
- 启动级别为1
- 映射路径为 / 【不要用/*,会404】
3. 编写 springmvc-servlet.xml
(注解版)
添加Spring MVC配置文件
在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:
使用 context:component-scan
和 mvc:annotation-driven
来开启注解支持。
<?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="..."><!-- 1. 自动扫描包,让指定包下的注解生效 --><context:component-scan base-package="com.github.subei.controller"/><!-- 2. 不处理静态资源,交由 Tomcat 默认的 Servlet 处理 --><mvc:default-servlet-handler /><!-- 3. 开启 MVC 注解驱动 --><!-- 它会自动注册默认的 HandlerMapping 和 HandlerAdapter,是 @RequestMapping 等注解生效的基础 --><!--支持mvc注解驱动在spring中一般采用@RequestMapping注解来完成映射关系要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。--><mvc:annotation-driven /><!-- 4. 视图解析器 (与配置版相同) --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean>
</beans>
4. 编写 Controller (注解版)
使用 @Controller
和 @RequestMapping
注解。
package com.github.subei.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
@RequestMapping("/HelloController") // 类级别的请求路径
public class HelloController {// 方法级别的请求路径// 真实访问地址: /your_project/HelloController/hello@RequestMapping("/hello")public String sayHello(Model model) {// 向模型中添加属性 msg,可以在 JSP 中获取model.addAttribute("msg", "Hello, SpringMVC! (Annotation Version)");// 返回视图名// WEB-INF/jsp/hello.jspreturn "hello";}
}
@Controller
:将该类声明为 Spring IoC 容器管理的一个控制器 Bean。@RequestMapping
:映射 URL 请求到具体的处理方法。Model
参数:用于在控制器和视图之间传递数据。- 返回
String
:该字符串会被视图解析器解析为具体的视图路径。
在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;
可以通过EL表示取出Model中存放的值,或者对象;
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>SpringMVC</title> </head> <body> ${msg} </body> </html>
5. 启动测试
访问 http://localhost:8080/your_project/HelloController/hello
即可。
3.3 小结
Spring MVC 核心配置步骤:
- 创建 Web 项目并导入依赖。
- 在
web.xml
中注册DispatcherServlet
。- 创建 Spring MVC 核心配置文件。
- 配置处理器映射器、处理器适配器和视图解析器。
- 编写 Controller 类来处理请求。
- 创建 JSP 视图来展示数据。
- 配置 Tomcat 并测试。
注解驱动的优势:
在现代开发中,我们通常只需要手动配置视图解析器,而处理器映射器和处理器适配器通过开启注解驱动 (<mvc:annotation-driven />
) 即可自动配置,大大简化了 XML 文件。
4. Controller 与 RESTful 风格
4.1 Controller
接口实现
这是较老的一种方式,控制器类必须实现 org.springframework.web.servlet.mvc.Controller
接口。
package com.github.subei.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 ControllerTest implements Controller {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = new ModelAndView();mv.addObject("msg", "实现了 Controller 接口");mv.setViewName("test");return mv;}
}
然后在 XML 中通过 <bean name="/t1" ... />
的方式注册。
缺点:一个控制器类只能处理一个请求(一个
handleRequest
方法),如果要处理多个请求,需要定义多个 Controller 类,非常繁琐。
4.2 @Controller
注解
使用 @Controller
注解是目前的主流方式。Spring 会通过组件扫描 (<context:component-scan>
) 找到并注册这些控制器。
package com.github.subei.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;@Controller // 将该类声明为一个控制器
public class ControllerTest2 {@RequestMapping("/t2") // 映射请求路径public String index(Model model) {// Spring MVC 会自动实例化一个 Model 对象用于向视图传值model.addAttribute("msg", "使用了 @Controller 注解");// 返回视图位置return "test";}
}
这种方式下,一个控制器类可以包含多个处理方法,每个方法通过
@RequestMapping
映射不同的 URL,实现了逻辑的聚合,更加灵活和高效。
4.3 @RequestMapping
注解
@RequestMapping
是 Spring MVC 中最核心的注解之一,用于将 URL 请求映射到控制器类或处理方法上。
- 用于类上:作为父路径,类中所有方法的请求路径都在这个父路径之下。
- 用于方法上:定义具体的请求路径。
@Controller
@RequestMapping("/s1") // 父路径
public class ControllerTest3 {@RequestMapping("/y1") // 子路径public String test(Model model) {model.addAttribute("msg", "访问了 s1/y1");return "test";}
}
访问路径为:
http://localhost:8080/your_project/s1/y1
4.4 RestFul 风格详解
REST (Representational State Transfer) 是一种软件架构风格,而非标准或协议。它强调通过统一的接口(URL)和标准的 HTTP 方法(GET, POST, PUT, DELETE)来对网络资源进行操作。
HTTP 方法 | 传统 URL | RESTful URL | 操作 |
---|---|---|---|
GET | /item/queryItem.action?id=1 | /item/1 | 查询 |
POST | /item/saveItem.action | /item | 新增 |
PUT | /item/updateItem.action | /item | 更新 |
DELETE | /item/deleteItem.action?id=1 | /item/1 | 删除 |
在 Spring MVC 中,我们通过 @PathVariable
注解来支持 RESTful 风格的 URL。
示例:
@Controller
public class RestFulController {// 传统方式: http://localhost:8080/add?p1=1&p2=9// RESTful方式: http://localhost:8080/add/1/9@RequestMapping("/add/{p1}/{p2}")public String index(@PathVariable int p1, @PathVariable String p2, Model model) {String result = p1 + p2;model.addAttribute("msg", "RESTful 结果:" + result);return "test";}
}
@PathVariable
注解可以将 URL 模板中的变量 ({p1}
) 绑定到方法的参数上。
使用 method
属性指定请求类型
@RequestMapping
的 method
属性可以限定处理的 HTTP 请求类型。
// 只处理 GET 请求
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String index2(Model model) {model.addAttribute("msg", "这是一个 GET 请求");return "test";
}
如果使用非 GET 方式访问,服务器会返回 405 Method Not Allowed 错误。
为了简化,Spring 提供了更具体的组合注解:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
它们分别是 @RequestMapping(method = ...)
的快捷方式,语义更清晰。
4.5 程序员的锦囊妙计:小黄鸭调试法
这是一个非常有效且有趣的调试技巧,源自《程序员修炼之道》。
核心思想:当你遇到一个棘手的 Bug 时,找一个(不存在的)同事——比如一只橡皮小黄鸭,然后耐心地、一行一行地向它解释你的代码是做什么的,以及为什么这么做。
在这个向外解释的过程中,你被迫整理自己的思路,审视每一行代码的逻辑。很多时候,当你讲到某一步时,你会突然发现:“哦!原来是这里错了!”
下次卡住时,不妨试试这个方法。
5. 数据处理与页面跳转
5.1 ModelAndView
这是最经典的方式,一个 ModelAndView
对象既包含模型数据,也包含视图信息。
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = new ModelAndView();mv.addObject("msg", "使用 ModelAndView"); // 添加模型数据mv.setViewName("test"); // 设置视图名return mv;
}
5.2 原生 Servlet API
在 Controller 方法中,可以直接声明 HttpServletRequest
和 HttpServletResponse
参数,Spring MVC 会自动注入它们。这允许我们使用原生的 Servlet API 进行操作,但通常不推荐,因为它会增加与 Servlet API 的耦合。
@Controller
public class ModelTest1 {@RequestMapping("/m2/t3")public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {// 手动转发req.setAttribute("msg", "使用原生 Servlet API 转发");req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, rsp);}
}
5.3 Spring MVC 的转发与重定向
Spring MVC 提供了更简洁的方式来实现页面的转发和重定向,只需在返回的视图字符串前加上特定前缀即可。
@Controller
public class ResultSpringMVC {@RequestMapping("/p1/t1")public String test4(Model model) {model.addAttribute("msg", "转发到 test 页面");// 转发 (forward)// 配合视图解析器,只需返回视图名return "test"; }@RequestMapping("/p1/t2")public String test5(Model model) {model.addAttribute("msg", "重定向到 index.jsp");// 重定向 (redirect)// 不经过视图解析器,需要写完整路径return "redirect:/index.jsp";}
}
- 转发:
return "viewName";
或return "forward:/WEB-INF/jsp/view.jsp";
- URL 地址不变,请求是一次。
- 可以共享
request
中的数据。
- 重定向:
return "redirect:/path/to/resource";
- URL 地址会改变,请求是两次。
- 无法共享
request
中的数据。
5.4 处理前端提交数据
Spring MVC 能够非常方便地将前端请求中的参数绑定到 Controller 方法的参数上。
1. 参数名与方法参数名一致
如果请求参数名与方法参数名相同,Spring MVC 会自动绑定。
URL:
/user/t1?name=subei
@GetMapping("/t1")
public String test(String name, Model model) {// 此时 name 的值就是 "subei"System.out.println("接收到参数: " + name);model.addAttribute("msg", "接收到的 name: " + name);return "test";
}
2. 参数名不一致 (@RequestParam
)
使用 @RequestParam("requestParamName")
来指定映射关系。
URL:
/user/t2?username=subei
@GetMapping("/t2")
public String hello(@RequestParam("username") String name, Model model) {// 此时 name 的值也是 "subei"System.out.println("接收到参数: " + name);model.addAttribute("msg", "接收到的 username: " + name);return "test";
}
3. 提交的是一个对象
如果前端提交的参数与一个 Java 对象的属性名一一对应,可以直接使用该对象作为方法参数,Spring MVC 会自动将参数封装到对象中。
URL:
/user/t3?id=1&name=subei&age=21
// User.java 实体类
public class User {private int id;private String name;private int age;// ...getters and setters
}// Controller 方法
@GetMapping("/t3")
public String user(User user, Model model) {// Spring MVC 会自动创建 User 对象并注入 id, name, ageSystem.out.println("接收到对象: " + user);model.addAttribute("msg", "接收到的 User 对象: " + user);return "test";
}
5.5 数据显示到前端
将数据从 Controller 传递到 View 有多种方式:
方式 | 描述 |
---|---|
ModelAndView | 既包含模型数据 (addObject ),也包含视图信息 (setViewName )。 |
Model | 一个接口,提供了 addAttribute 方法,专注于传递数据。 |
ModelMap | Model 的实现类,本质是一个 LinkedHashMap ,功能与 Model 类似。 |
在注解驱动的开发中,使用
Model
作为方法参数是最常见和推荐的方式。
5.6 乱码问题解决方案
在处理 POST 请求中的中文字符时,经常会遇到乱码问题。Spring MVC 提供了 CharacterEncodingFilter
来统一解决。
在 web.xml
中配置该过滤器:
<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>
注意:对于 GET 请求的乱码,此过滤器可能支持不佳,通常需要在 Tomcat 的
server.xml
中配置<Connector URIEncoding="UTF-8" />
。
6. JSON 交互处理
6.1 什么是 JSON
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,因其简洁、清晰、易于解析的特点,已成为现代 Web 应用(尤其是前后端分离架构)数据传输的事实标准。
- 本质:一个遵循特定格式的字符串。
- 语法:
- 对象用花括号
{}
表示。 - 数组用方括号
[]
表示。 - 数据为 “键/值” 对,键名必须用双引号包裹。
- 对象用花括号
JavaScript 对象与 JSON 字符串的互转:
// JS 对象
var user = { name: "subei", age: 4 };// JS 对象 -> JSON 字符串
var jsonStr = JSON.stringify(user); // '{"name":"subei","age":4}'// JSON 字符串 -> JS 对象
var userObj = JSON.parse(jsonStr); // { name: "subei", age: 4 }
6.2 Jackson 框架使用
Jackson 是 Spring MVC 默认使用的 JSON 处理库,功能强大且性能优异。
1. 引入依赖
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.1</version>
</dependency>
2. 使用 @ResponseBody
返回 JSON
在 Controller 方法上添加 @ResponseBody
注解,Spring MVC 会自动将方法的返回值(通常是 Java 对象或集合)通过 Jackson 转换为 JSON 字符串,并直接写入 HTTP 响应体中,而不是去查找视图。
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.subei.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 {User user = new User("哇哈哈4号", 22, "man");// Spring MVC 在引入 Jackson 依赖后,会自动处理对象到 JSON 的转换// 为了演示,这里手动转换,实际开发中直接 return user 即可return new ObjectMapper().writeValueAsString(user);}@RequestMapping("/j2")@ResponseBodypublic List<User> json2() {List<User> userList = new ArrayList<>();// ... 添加用户到列表return userList; // Spring MVC 会自动将 List 转换为 JSON 数组}
}
3. 乱码问题及代码优化
@ResponseBody
乱码:可以通过在@RequestMapping
中设置produces
属性来解决。@RequestMapping(value = "/j1", produces = "application/json;charset=utf-8")
- 统一配置解决乱码:在
springmvc-servlet.xml
中配置消息转换器,一劳永逸。<mvc:annotation-driven><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">...</bean></mvc:message-converters> </mvc:annotation-driven>
@RestController
:如果一个 Controller 中的所有方法都返回 JSON 数据,可以在类上使用@RestController
注解,它相当于@Controller
+@ResponseBody
的组合,这样每个方法就无需再单独添加@ResponseBody
。
4. 处理日期格式
Jackson 默认将 java.util.Date
对象序列化为时间戳(毫秒数)。为了输出格式化的日期字符串,可以进行如下配置:
public String json4() throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();// 1. 关闭时间戳输出mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);// 2. 自定义日期格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");mapper.setDateFormat(sdf);Date date = new Date();return mapper.writeValueAsString(date);
}
也可以将这些配置封装成一个 JsonUtils
工具类,方便复用。
6.3 FastJson 框架简介
FastJson 是阿里巴巴开源的一款高性能 JSON 库,其 API 设计简洁,使用方便。
1. 引入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.75</version>
</dependency>
2. 核心 API
JSON.toJSONString(object)
: 将 Java 对象转换为 JSON 字符串。JSON.parseObject(jsonStr, Class.class)
: 将 JSON 字符串转换为 Java 对象。JSON.toJSON(object)
: 将 Java 对象转换为JSONObject
(本质是 Map)。JSON.toJavaObject(jsonObject, Class.class)
: 将JSONObject
转换为 Java 对象。
public class FastJsonDemo {public static void main(String[] args) {User user = new User("FastJson 用户", 20, "man");// Java 对象 -> JSON 字符串String jsonString = JSON.toJSONString(user);System.out.println(jsonString);// JSON 字符串 -> Java 对象User parsedUser = JSON.parseObject(jsonString, User.class);System.out.println(parsedUser);}
}
7. 整合 SSM 框架实战
SSM (Spring + Spring MVC + MyBatis) 是 Java Web 开发的经典组合。下面通过一个书籍管理系统的案例,演示如何从零搭建一个完整的 SSM 项目。
7.1 基本环境搭建
-
创建数据库和表:
CREATE DATABASE `ssmbuild`; USE `ssmbuild`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` (`bookID` INT ( 10 ) NOT NULL AUTO_INCREMENT COMMENT '书id',`bookName` VARCHAR ( 100 ) NOT NULL COMMENT '书名',`bookCounts` INT ( 11 ) NOT NULL COMMENT '数量',`detail` VARCHAR ( 200 ) NOT NULL COMMENT '描述',KEY `bookID` ( `bookID` ) ) ENGINE = INNODB DEFAULT CHARSET = utf8; INSERT INTO `books` ( `bookID`, `bookName`, `bookCounts`, `detail` ) VALUES( 1, 'Java', 1, '从入门到放弃' ),( 2, 'MySQL', 10, '从删库到跑路' ),( 3, 'Linux', 5, '从进门到进牢' );
-
创建 Maven Web 项目 (
ssmbuild
),添加对web的支持。 -
导入
pom.xml
依赖:包括junit
,mysql-connector-java
,c3p0
,servlet-api
,jsp-api
,jstl
,mybatis
,mybatis-spring
,spring-webmvc
,spring-jdbc
。<dependencies><!--Junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--数据库驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!-- 数据库连接池 --><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.2</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.1.9.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.9.RELEASE</version></dependency> </dependencies>
-
配置 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>
-
建立项目结构:创建
pojo
,dao
,service
,controller
等包。mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration></configuration>
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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
7.2 MyBatis 层配置
-
database.properties
: 配置数据库连接信息。jdbc.driver=com.mysql.jdbc.Driver # 如果使用的是MySQL8.0+,增加一个时区的配置。 jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=root
IDEA关联数据库
-
mybatis-config.xml
: 配置类型别名 (typeAliases
) 等。<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><typeAliases><package name="com.github.subei.pojo"/></typeAliases><mappers><mapper class="com.github.subei.dao.BookMapper"/></mappers></configuration>
-
Books.java
Pojo: 创建与books
表对应的实体类。package com.github.subei.pojo;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;}
-
BookMapper.java
接口: 定义数据库操作方法(增删改查)。package com.github.subei.dao;import com.github.subei.pojo.Books; import org.apache.ibatis.annotations.Param;import java.util.List;public interface BookMapper {// 增加一个Bookint addBook(Books book);// 根据id删除一个Bookint deleteBookById(@Param("bookID") int id);// 更新一个Bookint updateBook(Books books);// 根据id查询,返回一个BookBooks queryBookById(@Param("bookID") int id);// 查询全部Book,返回list集合List<Books> queryAllBook(); }
-
BookMapper.xml
: 编写具体的 SQL 语句。<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.github.subei.dao.BookMapper"><!--增加一个Book--><insert id="addBook" parameterType="Books">insert into ssmbuild.books(bookName,bookCounts,detail)values (#{bookName}, #{bookCounts}, #{detail});</insert><!--根据id删除一个Book--><delete id="deleteBookById" parameterType="int">delete from ssmbuild.books where bookID=#{bookID};</delete><!--更新Book--><update id="updateBook" parameterType="Books">update ssmbuild.booksset bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}where bookID = #{bookID};</update><!--根据id查询,返回一个Book--><select id="queryBookById" resultType="Books">select * from ssmbuild.bookswhere bookID = #{bookID};</select><!--查询全部Book--><select id="queryAllBook" resultType="Books">SELECT * from ssmbuild.books;</select></mapper>
-
BookService.java
接口与BookServiceImpl.java
实现类: 编写业务逻辑。package com.github.subei.service;import com.github.subei.pojo.Books;import java.util.List;// BookService:底下需要去实现,调用dao层 public interface BookService {// 增加一个Bookint addBook(Books book);// 根据id删除一个Bookint deleteBookById(int id);// 更新Bookint updateBook(Books books);// 根据id查询,返回一个BookBooks queryBookById(int id);// 查询全部Book,返回list集合List<Books> queryAllBook(); }
package com.github.subei.service;import com.github.subei.dao.BookMapper; import com.github.subei.pojo.Books;import java.util.List;public class BookServiceImpl implements BookService {// 调用dao层的操作,设置一个set接口,方便Spring管理private BookMapper bookMapper;public void setBookMapper(BookMapper bookMapper) {this.bookMapper = bookMapper;}public int addBook(Books book) {return bookMapper.addBook(book);}public int deleteBookById(int id) {return bookMapper.deleteBookById(id);}public int updateBook(Books books) {return bookMapper.updateBook(books);}public Books queryBookById(int id) {return bookMapper.queryBookById(id);}public List<Books> queryAllBook() {return bookMapper.queryAllBook();} }
OK,到此,底层需求操作编写完毕!
7.3 Spring 业务层配置
这一层主要负责整合 MyBatis 和配置业务层的 Bean 及事务。
1. spring-dao.xml
- 加载
database.properties
:<context:property-placeholder>
- 配置数据源 (DataSource): 使用 c3p0 连接池。
- 配置
SqlSessionFactoryBean
: 注入数据源,并关联mybatis-config.xml
。 - 配置
MapperScannerConfigurer
: 自动扫描dao
包下的 Mapper 接口,并为其生成代理实现,注入到 Spring 容器中。
<!-- 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置整合mybatis --><!-- 1.关联数据库文件 --><context:property-placeholder location="classpath:database.properties"/><!-- 2.数据库连接池 --><!--数据库连接池dbcp 半自动化操作 不能自动连接c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)--><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!-- 配置连接池属性 --><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><!-- c3p0连接池的私有属性 --><property name="maxPoolSize" value="30"/><property name="minPoolSize" value="10"/><!-- 关闭连接后不自动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"/><!-- 配置MyBaties全局配置文件:mybatis-config.xml --><property name="configLocation" value="classpath:mybatis-config.xml"/></bean><!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 --><!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 注入sqlSessionFactory --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><!-- 给出需要扫描Dao接口包 --><property name="basePackage" value="com.github.subei.dao"/></bean></beans>
2. spring-service.xml
- 注册 Service 实现类: 将
BookServiceImpl
注册为 Bean。 - 配置声明式事务:
- 配置
DataSourceTransactionManager
事务管理器。 - 使用
<tx:advice>
定义事务通知(哪些方法需要事务)。 - 使用
<aop:config>
将事务通知织入到 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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描service相关的bean --><context:component-scan base-package="com.github.subei.service" /><!-- 1. 注册 Service --><bean id="BookServiceImpl" class="com.github.subei.service.BookServiceImpl"><property name="bookMapper" ref="bookMapper"/></bean><!-- 2. 配置事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 3. 配置事务通知及 AOP -->...
</beans>
7.4 Spring MVC 表现层配置
-
web.xml
:- 注册
DispatcherServlet
,并指定其加载总的 Spring 配置文件applicationContext.xml
。 - 配置
CharacterEncodingFilter
解决乱码问题。
<?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>DispatcherServlet</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>DispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--encodingFilter--><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--Session过期时间--><session-config><session-timeout>15</session-timeout></session-config></web-app>
- 注册
-
spring-mvc.xml
:- 开启注解驱动 (
<mvc:annotation-driven />
)。 - 配置静态资源处理 (
<mvc:default-servlet-handler />
)。 - 配置视图解析器 (
InternalResourceViewResolver
)。 - 扫描
controller
包 (<context:component-scan>
)。
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 配置SpringMVC --><!-- 1.开启SpringMVC注解驱动 --><mvc:annotation-driven /><!-- 2.静态资源默认servlet配置--><mvc:default-servlet-handler/><!-- 3.配置jsp 显示ViewResolver视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean><!-- 4.扫描web相关的bean --><context:component-scan base-package="com.github.subei.controller" /></beans>
- 开启注解驱动 (
-
applicationContext.xml
(总配置文件):- 使用
<import>
标签将spring-dao.xml
,spring-service.xml
,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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><import resource="spring-dao.xml"/><import resource="spring-service.xml"/><import resource="spring-mvc.xml"/></beans>
配置文件,暂时结束!Controller 和 视图层编写
- 使用
7.5 案例功能实现
-
BookController.java
:- 注入
BookService
。 - 编写处理查询全部书籍、跳转到添加页面、添加书籍、跳转到修改页面、修改书籍、删除书籍、按名查询等请求的方法。
package com.github.subei.controller;import com.github.subei.pojo.Books; import com.github.subei.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller @RequestMapping("/book") public class BookController {@Autowired@Qualifier("BookServiceImpl")private BookService bookService;@RequestMapping("/allBook")public String list(Model model) {List<Books> list = bookService.queryAllBook();model.addAttribute("list", list);return "allBook";}@RequestMapping("/toAddBook")public String toAddPaper() {return "addBook";}@RequestMapping("/addBook")public String addPaper(Books books) {System.out.println(books);bookService.addBook(books);return "redirect:/book/allBook";}@RequestMapping("/toUpdateBook")public String toUpdateBook(Model model, int id) {Books books = bookService.queryBookById(id);System.out.println(books);model.addAttribute("book",books );return "updateBook";}@RequestMapping("/updateBook")public String updateBook(Model model, Books book) {System.out.println(book);bookService.updateBook(book);Books books = bookService.queryBookById(book.getBookID());model.addAttribute("books", books);return "redirect:/book/allBook";}@RequestMapping("/del/{bookID}")public String deleteBook(@PathVariable("bookID") int id) {bookService.deleteBookById(id);return "redirect:/book/allBook";} }
- 注入
-
JSP 视图:
-
index.jsp
(首页)<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE HTML> <html> <head><title>首页</title><style type="text/css">a {text-decoration: none;color: black;font-size: 18px;}h3 {width: 180px;height: 38px;margin: 100px auto;text-align: center;line-height: 38px;background: deepskyblue;border-radius: 4px;}</style> </head> <body><h3><a href="${pageContext.request.contextPath}/book/allBook">点击进入列表页</a> </h3> </body> </html>
-
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><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body><div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>书籍列表 —— 显示所有书籍</small></h1></div></div></div><div class="row"><div class="col-md-4 column"><a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a></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><tbody><c:forEach var="book" items="${requestScope.get('list')}"><tr><td>${book.getBookID()}</td><td>${book.getBookName()}</td><td>${book.getBookCounts()}</td><td>${book.getDetail()}</td><td><a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |<a href="${pageContext.request.contextPath}/book/del/${book.getBookID()}">删除</a></td></tr></c:forEach></tbody></table></div></div> </div>
-
addBook.jsp
(添加页)<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %><html> <head><title>新增书籍</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>新增书籍</small></h1></div></div></div><form action="${pageContext.request.contextPath}/book/addBook" method="post">书籍名称:<input type="text" name="bookName"><br><br><br>书籍数量:<input type="text" name="bookCounts"><br><br><br>书籍详情:<input type="text" name="detail"><br><br><br><input type="submit" value="添加"></form></div>
-
updateBook.jsp
(修改页)<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>修改信息</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>修改信息</small></h1></div></div></div><form action="${pageContext.request.contextPath}/book/updateBook" method="post"><input type="hidden" name="bookID" value="${book.getBookID()}"/>书籍名称:<input type="text" name="bookName" value="${book.getBookName()}"/>书籍数量:<input type="text" name="bookCounts" value="${book.getBookCounts()}"/>书籍详情:<input type="text" name="detail" value="${book.getDetail() }"/><input type="submit" value="提交"/></form></div>
-
- BookController 类编写 , 方法四:删除书籍
@RequestMapping("/del/{bookID}")public String deleteBook(@PathVariable("bookID") int id) {bookService.deleteBookById(id);return "redirect:/book/allBook";}
- 书籍查询功能
- 修改前端页面,allBook.jsp页面
<div class="col-md-4 column"></div><div class="form-inline"><%-- 查询书籍 --%><form action="" method="" style="float: right"><input type="text" name="queryBookName" class="form-control" placeholder="请输入需要查询的书籍名"><input type="submit" value="查询" class="btn btn-primary"></form></div>
- 修改DAO层:BookMapper.java
// 查询,搜索书籍Books queryBookByName(@Param("bookName")String bookName);
- 修改DAO层:BookMapper.xml
<!--搜索Book--><select id="queryBookByName" resultType="Books">SELECT * from ssmbuild.books where bookName = #{bookName};</select>
- 修改service层:BookService.java
// 查询,搜索书籍Books queryBookByName(String bookName);
- 修改service层:BookServiceImpl.java
public Books queryBookByName(String bookName) {return bookMapper.queryBookByName(bookName);}
- 修改controller层:BookController.java
// 查询书籍@RequestMapping("/queryBook")public String queryBook(String queryBookName,Model model){Books books = bookService.queryBookByName(queryBookName);List<Books> list = new ArrayList<Books>();list.add(books);model.addAttribute("list", list);return "allBook";}
- 修改前端:allBook.jsp
<div class="col-md-4 column"></div><div class="form-inline"><%-- 查询书籍 --%><form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right"><input type="text" name="queryBookName" class="form-control" placeholder="请输入需要查询的书籍名"><input type="submit" value="查询" class="btn btn-primary"></form></div>
- 运行测试:
如果查询失败,需要显示全部书籍页面?
- 修改前端页面allBook.jsp
<div class="row"><div class="col-md-4 column"><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-4 column"></div><div class="form-inline"><%-- 查询书籍 --%><form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right"><span style="color: red;font-weight: bold" >${error}</span><input type="text" name="queryBookName" class="form-control" placeholder="请输入需要查询的书籍名"><input type="submit" value="查询" class="btn btn-primary"></form></div></div>
- 修改后台BookController.java
// 查询书籍@RequestMapping("/queryBook")public String queryBook(String queryBookName,Model model){Books books = bookService.queryBookByName(queryBookName);List<Books> list = new ArrayList<Books>();list.add(books);if(books==null){list = bookService.queryAllBook();model.addAttribute("error","未查到");}model.addAttribute("list", list);return "allBook";}
配置Tomcat,进行运行!
项目结构图:
通过以上步骤,一个功能完整的 SSM 项目就搭建完成了。
8. Ajax 技术
8.1 初识 Ajax
AJAX = Asynchronous JavaScript and XML (异步的 JavaScript 和 XML)
AJAX 是一种在无需重新加载整个网页的情况下,能够局部更新页面内容的技术。它通过在后台与服务器进行少量数据交换,实现了网页的异步通信,极大地提升了用户体验。
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
- AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
- Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。
- 在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest能够自动帮你完成搜索单词。
- Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
- 和国内百度的搜索框一样!
- 传统的网页(即不用ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
- 使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
- 使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。
伪造Ajax – 可以使用前端的一个标签来伪造一个ajax的样子。iframe标签
- 新建一个module :springmvc-06-ajax , 导入web支持!
- 测试项目是否成功!
- 配置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>encoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
- 配置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"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.github.subei.controller"/><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean><!-- 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></beans>
- 配置AjaxController.java
package com.github.subei.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AjaxController {@RequestMapping("k1")public String test(){return "hello";}
}
- 这一步很关键,手动添加lib
- 测试文件!
- 编写一个 ajax-frame.html 使用 iframe 测试,感受下效果。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>subei</title>
</head>
<body>
<script type="text/javascript">window.onload = function(){var myDate = new Date();document.getElementById('currentTime').innerText = myDate.getTime();};function LoadPage(){var targetUrl = document.getElementById('url').value;console.log(targetUrl);document.getElementById("iframePosition").src = targetUrl;}</script><div><p>请输入要加载的地址:<span id="currentTime"></span></p><p><input id="url" type="text" value="https://www.dogedoge.com/"/><input type="button" value="提交" onclick="LoadPage()"></p>
</div><div><h3>加载页面位置:</h3><iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe>
</div>
</body>
</html>
- 使用IDEA开浏览器测试一下!
利用AJAX可以做:
- 注册时,输入用户名自动检测用户是否已经存在。
- 登陆时,提示用户名密码错误
- 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。
- …等等
8.2 结合 jQuery 使用 Ajax
原生 JavaScript 实现 Ajax 比较繁琐,通常我们会使用 jQuery 提供的 Ajax API,它对原生 XMLHttpRequest
进行了优雅的封装。
核心方法是 $.ajax()
,以及其简化版如 $.get()
, $.post()
。
- 纯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:请求地址type:请求方式,GET、POST(1.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 将自动替换 ? 为正确的函数名,以执行回调函数
- 项目06中引用jQuary,下载地址:https://jquery.com/download/
- 放入项目
案例测试
- 配置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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.github.subei.controller"/><!-- 静态资源过滤 --><mvc:default-servlet-handler /><mvc:annotation-driven /><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean><!-- 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></beans>
- 编写一个AjaxController
package com.github.subei.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@RestController
public class AjaxController {@RequestMapping("/a1")public void a1(String name , HttpServletResponse response) throws IOException {System.out.println("a1:param=>"+name);if ("subei".equals(name)){response.getWriter().print("true");}else{response.getWriter().print("false");}}
}
- 导入jquery
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>
- 编写index.jsp测试
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script><script>function a(){$.post({url:"${pageContext.request.contextPath}/a1",data:{'name':$("#username").val()},success:function (data,status) {console.log("data=" + data);console.log("status=" + status);}});}</script></head><body><%-- 失去焦点的时候,发起一个请求到后台 --%>用户名:<input type="text" id="txtName" onblur="a()"/></body>
</html>
- 启动tomcat测试!打开浏览器的控制台,当我们鼠标离开输入框的时候,可以看到发出了一个ajax的请求!是后台返回给我们的结果!测试成功!
8.3 Ajax异步加载数据
- 实体类user
package com.github.subei.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private String name;private int age;private String sex;}
- 获取一个集合对象,展示到前端页面
@RequestMapping("/a2")public List<User> a2(){List<User> list = new ArrayList<User>();list.add(new User("哇哈哈1号",9,"男"));list.add(new User("哇哈哈2号",6,"男"));list.add(new User("哇哈哈3号",1,"男"));return list; // 由于@RestController注解,将list转成json格式返回}
- 前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<input type="button" id="btn" value="获取数据"/>
<table width="80%" align="center"><tr><td>姓名</td><td>年龄</td><td>性别</td></tr><tbody id="content"></tbody>
</table><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>
<script>$(function () {$("#btn").click(function () {$.post("${pageContext.request.contextPath}/a2",function (data) {console.log(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>"}$("#content").html(html);});})})
</script>
</body>
</html>
- 测试服务器
4.Ajax验证用户名体验
- 编写Controller
@RequestMapping("/a3")public String a3(String name,String pwd){String msg = "";//模拟数据库中存在数据if (name!=null){if ("admin".equals(name)){msg = "OK";}else {msg = "用户名输入错误";}}if (pwd!=null){if ("123456".equals(pwd)){msg = "OK";}else {msg = "密码输入有误";}}return msg; //由于@RestController注解,将msg转成json格式返回}
- 前端页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>ajax</title><script src="${pageContext.request.contextPath}/statics/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>
- 测试一下效果
5.获取baidu接口Demo
<!DOCTYPE HTML>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>JSONP百度搜索</title><style>#q{width: 500px;height: 30px;border:1px solid #ddd;line-height: 30px;display: block;margin: 0 auto;padding: 0 10px;font-size: 14px;}#ul{width: 520px;list-style: none;margin: 0 auto;padding: 0;border:1px solid #ddd;margin-top: -1px;display: none;}#ul li{line-height: 30px;padding: 0 10px;}#ul li:hover{background-color: #f60;color: #fff;}</style><script>// 2.步骤二// 定义demo函数 (分析接口、数据)function demo(data){var Ul = document.getElementById('ul');var html = '';// 如果搜索数据存在 把内容添加进去if (data.s.length) {// 隐藏掉的ul显示出来Ul.style.display = 'block';// 搜索到的数据循环追加到li里for(var i = 0;i<data.s.length;i++){html += '<li>'+data.s[i]+'</li>';}// 循环的li写入ulUl.innerHTML = html;}}// 1.步骤一window.onload = function(){// 获取输入框和ulvar Q = document.getElementById('q');var Ul = document.getElementById('ul');// 事件鼠标抬起时候Q.onkeyup = function(){// 如果输入框不等于空if (this.value != '') {// 创建标签var script = document.createElement('script');//给定要跨域的地址 赋值给src//这里是要请求的跨域的地址 我写的是百度搜索的跨域地址script.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+this.value+'&cb=demo';// 将组合好的带src的script标签追加到body里document.body.appendChild(script);}}}</script>
</head><body>
<input type="text" id="q" />
<ul id="ul"></ul>
</body>
</html>
9. 拦截器 (Interceptor)
9.1 概述与区别
Spring MVC 的拦截器(HandlerInterceptor)类似于 Servlet 的过滤器(Filter),用于对处理器(Controller)进行预处理和后处理。它是 AOP 思想的一种具体应用。
过滤器 (Filter) vs. 拦截器 (Interceptor)
特性 过滤器 (Filter) 拦截器 (Interceptor) 归属 Servlet 规范的一部分,任何 Java Web 项目可用 Spring MVC 框架的一部分,仅在 Spring MVC 中使用 拦截范围 可拦截所有资源(URL-Pattern /*
)只拦截对 Controller 的请求,不拦截静态资源 控制粒度 较粗 更细,可以访问 Handler 等 Spring 容器中的对象
9.2 自定义拦截器
自定义拦截器需要实现 HandlerInterceptor
接口,该接口包含三个方法:
preHandle(req, res, handler)
: 在 Controller 方法执行之前调用。- 返回
true
:放行,执行下一个拦截器或 Controller。 - 返回
false
:请求被拦截,不再继续执行。
- 返回
postHandle(req, res, handler, mv)
: 在 Controller 方法执行之后,视图渲染之前调用。可以修改ModelAndView
。afterCompletion(req, res, handler, ex)
: 在整个请求完成,视图渲染之后调用。通常用于资源清理。
那如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持。
2、配置web.xml 和 applicationContext.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>encoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
- 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"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.github.subei.controller"/><!-- 静态资源过滤 --><mvc:default-servlet-handler /><mvc:annotation-driven /><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean><!-- 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></beans>
- 编写一个拦截器
package com.github.subei.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;package com.github.subei.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class Interceptor implements HandlerInterceptor {// 在请求处理的方法之前执行// 如果返回true执行下一个拦截器// 如果返回false就不执行下一个拦截器public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {System.out.println("------------处理前------------");return true;}// 在请求处理方法执行之后执行public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {System.out.println("------------处理后------------");}// 在dispatcherServlet处理后执行,做清理工作.public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {System.out.println("------------清理------------");}
}
- 在applicationContext.xml的配置文件中配置拦截器
<!--关于拦截器的配置-->
<mvc:interceptors><mvc:interceptor><!--/** 包括路径及其子路径--><!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--><!--/admin/** 拦截的是/admin/下的所有--><mvc:mapping path="/**"/><!--bean配置的就是拦截器--><bean class="com.github.subei.interceptor.Interceptor"/></mvc:interceptor>
</mvc:interceptors>
- 编写一个Controller,接收请求
package com.github.subei.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;// 测试拦截器的控制器
@Controller
public class InterceptorController {@RequestMapping("/k1")@ResponseBodypublic String testFunction() {System.out.println("控制器中的方法执行了==》");return "hello";}
}
- 前端 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><a href="${pageContext.request.contextPath}/k1">拦截器测试</a></body>
</html>
7、启动tomcat 测试一下!
9.3 实战:用户登录验证
实现思路
- 有一个登陆页面,需要写一个controller访问页面。
- 登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
- 拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面
案例测试:
- 编写一个登陆页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head><h1>登录页面</h1>
<hr><body>
<form action="${pageContext.request.contextPath}/user/login">用户名:<input type="text" name="username"> <br>密码:<input type="password" name="pwd"> <br><input type="submit" value="提交">
</form>
</body>
</html>
- 编写一个Controller处理请求
package com.github.subei.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpSession;@Controller
@RequestMapping("/user")
public class UserController {// 跳转到登陆页面@RequestMapping("/jumplogin")public String jumpLogin() throws Exception {return "login";}// 跳转到成功页面@RequestMapping("/jumpSuccess")public String jumpSuccess() throws Exception {return "success";}// 登陆提交@RequestMapping("/login")public String login(HttpSession session, String username, String pwd) throws Exception {// 向session记录用户身份信息System.out.println("接收前端==="+username);session.setAttribute("user", username);return "success";}// 退出登陆@RequestMapping("logout")public String logout(HttpSession session) throws Exception {// session 过期session.invalidate();return "login";}
}
- 编写一个登陆成功的页面 success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>登录成功页面</h1>
<hr>${user}
<a href="${pageContext.request.contextPath}/user/logout">注销</a>
</body>
</html>
- 在 index.jsp 页面上测试跳转!启动Tomcat 测试,未登录也可以进入主页!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><%--登录--%><a href="${pageContext.request.contextPath}/user/jumplogin">登录</a><a href="${pageContext.request.contextPath}/user/jumpSuccess">成功页面</a></body>
</html>
- 编写用户登录拦截器
package com.github.subei.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException, ServletException, IOException {// 如果是登陆页面则放行System.out.println("uri: " + request.getRequestURI());if (request.getRequestURI().contains("login")) {return true;}HttpSession session = request.getSession();// 如果用户已登陆也放行if(session.getAttribute("user") != null) {return true;}// 用户没有登陆跳转到登陆页面request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);return false;}public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}
- 在applicationContext.xml的配置文件中注册拦截器
<!--关于拦截器的配置--><mvc:interceptors><mvc:interceptor><!--/** 包括路径及其子路径--><!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--><!--/admin/** 拦截的是/admin/下的所有--><mvc:mapping path="/**"/><!--bean配置的就是拦截器--><bean class="com.github.subei.interceptor.LoginInterceptor"/></mvc:interceptor></mvc:interceptors>
- 再次重启Tomcat测试!
10. 文件上传和下载
10.1 准备工作
- 文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
- 前端表单要求:为了能上传文件,必须将表单的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程序员上传文件的最佳选择。
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
- CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
10.2 文件上传
- 导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入依赖包 commons-io包;
<dependencies><!--文件上传--><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>3.0.1</version><scope>provided</scope></dependency></dependencies>
- 配置bean:multipartResolver,在 applicationContext.xml 中配置。
【注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!】
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.github.subei.controller"/><!-- 静态资源过滤 --><mvc:default-servlet-handler /><mvc:annotation-driven /><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean><!--文件上传配置--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --><property name="defaultEncoding" value="utf-8"/><!-- 上传文件大小上限,单位为字节(10485760=10M) --><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="40960"/></bean><!-- 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></beans>
CommonsMultipartFile 的 常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transferTo(File dest):将上传文件保存到一个目录文件中
- 编写前端页面,index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit" value="upload"></form></body>
</html>
- Controller
package com.github.subei.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;import javax.servlet.http.HttpServletRequest;
import java.io.*;@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";}
}
报错:Cannot resolve method ‘getServletContext’ in ‘HttpServletRequest’
- 这个报错原因是因为pom依赖导包
servlet-api
的版本错误: - 在调用request.getServletContext()的方法需要
servlet-api
的版本3.0以上才可以。 - 所以把之前的版本修改一下就可以了,修改为4.0.1版本即可。如下:
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope>
</dependency>
- 测试上传,成功!!!
采用file.Transto 来保存上传的文件
- 编写Controller
/** 采用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";}
- 前端表单提交地址修改
- 访问提交测试,OK!
3.文件下载
文件下载步骤:
- 设置 response 响应头
- 读取文件 – InputStream
- 写出文件 – OutputStream
- 执行操作
- 关闭流 (先开后关)
代码实现:
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{// 要下载的图片地址String path = request.getServletContext().getRealPath("/upload");String fileName = "winter.jpg";// 1、设置response 响应头response.reset(); // 设置页面不缓存,清空bufferresponse.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;
}
- 前端
<a href="${pageContext.request.contextPath}/download">点击下载</a>
- 测试,文件下载OK
参考
- Spring MVC入门学习目录 - subeiLY - 博客园
- B站狂神讲Spring MVC
- https://www.cnblogs.com/gh110/category/1586866.html