SpringMVC学习笔记
1 SpringMVC 简介
1.1 MVC 架构
-
MVC 是模型(Model)、视(View)、控制器(Controller)的简写,是一种软件设计规范
-
MVC 架构可以将业务逻辑、视图、数据库三者清晰地分离开来,各司其职互不干扰
-
MVC 架构主要作用是降低了视图、业务逻辑之间的双向耦合
-
MVC 不是一种设计模式,而是一种架构模式
1.2 什么是 SpringMVC
SpringMVC 是 Spring 的一部分,是基于 Java 实现的 MVC 的轻量级 Web 框架
SpringMVC 有以下特点:
-
轻量级,简单易学,功能强大
-
是基于请求响应的 MVC 框架,效率较高
-
与 Spring 的兼容性好,且约定大于配置
-
SpringMVC 的框架是围绕 DispatcherServlet 进行设计
-
使用的人很多,范围很广
2 SpringMVC 原理
2.1 测试例子
接下来实现一遍 SpringMVC 的原理(实际开发不这样写)
首先在核心文件 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-->
<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>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后要去 /resources/springmvc-servlet.xml 中配置视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--Handler-->
<bean id="/hello" class="com.jiuxiao.controller.HelloController"/>
</beans>
然后就是 Controller 的编写
package com.jiuxiao.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Hello
*
* @author: WuDaoJiuXiao
* @Date: 2022/05/07 10:08
* @since: 1.0.0
*/
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//模型视图
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "Hello SpringMVC!");
//配置文件中有前缀和后缀,拼接之后就是 /WEB-INF/jsp/hello.jsp
modelAndView.setViewName("hello");
return modelAndView;
}
}
在我们要测试的 hello 页面中,取到传过来的值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
启动 Tomcat,访问 hello 页面,发现报错 404,资源未找到

这是很常见的错误,IDEA 的问题,只要在项目的 Artifacts 中增加一个 lib 目录,然后将项目所需的 jar 包复制过去就行了


配置好之后,重启 Tomcat,已经可以正常访问

2.2 原理分析
SpringMVC 大致原理如下:
-
用户发起请求,被前置的控制器拦截到请求
-
根据请求的参数生成代理请求,找到请求对应的实际控制器
-
控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器
-
控制器使用视图与视图渲染的结果,将结果返回给中心控制器
-
最后,将结果返回给用户

执行流程再分析

- DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心,当用户发出请求时,DispatcherServlet 就会接受并且拦截该请求
-
假设请求的 url 为:http://localhost:8088/SrpingMvc/hello
-
http://localhost:8088:表示服务器的域名
-
SpringMVC:表示部署在服务器上的 web 站点
-
hello:表示控制器
-
则该 url 表示为:请求位于服务器 http://localhost:8088 上的 SpringMVC 站点的 hello 控制器
-
HandlerMapping 为处理器映射,DispatcherServlet 调用 HandlerMapping,HandlerMapping 根据请求的 url 查找 Handler
-
HandlerExecution 表示具体的 Handler,主要作用是根据 url 查找出控制器
-
HandlerExecution 将解析后的信息传递给 DispatcherServlet
-
HandlerAdapter 表示处理适配器,按照特定的规则去执行 Handler
-
Handler 让具体的 Controller 执行
-
Controller 将具体的执行信息返回给 HandlerAdapter
-
HandlerAdapter 将视图逻辑名或者模型传递给 DispatcherServelt
-
DispatcherServelt 调用视图解析器(ViewResolver)解析 HandlerAdapter 传递的逻辑视图名
-
视图解析器将解析的逻辑视图名传递给 DispatcherServlet
-
DispatcherServlet 根据视图解析器解析的视图结果,调用具体的视图
-
最终视图呈现给用户
3 使用注解正式开发
-
新建一个子模块,使用默认的 Maven 空项目
-
项目右键,添加该项目为 web 项目
-
然后因为 IDEA 的原因,要在项目的 Artifacts 的 lib 中加入此项目所需的 jar 包
-
配置 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>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联SpringMVC配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc-servlet.xml</param-value>
</init-param>
</servlet>
<!--所有的请求都会被 DispatcherServlet拦截,注意 / 和 /* 区别-->
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置 spring-mvc-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
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/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包,让指定位置下的注解生效,由 IOC 容器统一管理-->
<context:component-scan base-package="com.jiuxiao.controller"/>
<!--让SpringMVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持MVC注解驱动-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 编写控制器 HelloController
package com.jiuxiao.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Hello控制器
*
* @author: WuDaoJiuXiao
* @Date: 2022/05/07 15:59
* @since: 1.0.0
*/
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model){
//封装数据
model.addAttribute("msg", "Hello, SpringMVC Annotation!");
//这里的返回值,就是我们所要请求的页面,会直接被视图解析器解析
return "hello";
}
}
- 编写视图层 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
到此为止,我们的使用注解开发的步骤完成,启动 Tomcat,成功访问

