MVC架构模式#
约 3795 个字 255 行代码 10 张图片 预计阅读时间 84 分钟
不使用MVC模式存在的问题#
- 不使用MVC模式,Servlet需要负责
数据接收
、核心业务逻辑处理
、数据库连接和增删改查操作
、页面展示
等功能。职责过重。 - 代码的
复用性差
,相同的业务操作或数据库操作,需要在不同Servlet中编写重复代码,不方便维护。 - 代码
耦合度高
,导致代码很难扩展。 - 操作数据库的代码和处理业务逻辑的代码混杂在一起,很容易出错,无法专注于业务逻辑的编写。
MVC模式概述#
M(Model、模型)
:用于处理业务V(View、视图)
:负责页面展示C(Controller、控制器)
:控制器是MVC架构的核心,
DAO(Data Access Object、数据访问对象)
属于JavaEE的设计模式之一。只负责数据库的增删改查,没有任何业务逻辑在里面
三层架构#
表示层
:Controller控制器+View视图业务逻辑层
:Service服务持久化层
:DAO数据访问对象
Spring MVC概述#
Spring Web MVC
是基于 Servlet API 构建的原始 Web 框架,从 Spring 框架诞生之初就已包含其中。其正式名称Spring Web MVC
源自其源码模块名称:spring-webmvc
,但更常被称为Spring MVC
。
与 Spring Web MVC 并行,Spring Framework 5.0 引入了一个名为Spring WebFlux
的响应式堆栈Web框架,其名称也基于其源模块 spring-webflux
。
Spring MVC
是实现MVC架构模式
的Web框架。底层使用Servlet
实现。
Spring MVC能干什么
- 入口控制:通过
DispatcherServlet
作为入口控制器负责接收请求和分发请求。 - 自动将表单请求参数封装为JavaBean对象
- 统一使用IOC容器管理对象
- 统一请求处理:提供拦截器、统一异常处理等机制
- 视图解析:轻松切换JSP、Freemarker、Velocity等视图模板
- 对Controller进行单元测试时
入口控制
:Servlet开发中,每个Servlet都需要在web.xml中进行配置,Spring MVC通过DispatcherServlet
作为入口控制器负责统一接收请求和分发请求。- Spring MVC会自动将表单数据封装为JavaBean对象,而不需要手动通过request对象获取表单数据。
- Spring MVC通过IOC容器管理对象,不需要手动创建对象。
- Spring MVC提供拦截器、异常处理器等统一处理请求机制。不需要手动编写过滤器。
视图解析器
:Spring MVC提供了JSP、Freemarker、Velocity等视图解析器。
Spring MVC入门案例#
创建maven工程,将工程改为war包,引入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.luguosong</groupId>
<artifactId>springmvc-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--Spring MVC依赖中包含了Spring的相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.8</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<!--provided表示该依赖只在编译和测试时有效-->
<!--打war包时不会包含,由Tomcat提供-->
<scope>provided</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--thymeleaf与spring6整合-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Warning
注意,需要将maven工程改为war包:<packaging>war</packaging>
创建webapp/WEB-INF/web.xml
目录和文件。
在web.xml
中配置前端控制器(DispatcherServlet):
Note
相比于Servlet开发,Spring MVC会配置一个全局统一的DispatcherServlet
来管理所有请求。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--优化:让DispatcherServlet在启动时就加载,而不是首次访问时加载,提高首次访问速度-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--url-pattern配置/表示处理除jsp外的所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在Spring MVC配置文件配置包扫描视图解析器:
其中常见的视图解析器有以下几种:
- JSP的视图解析器:InternalResourceViewResolver
- FreeMarker的视图解析器:FreeMarkerViewResolver
- Velocity的视图解析器:VelocityViewResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller"/>
<!--配置视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
编写视图:
编写Controller:
package com.luguosong.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
public class HelloController {
//请求映射
@RequestMapping("/hello-mvc")
public String hello() {
//返回逻辑视图名称
return "hello";
}
}
Note
逻辑视图名称
会根spring mvc配置文件中的prefix
和suffix
属性进行拼接。找到具体的视图位置(物理视图名称)。
启动Tomcat,通过以下地址可以访问视图:
Spring MVC执行流程#
- 发送请求,
DispatcherServlet类
接收请求。doDispatch方法
负责处理请求。- 通过
HttpServletRequest
请求对象得到uri,根据uri得到HandlerExecutionChain处理器执行链
对象( 其中包含拦截器和处理器)。 HandlerExecutionChain处理器执行链
获取处理器适配器HandlerAdapter
对象。HandlerExecutionChain对象
执行该请求所有拦截器中的preHandle方法
。- 通过消息转换器将请求参数进行转换,
HandlerAdapter
对象调用Controller处理器方法。返回ModelAndView
对象。 HandlerExecutionChain对象
执行该请求所有拦截器中的postHandle方法
。processDispatchResult方法
处理响应结果。- 通过
视图解析器
解析,返回视图对象
。调用视图对象的渲染方法。 - 执行该请求所有拦截器中的
afterCompletion方法
。
- 通过
- 通过
自定义Spring MVC配置文件名称#
默认情况下,Spring MVC会根据web.xml中<servlet-name>标签
的值去寻找Spring MVC配置文件。
比如<servlet-name>
的值为springmvc
,那么就会去寻找/WEB-INF/springmvc-servlet.xml
配置文件。
当然,也可以手动指定Spring MVC配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定配置文件位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
@RequestMapping注解#
@RequestMapping注解的使用#
您可以使用@RequestMapping
注解将请求映射到控制器方法。
@RequestMapping
可以作用于类
或者方法
。
value属性#
value属性
与path属性
功能相同,都是用于映射请求路径。
@Controller
public class HelloController {
//请求映射
@RequestMapping("/hello1-1")
public String hello() {
//返回逻辑视图名称
return "hello";
}
//多个映射可以指向同一个方法
@RequestMapping({"/hello2-1", "/hello2-2"})
public String hello() {
//返回逻辑视图名称
return "hello";
}
}
value属性Ant风格#
value属性也支持Ant风格
的通配符:
?
:匹配任意单个字符*
:匹配任意多个字符**
:匹配任意多个字符(包括目录,即/
)
Warning
如果使用**
,左右两边只能是/
。
@Controller
public class HelloController {
//?表示任意单个字符,比如 hello1 或 helloa 都会访问到该方法
@RequestMapping("/hello?")
public String hello() {
//返回逻辑视图名称
return "hello";
}
}
value属性占位符#
使用占位符
,可以实现Restful风格
的参数传递
方法中通过@PathVariable
获取参数
@Controller
public class HelloController {
@RequestMapping("/login/{username}/{password}")
public String login(@PathVariable String username,
@PathVariable String password) {
//用户登录
//...
return "ok";
}
}
method属性#
method属性
用于限制请求方法,method
属性的值可以是GET
、POST
、PUT
、DELETE
等。
@Controller
public class HelloController {
//只会接收Get类型的请求
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
//返回逻辑视图名称
return "hello";
}
}
衍生Mapping#
除了@RequestMapping
注解,还可以使用@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
、@PatchMapping
等注解。表示具体method方法的请求。
params属性#
params属性
对请求参数进行限制
@Controller
public class HelloController {
//表示请求参数中必须存在username和password,且username必须为张三
@PostMapping(value = "/hello", params = {"username=张三", "password"})
public String hello() {
//返回逻辑视图名称
return "ok";
}
}
headers属性#
headers属性
对请求头进行限制
@Controller
public class HelloController {
//表示请求头中必须存在token
@PostMapping(value = "/hello", headers = {"token"})
public String hello() {
//返回逻辑视图名称
return "ok";
}
}
请求参数处理⭐#
消息转换器#
消息转换器
可以将HTTP请求的消息转换为Java对象,或者将Java对象转换为HTTP响应。
Form请求-形参解析参数#
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping("/formView")
public String formView() {
return "get-parameters/form";
}
/*
* 模拟Servlet接收参数
* */
@PostMapping("/servlet")
public String servletPost(HttpServletRequest request) {
request.setAttribute("username", request.getParameter("username"));
return "get-parameters/form";
}
/*
* @RequestParam注解value值为请求参数名
* required属性表示参数是否必须,默认为true
* defaultValue属性为默认值
* */
@PostMapping("/springMvc")
public String springMvc(
@RequestParam(
value = "username",
required = false,
defaultValue = "张三") String username,
HttpServletRequest request) {
request.setAttribute("username", username);
return "get-parameters/form";
}
/*
* 如果请求参数的形参名和方法参数名一样,则@RequestParam注解可以省略
* */
@PostMapping("/springMvc2")
public String springMvc2(
String username,
HttpServletRequest request) {
request.setAttribute("username", username);
return "get-parameters/form";
}
}
如果是Spring6+,想要省略@RequestParam注解,需要在pom.xml中配置-parameters
标记
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
Form请求-Bean对象解析参数#
Note
SpringMVC会使用FormHttpMessageConverter
消息转换器将表单数据转为JavaBean。
package com.luguosong.controller;
import com.luguosong.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/formPojo")
public class FormPojoController {
@PostMapping("/springMvc")
public String springMvcPojo(
User user,
HttpServletRequest request) {
request.setAttribute("username", user.getUsername());
return "get-parameters/form";
}
}
package com.luguosong.pojo;
/**
* @author luguosong
*/
public class User {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
}
Form请求-获取参数原始字符串#
通过@RequestBody注解
可以拿到请求参数的原始字符串。
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/formString")
public class FormStringController {
@RequestMapping("/springMvc")
public String springMvc(
@RequestBody String requestBody,
HttpServletRequest request
) {
request.setAttribute("username", requestBody);
return "get-parameters/form";
}
}
Note
底层使用FormHttpMessageConverter
消息转换器。
JSON请求-Bean对象解析参数#
在pom.xml引入处理json的依赖:
<!--负责json字符串和java对象之间的转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
通过@RequestBody注解
可以将JSON格式的请求参数转为Java对象。
package com.luguosong.controller;
import com.luguosong.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("jsonPojo")
public class JsonPojoController {
@RequestMapping("springMvc")
public String springMvc(
@RequestBody User user) {
System.out.println(user);
return "get-parameters/form";
}
}
Get请求中文乱码问题#
Tomcat8以及之前版本,解决Get请求中文乱码,在Tomcat服务器CATALINA_HOME/conf/server.xml
中配置:
Tomcat8之后版本,请求行默认采用UTF-8编码,无需解决中文乱码问题。
Post请求中文乱码问题#
Tomcat9以及之前的版本,需要解决Post请求中文乱码问题。
在Servlet编程
中,可以使用request.setCharacterEncoding("UTF-8");
解决乱码问题。
但在Spring MVC中,无法在Controller中使用以上方法解决中文乱码。
解决方案一:编写Servlet过滤器
,过滤器会在DispatcherServlet之前执行。因此在过滤器中设置 request.setCharacterEncoding("UTF-8");
可以解决乱码问题。
解决方案二:Spring MVC为我们提供了类似的过滤器类CharacterEncodingFilter
,无需我们重新手写过滤器类。只需要在web.xml
中配置该过滤器并设置encoding属性
即可。
<web-app>
<filter>
<filter-name>characterEncodingFilter</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>
<!--是否强制设置编码-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--是否强制设置编码-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Tomcat10请求体默认采用UTF-8编码,无需解决中文乱码问题。
RequestEntity对象#
RequestEntity对象
中存储了所有请求信息,包括请求行、请求头、请求体。
RequestEntity的泛型
对应请求体信息,如果是String表示请求体字符串,如果是实体类会将请求体转换为实体类。
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("requestEntity")
public class RequestEntityController {
@RequestMapping("/springMvc")
public String springMvc(RequestEntity<String> requestEntity) {
/*
* 获取请求行信息
* */
System.out.println("请求方法:" + requestEntity.getMethod());
System.out.println("请求地址:" + requestEntity.getUrl());
/*
* 获取请求头信息
* */
HttpHeaders headers = requestEntity.getHeaders();
System.out.println("请求参数类型:" + headers.getContentType());
/*
* 获取请求体信息
* */
String entityBody = requestEntity.getBody();
System.out.println("请求体内容:" + entityBody);
return "get-parameters/form";
}
}
文件上传#
❗Spring MVC 5以及之前版本在pom.xml引入处理文件的依赖:
<!--负责文件上传-->
<!--Spring MVC 6之后不再需要添加此依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
上传参数配置:
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置上传参数-->
<multipart-config>
<max-file-size>102400</max-file-size>
<!--设置整个表单所有文件上传的最大值-->
<max-request-size>102400</max-request-size>
<!--最小上传大小-->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
文件上传必须时post请求,因为文件数据需要通过请求体传递,get请求没有请求体。
设置请求参数类型为multipart/form-data
。
<form method="post" th:action="@{/fileUpload/springMvc}" enctype="multipart/form-data">
文件上传:<input type="file" name="fileName">
<input type="submit" value="文件上传">
</form>
Controller通过MultipartFile对象
接收文件:
package com.luguosong.controller;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;
/**
* 文件上传
*
* @author luguosong
*/
@Controller
@RequestMapping("/fileUpload")
public class FileUploadController {
@RequestMapping("/springMvc")
public String springMvc(
@RequestParam("fileName") MultipartFile file,
@RequestParam("username") String username,
HttpServletRequest request) throws IOException {
System.out.println("请求参数名:" + file.getName());
String originalFilename = file.getOriginalFilename();
System.out.println("文件真实名称:" + originalFilename);
System.out.println("额外参数:" + username);
//将文件存储到服务端
//输入流
InputStream in = file.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
//输出流
ServletContext application = request.getServletContext();//获取ServletContext
String folderPath = application.getRealPath("/upload");
File folder = new File(folderPath);
if (!folder.exists()) {
folder.mkdirs();
}
File saveFile = new File(folder.getAbsolutePath() + "/" + UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf('.')));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(saveFile));
//开始传输文件
byte[] bytes = new byte[1024 * 10];
int readCount = 0;
while ((readCount = bis.read(bytes)) != -1) {
bos.write(bytes, 0, readCount);
}
bos.flush();
//释放资源
bos.close();
bis.close();
return "get-parameters/form";
}
}
响应结果处理⭐#
返回逻辑视图名称#
默认情况下,Controller返回String,回转到对应的视图解析器进行视图解析。
响应纯字符串#
默认情况下,Controller返回String,回转到对应的视图解析器进行视图解析。
可以通过@ResponseBody注解
返回String字符串,此时返回的不再是逻辑视图名称,而是直接返回text/html
。
Note
@ResponseBody采用的是StringHttpMessageConverter
消息转换器将String字符串转换为text/html
格式。
package com.luguosong.controller;
import com.luguosong.pojo.User;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author luguosong
*/
@Controller
@RequestMapping("return-string")
public class ResponseStringController {
/*
* 模拟servlet返回String字符串
*
* 理论上应该返回逻辑视图名称,但此处返回null
* 通过HttpServletResponse做出响应
* */
@RequestMapping("/servlet")
public String servlet(HttpServletResponse resp) throws IOException {
resp.setHeader("content-type", "text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("servlet输出字符串");
return null;
}
/*
* 模拟springMvc返回String字符串
*
* 添加@ResponseBody后,返回的不再是逻辑视图名称
* 而是直接返回text/html
* */
@RequestMapping(value = "/springMvc", produces = "text/html; charset=utf-8")
@ResponseBody
public String springMvc() {
return "Spring MVC输出字符串";
}
}
响应JSON字符串#
在pom.xml引入处理json的依赖:
<!--负责json字符串和java对象之间的转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
在spring mvc配置文件中需要配置:
package com.luguosong.controller;
import com.luguosong.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author luguosong
*/
@Controller
@RequestMapping("return-json-string")
public class ResponseJSONStringController {
/*
* 返回json字符串
* */
@RequestMapping("user")
@ResponseBody
public User getUser() {
User user = new User();
user.setUsername("luguosong");
return user;
}
}
Note
当处理器方法上面有@ResponseBody注解,并返回一个Java对象,SpringMVC会自动将对象转为json字符串并响应。
此时使用的是MappingJackson2HttpMessageConverter
消息转换器。
RestController注解#
在类上添加@RestController注解
,等同于在该类上添加了@Controller注解
,同时为该类的所有方法添加了@ResponseBody注解
。
ResponseEntity对象#
ResponseEntity对象
可以定制响应协议,包括状态行、响应头和响应体。当想自定定制响应协议时,可以使用该类。
package com.luguosong.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/responseEntity")
public class ResponseEntityController {
@RequestMapping("/springmvc")
public ResponseEntity<String> springMvc() {
/*
* 模拟响应404
* */
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
}
文件下载#
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/**
* 文件下载
*
* @author luguosong
*/
@Controller
@RequestMapping("/fileDownload")
public class FileDownloadController {
@RequestMapping("/springMvc")
public ResponseEntity<byte[]> springMvc(
HttpServletRequest request
) throws IOException {
File file = new File(request.getServletContext().getRealPath("/upload") + "/demo.jpg");
//创建响应头对象
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", file.getName());
//下载文件
ResponseEntity<byte[]> entity = new ResponseEntity<>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);
return entity;
}
}
获取请求头信息#
获取请求头信息#
package com.luguosong.controller.header_info;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/header-info")
public class HeaderInfoController {
@PostMapping("/springMvc")
public String springMvc(@RequestHeader(value = "content-type", required = false) String contentType) {
System.out.println(contentType);
return "header-info/form";
}
}
获取Cookie信息#
package com.luguosong.controller.header_info;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/cookie")
public class CookieController {
/*
* ❗cookie需要在父路径相同的情况才能保存下来
* 因此通过该地址访问视图
* */
@GetMapping("/view")
public String view() {
return "header-info/form";
}
@RequestMapping("/springMvc")
public String springMvc(@CookieValue("username") String username) {
System.out.println(username);
return "header-info/form";
}
}
域对象操作#
request域#
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/request-scope")
public class RequestScopeController {
/**
* 模拟Servlet存储Request域
*/
@RequestMapping("/servlet")
public String servletTest(HttpServletRequest request) {
request.setAttribute("requestScope", "通过HttpServletRequest设置请求域");
return "request-scope";
}
/*
* Model对象存储request域
* */
@RequestMapping("/model")
public String modelTest(Model model) {
model.addAttribute("requestScope", "通过Model对象设置请求域");
return "request-scope";
}
/*
* Map集合存储request域
* */
@RequestMapping("/map")
public String mapTest(Map<String, Object> map) {
map.put("requestScope", "通过Map对象设置请求域");
return "request-scope";
}
/*
* ModelMap对象存储request域
* */
@RequestMapping("/modelMap")
public String modelMapTest(ModelMap modelMap) {
modelMap.addAttribute("requestScope", "通过ModelMap对象设置请求域");
return "request-scope";
}
/*
* ⭐ModelAndView对象存储request域
* */
@RequestMapping("/modelAndView")
public ModelAndView modelAndViewTest() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("requestScope", "通过ModelAndView对象设置请求域");
modelAndView.setViewName("request-scope");
return modelAndView;
}
}
不管是Model对象
、Map集合
还是ModelMap对象
,实际创建的的都是BindingAwareModelMap对象
。
Spring MVC为了更好的体现MVC架构模式,还提供了ModelAndView类
,这个类封装了Model和View。也就是说这个类封装业务处理之后的数据,体支持跳转指定视图。通过ModelAndView也可以设置请求域。
session域#
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/session-scope")
@SessionAttributes({"sessionScope"})
public class SessionScopeController {
/*
* 通过Servlet方式设置session
* */
@RequestMapping("/servlet")
public String servletTest(HttpServletRequest request) {
request.getSession().setAttribute("sessionScope", "通过Servlet原生方式设置session域");
System.out.println(request.getSession().getAttribute("sessionScope"));
return "session-scope";
}
/*
* 通过@SessionAttributes注解设置session
* */
@RequestMapping("/modelAndView")
public ModelAndView modelAndViewTest() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("sessionScope", "通过@SessionAttributes注解方式设置session域");
modelAndView.setViewName("session-scope");
return modelAndView;
}
}
一般情况下modelAndView.addObject
是设置request域的,但通过@SessionAttributes({"xxx"})
注解可以指定特定字段为session域。
application域#
package com.luguosong.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
@RequestMapping("/application-scope")
public class ApplicationScopeController {
@RequestMapping("/servlet")
public String servletTst(HttpServletRequest request) {
request.getServletContext().setAttribute("applicationScope", "通过Servlet方式设置application域");
return "application-scope";
}
}
一般直接采用Servlet原始方式设置application域。
视图(View)#
常见的视图#
InternalResourceView
:内部资源视图,Spring MVC框架内置,专门为JSP模板语法准备RedirectView
:重定向视图,Spring MVC框架内置,用来完成重定向效果ThymeLeafView
:Thymeleaf 是一种现代化的服务器端 Java 模板引擎,适用于网页和独立环境。Thymeleaf 的主要目标是为您的开发流程带来优雅的自然模板——这些 HTML 可以在浏览器中正确显示,同时也能作为静态原型使用,从而增强开发团队的协作。它提供了 Spring Framework 的模块、与您喜爱的工具的多种集成,并允许您插入自己的功能,因此 Thymeleaf 非常适合现代 HTML5 JVM 的网页开发——尽管它的功能远不止于此。FreeMarkerView
:Apache FreeMarker™ 是一个模板引擎:它是一个 Java 库,用于根据模板和变化的数据生成文本输出(如 HTML 网页、电子邮件、配置文件、源代码等)。模板使用 FreeMarker 模板语言(FTL)编写,这是一种简单的专用语言(不像 PHP 那样是完整的编程语言)。通常,会使用通用编程语言(如 Java)来准备数据(执行数据库查询、进行业务计算)。然后,Apache FreeMarker 使用模板展示这些准备好的数据。在模板中,你专注于如何展示数据,而在模板之外,你专注于展示哪些数据。VelocityView
:VelocityView 包含所有的 GenericTools,并增加了在 Web 应用程序(Java EE 项目)视图层中使用 Velocity 的基础设施和专用工具。这包括用于处理 Velocity 模板请求的 VelocityViewServlet 或 VelocityLayoutServlet,以及用于在 JSP 中嵌入 Velocity 的 VelocityViewTag。PDFView
:第三方,用于生成pdf文件视图ExcelView
:第三方,用于生成excel文件视图
配置JSP视图解析器#
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller"/>
<!--配置JSP视图解析器-->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
配置Thymeleaf视图解析器#
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller"/>
<!--配置视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
视图控制器#
如何仅仅是进行视图转发,无需编写Controller类,可以通过spring mvc配置文件mvc:view-controller
标签进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller"/>
<!--配置JSP视图解析器-->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--在Spring MVC配置文件中配置视图控制器-->
<mvc:view-controller path="/test" view-name="test"/>
<!--<mvc:view-controller/>会让Spring MVC项目中的注解失效,需要重新开启-->
<!--开启注解驱动-->
<mvc:annotation-driven/>
</beans>
转发和重定向#
package com.luguosong.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author luguosong
*/
@Controller
public class TestController {
/*
* 转发
* */
@RequestMapping("forward")
public String forwardTest() {
return "forward:/test";
}
/*
* 重定向
* */
@RequestMapping("redirect")
public String redirectTest() {
return "redirect:/test";
}
@RequestMapping("/test")
public String test() {
return "test";
}
}
静态资源访问#
由于DispatcherServlet的url-pattern
配置的是/
,访问静态资源会经过DispatcherServlet
。DispatcherServlet
没有静态资源处理。
方式一:开启默认Servlet#
Tomcat目录conf/web.xml
中存在名为default
的servlet
其url-pattern
与DispatcherServlet
一样也是/
,因此默认servlet
访问被DispatcherServlet
覆盖。
Spring MVC仍然允许静态资源请求由Tomcat的默认Servlet
处理。它配置了一个DefaultServletHttpRequestHandler
,URL映射为/**
,并且相对于其他URL映射具有最低优先级
。
以下示例展示了如何通过默认设置启用该功能:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
以下示例展示了如何在Spring MVC配置文件 XML 中实现相同的配置:
方式二:配置静态资源处理#
在下一个示例中,对于以 /resources 开头的请求,将使用相对路径来查找和提供相对于 Web 应用程序根目录下的 /public 或类路径下的 /static 的静态资源。这些资源的过期时间设置为一年,以确保最大限度地利用浏览器缓存并减少浏览器发出的 HTTP 请求。Last-Modified 信息通过 Resource#lastModified 推断,以支持带有 "Last-Modified" 头的 HTTP 条件请求。
以下列表展示了如何使用 Java 配置来实现:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
}
}
以下示例展示了如何在 XML 中实现相同的配置:XML
<beans>
<mvc:resources mapping="/resources/**"
location="/public, classpath:/static/"
cache-period="31556926"/>
<mvc:annotation-driven/>
</beans>
RESTFul#
概述#
RESTFul是web服务接口的一种设计风格
。提供了一套约束,可以让web服务接口更加简介、易于理解。
- 查询:使用GET方法请求
- 添加:使用POST方法请求
- 更新:使用PUT方法请求
- 删除:使用DELETE方法请求
请求参数从/springmvc/getUserById?id=1
风格转为/springmvc/user/1
风格,变得更加简洁。
HiddenHttpMethodFilter#
理论上表单只能发送get请求
和post请求
。
但是可以借助HiddenHttpMethodFilter过滤器
,将post
方法转为put
、delete
或patch
方法。
示例#
模拟通过表单发送get
、post
、put
、delete
请求。
在web.xml中配置HiddenHttpMethodFilter过滤器
:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--优化:让DispatcherServlet在启动时就加载,而不是首次访问时加载,提高首次访问速度-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--url-pattern配置/表示处理除jsp外的所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Controller中的@RequestMapping
地址是一样的,通过请求方法
区分请求:
package com.luguosong.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author luguosong
*/
@Controller
public class TestController {
/*
* 执行get方法,表示查询
* */
@RequestMapping(value = "user", method = RequestMethod.GET)
public String get() {
System.out.println("执行get方法");
return "test";
}
/*
* 执行post方法,表示新增
* */
@RequestMapping(value = "user", method = RequestMethod.POST)
public String post() {
System.out.println("执行post方法");
return "test";
}
/*
* 执行put方法,表示修改
* */
@RequestMapping(value = "user", method = RequestMethod.PUT)
public String put() {
System.out.println("执行put方法");
return "test";
}
/*
* 执行delete方法,表示删除
* */
@RequestMapping(value = "user", method = RequestMethod.DELETE)
public String delete() {
System.out.println("执行delete方法");
return "test";
}
}
表单发送不同方法的请求:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Document</title>
</head>
<body>
<form th:action="@{/user}" method="get">
<input type="submit" value="提交get请求">
</form>
<form th:action="@{/user}" method="post">
<input type="submit" value="提交post请求">
</form>
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="提交put请求">
</form>
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="提交delete请求">
</form>
</body>
</html>
异常处理器#
Controller在执行过程中发生异常,通过异常处理器
跳转到对应视图,在视图上展示友好信息。
Spring MVC提供了一个接口:HandlerExceptionResolver
,用于处理异常。
DefaultHandlerExceptionResolver#
DefaultHandlerExceptionResolver
是Spring MVC默认的异常处理器。
比如Post方法的Controller方法,通过Get请求访问,就会进入这个处理器。
SimpleMappingExceptionResolver#
SimpleMappingExceptionResolver可以让我们自定义异常处理。
方式一:通过spring mvc配置文件配置SimpleMappingExceptionResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller"/>
<!--配置视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--配置异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--表示发生错误跳转到error视图-->
<prop key="java.lang.Exception">error</prop>
</props>
</property>
<!--表示将错误存储在request域,域名为errMsg-->
<property name="exceptionAttribute" value="errMsg"/>
</bean>
</beans>
方式二:使用注解
package com.luguosong.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @author luguosong
*/
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler
public String error(Exception e, Model model){
model.addAttribute("errMsg", e);
return "error";
}
}
拦截器(Interceptor)#
概述#
Spring MVC拦截器
作用是在请求到达Controller之前
和之后
进行拦截,可以对请求和响应进行一些特殊处理。
常见用途:
- 登录验证
- 权限校验
- 请求日志
- 更改响应
示例#
通过实现HandlerInterceptor接口
,实现拦截器。
preHandle方法
如果返回false
,请求将被拦截,不会再执行后续的拦截器和Controller。
编写拦截器:
package com.luguosong.interceptors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aopalliance.intercept.Interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @author luguosong
*/
@Component
public class Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor1 =>preHandle 执行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor1 =>postHandle 执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor1 =>afterCompletion 执行");
}
}
package com.luguosong.interceptors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aopalliance.intercept.Interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @author luguosong
*/
@Component
public class Interceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor2 =>preHandle 执行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor2 =>postHandle 执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor2 =>afterCompletion 执行");
}
}
在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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置组件扫描-->
<context:component-scan base-package="com.luguosong.controller,com.luguosong.interceptors"/>
<!--配置视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<!--方式一-->
<!--<bean class="com.luguosong.interceptors.Interceptor1"/>-->
<!--方式二:在Interceptor1上添加@Component注解,使用ref进行引用-->
<!--<ref bean="interceptor1"/>-->
<!--方式三:指定拦截路径-->
<mvc:interceptor>
<!--表示拦截所有请求-->
<mvc:mapping path="/**"/>
<!--排除指定请求-->
<mvc:exclude-mapping path="/hello-interceptor2"/>
<!--指定拦截器-->
<ref bean="interceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--表示拦截所有请求-->
<mvc:mapping path="/**"/>
<!--排除指定请求-->
<mvc:exclude-mapping path="/hello-interceptor2"/>
<!--指定拦截器-->
<ref bean="interceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
全注解开发#
编写Spring 配置类,继承AbstractAnnotationConfigDispatcherServletInitializer
类,相当于web.xml
:
package com.luguosong.config;
import jakarta.servlet.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* 该类相当于web.xml
*
* @author luguosong
*/
@Configuration
public class WebAppInitialize extends AbstractAnnotationConfigDispatcherServletInitializer {
/*
* Spring配置
* */
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
/*
* Spring MVC配置
* */
@Override
protected Class<?>[] getServletConfigClasses() {
// 指定Spring MVC配置类
return new Class[]{SpringMvcConfig.class};
}
/*
* 配置DispatcherServlet的url-pattern
* */
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/*
* 配置过滤器
* */
@Override
protected Filter[] getServletFilters() {
// 配置字符编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceRequestEncoding(true);
characterEncodingFilter.setForceResponseEncoding(true);
//配置过滤器,让form表单支持"PUT"、"DELETE"和"PATCH"方法
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
Spring MVC配置类:
package com.luguosong.config;
import com.luguosong.interceptors.MyInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import java.time.Duration;
import java.util.List;
import java.util.Properties;
/**
* 注解@ComponentScan配置包扫描,相当于<context:component-scan/>标签
* 注解@EnableWebMvc开启Spring MVC注解驱动,相当于<mvc:annotation-driven/>标签
*
* @author luguosong
*/
@Configuration
@ComponentScan("com.luguosong.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
/*
* 配置Thymeleaf视图解析器
* */
@Bean
public ThymeleafViewResolver getThymeleafViewResolver(SpringTemplateEngine springTemplateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(springTemplateEngine);
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
return resolver;
}
@Bean
public SpringTemplateEngine getSpringTemplateEngine(ITemplateResolver iTemplateResolver) {
SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.setTemplateResolver(iTemplateResolver);
return springTemplateEngine;
}
@Bean
public ITemplateResolver getITemplateResolver(ApplicationContext applicationContext) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding("UTF-8");
resolver.setCacheable(false); //开发环境关闭缓存,生产环境建议开启缓存
return resolver;
}
/*
* 开启静态资源访问
* */
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/*
* 配置试图控制器
* */
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/view-controller").setViewName("view-controller");
}
/*
* 配置异常处理器
* */
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.Exception", "error");
resolver.setExceptionMappings(properties);
resolver.setExceptionAttribute("errMsg");
resolvers.add(resolver);
}
/*
* 配置拦截器
* */
@Override
public void addInterceptors(InterceptorRegistry registry) {
MyInterceptor myInterceptor = new MyInterceptor();
registry.addInterceptor(myInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error");
}
}