4 控制器 Controller
-
控制器负责提供访问应用程序的行为,通常通过实现接口或者注解定义两种方式实现
-
控制器负责解析用户的行为,并且将其转换为一个模型
-
在 SpringMVC 中,一个控制器可以包含多个方法
-
在 SpringMVC 中,对于 Controller 的配置方式有很多种
-
被他注解的类,会被 Spring 默认接管
-
被它注解的类中,所有的返回值为 String,并且有具体的页面可以跳转的方法,都会被视图解析器解析
4.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">
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在 spring-mxv.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">
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 实现 Controller 接口
public class ControllerTest01 implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "test01");
modelAndView.setViewName("test01");
return modelAndView;
}
}
- 配置该控制器对应的 bean
<bean name="/t1" class="com.jiuixao.controller.ControllerTest01"/>
- 在对应的前端视图页面取值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
5.2 注解方式
- 在核心配置文件中配置 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在 spring-mxv.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">
<!--扫描路径-->
<context:component-scan base-package="com.jiuixao.controller"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 然后就是 Controller 类
@Controller
public class ControllerTest02 {
@RequestMapping("/t2")
public String test2(Model model){
model.addAttribute("msg", "Hello SpringMVC 02");
return "test01";
}
}
5 RestFul 风格
RestFul 就是一个资源定位已经资源操作的风格,既不是标准也不是协议,只是一种风格
基于该风格设计的软件可以更加简洁、层次化,更易实现缓存,而且也更加具有安全性
现在我们假设,前端传递过来两个参数,我们实现两个数相加并显示
普通的方式:需要前端直接在 url 里进行传参,即 localhost:8088/add?a=2&b=5
@Controller
public class RestFukController {
@RequestMapping("/add")
public String test01(int a, int b, Model model){
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test01";
}
}

RestFul 风格:使用 @PathVariable 注解,让方法参数的值对应绑定到一个 url 模板变量上,即 localhost:8088/add/2/5
@Controller
public class RestFulController {
@RequestMapping("/add/{a}/{b}")
public String test01(@PathVariable int a, @PathVariable int b, Model model){
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test01";
}
}

6 结果跳转方式
6.1 SpringMVC 转发
@RequestMapping("/hello/t1")
public String test01(){
return "forward:/index.jsp";
}
6.2 SpringMVC 重定向
@RequestMapping("/hello/t2")
public String test02(){
return "redirect:/index.jsp";
}
7 数据处理
7.1 处理提交的数据
- 提交的参数名称和处理方法的参数名一致
//提交的数据:http://localhost:8088/test?name=jack
//处理方法
@RequestMapping("/test")
public String test01(String name){
return "test";
}
- 提交的参数名称与处理方法的参数名不一致
//提交的数据:localhost:8088/test?username=jack
//处理方法
@RequestMapping("/test")
public String test01(@RequestParam("username") String name){
return "test";
}
- 提交的是一个对象
该情况下需要提交的表单域和对象的属性名一致,参数使用对象即可
//实体类
public class User {
private int id;
private String name;
private int age;
}
//提交的数据中的参数名必须与对象的属性名一致,否则会接收不到
//提交的数据:http://localhost:8088/test?id=1&name=jack&age=18
//处理方法
@RequestMapping("/test")
public String test02(User user){
return "test";
}
7.2 数据显示到前端
- 通过 ModelAndView
public class ControllerTest01 implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "test01");
modelAndView.setViewName("test01");
return modelAndView;
}
}
- 通过 ModelMap
@RequestMapping("/test")
public String test01(@RequestParam("username") String name, ModelMap modelMap) {
modelMap.addAttribute("msg", name);
return "test";
}
- 通过 Model
@RequestMapping("/test")
public String test02(@RequestParam("username") String name, Model model) {
model.addAttribute("msg", name);
return "test";
}
7.3 乱码处理
在前端提交中文数据是很常见的事情,既然出现了中文,那么乱码问题也就不可避免
首先建立一个 codetest.jsp 页面,用来模拟提交表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/code/t1" method="post">
姓名:<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>
然后在后台接收前端输传回的数据
@Controller
public class EncodingController {
@PostMapping("/code/t1")
public String test01(@RequestParam("name") String name, Model model){
System.out.println(name); //在这里已经是乱码了
model.addAttribute("msg", name);
return "test01";
}
}
启动 Tomcat,输入中文数据之后,点击提交,发现后端接收到的数据果然是乱码


怎么解决乱码呢?导致乱码的问题千奇百怪,需要一个个去排除
一般情况下,使用 SpringMVC 提供的过滤器的功能就可以解决乱码问题了,直接在 web.xml 中进行配置
<!--配置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 自带的过滤器都会解决乱码问题,但也有例外,那么就要从 Tomcat 配置、项目总配置其他方向着排查了
8 JSON 对象
8.1 什么是 json
-
JSON 是一种轻量级的数据交换格式,使用特别广泛
-
它采用完全独立于编程语言的文本格式来存储和表示数据
-
简洁和清晰的结构使得 JSON 成为一个理想的数据交换语言,提高2网络传输的效率
let user = {
name: '荒天帝',
age: 18,
sex: '男'
};
//对象转换为 json 字符串
let jsonUser = JSON.stringity(user); // {"name":"荒天帝", "age":"18", "sex":"男"}
//json 字符串解析为对象
JSON.parse(jsonUser); //{mame: '荒天帝', age: 18, sex: '男'}
8.2 Jackson
Jackson 应该是目前比较好的一种 json 解析工具了,他可以帮助我们使用 Controller 返回给前端 json 数据
- 首先,需要导入它所对应的依赖
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
- 在 spring-mvc.xml 中配置编码,这样可以解决转换的 json 中文乱码问题
<?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
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--包扫描-->
<context:component-scan base-package="com.jiuxiao.controller"/>
<!--注解支持,包括 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>
<!--静态资源过滤-->
<mvc:default-servlet-handler/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 一个 SpringMVC 支持的项目,配置必不可少
<?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>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置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>
</web-app>
- 然后创建一个 User 实体类用于测试
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
- 编写 Controller 类
@Controller
public class UserController {
@RequestMapping("/t1")
@ResponseBody //加上该注解后,就不会走视图解析器,会直接返回一个字符串
public String test01() throws JsonProcessingException {
List<User> list = new ArrayList<User>();
list.add(new User(1, "林枫", 24));
list.add(new User(2, "梦情", 18));
list.add(new User(3, "段欣叶", 20));
list.add(new User(4, "唐幽幽", 22));
return new ObjectMapper().writeValueAsString(list);
}
}
启动项目,成功将对象转为 json 数据对象

8.3 fastjson
fastJson 是阿里的针对 Json 解析的一个工具类库
- 首先要导入依赖,其余就是常规的 web.xml、spring-mvc.xml 配置
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.2</version>
</dependency>
- 仍然使用上方例子中的 user 对象
@RequestMapping("/t2")
@ResponseBody
public String test02(){
List<User> userList = new ArrayList<User>();
userList.add(new User(1, "林枫", 24));
userList.add(new User(2, "梦情", 18));
userList.add(new User(3, "段欣叶", 20));
userList.add(new User(4, "唐幽幽", 22));
return JSON.toJSONString(userList);
}
启动项目,成功转换为 json 对象

9 Ajax 技术
9.1 Ajax 简介
-
Ajax 全名为 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
-
Ajax 是一种在无需重新加载整个网页的情况下,能够异步刷新部分网页的技术
-
Ajax 不是一种语言,而是一种可以更好、更快速的、交互性更强的 Web 应用技术
-
使用了 Ajax 的网页,通过在后台服务器进行少量的数据交换,就可以实时异步更新
9.2 Ajax 接收数据
- 首先创建一个测试用的实体类 User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
- 然后要有一个前端页面,用来展示数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/statics/jquery-3.6.0.js"></script>
<script>
$(function () {
$("#btn").click(function () {
$.post("${pageContext.request.contextPath}/test01", function (data) {
let newHtml = "";
for (let i = 0; i < data.length; i++){
newHtml += "<tr>" +
"<td>" + data[i].name + "</td>" +
"<td>" + data[i].age + "</td>" +
"<td>" + data[i].sex + "</td>" +
"</tr>"
}
$("#content").html(newHtml);
});
});
});
</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>
- 接下来就是控制器
@RestController
public class UserController {
@RequestMapping("/test01")
public List<User> test01(){
List<User> userList = new ArrayList<User>();
userList.add(new User("荒天帝", 21, "男"));
userList.add(new User("伊人泪", 20, "女"));
userList.add(new User("洛璃", 17, "女"));
return userList;
}
}
启动项目,访问 jsp 页面,初始状态如下

然后点击加载数据按钮,我们发现,网页并没有重新加载,但是却通过 Ajax 异步刷新了数据并且展示

9.3 Ajax 验证用户信息
- 首先是后台控制类的编写
@RestController
public class UserController {
@RequestMapping("/t2")
public String test02(String name, String pwd){
String msg = "";
if (name != null){
msg = "admin".equals(name) ? "OK" : "用户名有误!";
}
if (pwd != null){
msg = "123".equals(pwd) ? "OK" : "密码有误!";
}
return msg;
}
}
- 然后是前端 jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/statics/jquery-3.6.0.js"></script>
<script>
function test01() {
$.post({
url: "${pageContext.request.contextPath}/t2",
data: {"name": $("#username").val()},
success: function (data) {
if (data.toString() === 'OK') {
$("#nameInfo").html(data).css("color", "green");
} else {
$("#nameInfo").html(data).css("color", "red");
}
}
});
}
function test02() {
$.post({
url: "${pageContext.request.contextPath}/t2",
data: {"pwd": $("#password").val()},
success: function (data) {
if (data.toString() === 'OK') {
$("#pwdInfo").css("color", "green").html(data);
} else {
$("#pwdInfo").css("color", "red").html(data);
}
}
});
}
</script>
</head>
<body>
<p>
用户名 : <input type="text" id="username" onblur="test01()">
<span id="nameInfo"></span>
</p>
<p>
密码 : <input type="text" id="password" onblur="test02()">
<span id="pwdInfo"></span>
</p>
</body>
</html>
启动项目,当我们输入错误的用户名、密码之后,只要鼠标失去焦点,就会提示信息错误

然后输入正确的用户名、密码之后,就会显示正确的信息

10 拦截器
-
SpringMVC 的拦截器类似于 Servlet 中的过滤器,对于处理器进行预处理和后处理,开发者可以自定义一些自己的拦截器
-
拦截器只会拦截访问控制器的方法,如果访问的是 jsp、html、css、js、image 等是不会拦截的
-
SpringMVC 的拦截器是基于 AOP 思想的具体实现
10.1 拦截器执行顺序
- 首先新建一个拦截器,实现 HandlerInterceptor 接口,重写该接口的三个方法
/**
* 自定义拦截器
*
* @author: WuDaoJiuXiao
* @Date: 2022/05/11 09:48
* @since: 1.0.0
*/
public class MyInterceptor implements HandlerInterceptor {
//return true : 会放行,执行下一个拦截器
//return false : 会拦截,不会执行下一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截前");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截后");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("清理");
}
}
- 然后去 spring-mvc 配置拦截器及其拦截路径
<!--拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.jiuxiao.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 使用测试类测试
@RestController
public class UserController {
@GetMapping("/a2")
public String test03(){
System.out.println("UserController执行了...");
return "OK";
}
}
启动项目,访问 /a2 路径,发现拦截器就是基于 AOP 思想实现的

10.2 登录拦截器
一般情况下,用户不登录是无法直接访问后台首页的,那么怎么用拦截器实现?
- 首先依次创建测试页面
index 主页面,就两个链接:后台首页和登录页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1><a href="${pageContext.request.contextPath}/user/goLogin">点击登录</a></h1>
<h1><a href="${pageContext.request.contextPath}/user/goMain">后台首页</a></h1>
</body>
</html>
login 登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名: <input type="`text`" name="username">
密码: <input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
main 后台首页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是后台首页</h2>
<span>欢迎用户 : ${username}</span>
<p><a href="${pageContext.request.contextPath}/user/goOut">注销</a></p>
</body>
</html>
- 然后是控制器
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/login")
public String login(HttpSession session, String username, String password, Model model){
session.setAttribute("userInfo", username);
model.addAttribute("username", username);
return "main";
}
@RequestMapping("/goMain")
public String main(){
return "main";
}
@RequestMapping("/goLogin")
public String login(){
return "login";
}
@RequestMapping("/goOut")
public String logout(HttpSession session){
session.removeAttribute("userInfo");
return "login";
}
}
此时,我们启动服务后,在 index 页面点击后台管理,他会直接跳转到后台主页,显然这种情况下,用户可以跳过登录直接访问后台页面,这不符合常理
所以就需要使用拦截器进行拦截
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (request.getRequestURI().contains("goLogin")){
return true;
}
if (request.getRequestURI().contains("login")){
return true;
}
if (session.getAttribute("userInfo") != null){
return true;
}
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
再次启动服务,在主界面点击后台首页,并不会进入,而是跳转到了登录页面


然后输入用户名和密码,点击登录后,表示登陆成功,会跳转到后台首页并显示用户信息

然后不点击注销,直接返回 index 页面,再次点击后台首页链接,发现不用登陆直接进来,这是因为浏览器还没关闭,用户的 Session 还在,所以可以直接进入
当我们点击注销链接后,用户的 Session 会被移除,并且自动跳转到登录页面,此时如果再点击后台首页,并不会进入,会让重新登录
通过拦截器实现的用户登录的简单功能实现了
11 文件上传和下载
11.1 文件上传
- 首先需要导入文件上传的依赖
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 然后是前端的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="upload">
</form>
</body>
</html>
- 接下来实现文件上传的 Controller
@RestController
public class FileController {
//第一种文件上传方式
@RequestMapping("/upload01")
public String fileUpload01(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件名
String uploadFileName = file.getOriginalFilename();
//文件名为空,直接返回首页
if ("".equals(uploadFileName)) {
return "redirect:/index.jsp";
}
System.out.println("上传的文件名为:" + uploadFileName);
//上传路径保存设置
String path = request.getRealPath("/upload01");
//如果路径不存在就创建一个
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件保存地址:" + realPath);
InputStream is = file.getInputStream();
FileOutputStream 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";
}
//第二种文件上传模式
@RequestMapping("/upload02")
public String fileUpload02(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//上传路径设置
File realPath = new File(request.getRealPath("/upload02"));
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件的地址是:" + realPath);
//通过 CommonsMultipartFile 的方法直接写文件
file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
return "redirect:/index.jsp";
}
}
- SpringMVC自带有文件上传,所以要去 spring-mvc.xml 配置
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="10485706"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
启动服务,选择一个文件上传,上传成功后重定向到主页,后端控制台输出了文件的信息,只在项目中找到了上传的文件,表示两种方式均上传成功

11.2 文件下载
@RequestMapping("/download")
public String downloadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
//要下载图片的地址
String path = request.getRealPath("/upload01");
String fileName = "test.jpg";
//设置响应头
response.reset();
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);
FileInputStream input = new FileInputStream(file);
ServletOutputStream output = response.getOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = input.read(buffer)) != -1){
output.write(buffer, 0, len);
output.flush();
}
output.close();
input.close();
return "OK";
}

浙公网安备 33010602011771号