SpringMVC-MVC核心
SpringMVC-MVC核心
学习核心
- SpringMVC的核心概念
- Spring流程分析(处理流程)
- 全局异常处理
- 参数校验
- 拦截器
学习资料
MVC模式
MVC:Model(模型)、View(视图)和Controller(控制)
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
SpringMVC
SpringMVC
- 【版本1】视图阶段(一些老旧项目):JSP等
- 【版本2】前后端分离阶段:接口开发、异步请求等
// 版本1:JSP版本
[1]用户发送出请求到前端控制器DispatcherServlet
[2]DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
[3]HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet
[4]DispatcherServlet调用HandlerAdapter(处理器适配器)
[5]HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
[6]Controller执行完成返回ModelAndView对象
[7]HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
[8]]DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
[9]ViewReslover解析后返回具体View(视图)
[10]DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
[11]DispatcherServlet响应用户
对于版本2中的前后端分离概念,则是在适配器处理之后的节点操作有些不同,不需要渲染视图,而是通过将响应数据封装为JSON返回给前端
[1]用户发送出请求到前端控制器DispatcherServlet
[2]DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
[3]HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet
[4]DispatcherServet调用HandlerAdapter(处理器适配器)
[5]HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
[6]方法上添加了@ResponseBody
[7]通过HttpMessageConverter来返回结果转换为JSON并响应
1.核心流程
SpringMVC是Spring框架的一部分,属于表现层的框架。Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发
架构核心流程说明
Spring框架核心组件:
- DispatchServlet:前端控制器
- HandlerMapping:处理器映射器
- HandlerAdapter:处理器适配器
- Handler:处理器
- ViewResolver:视图解析器
步骤 | 操作 | 说明 |
---|---|---|
1 | 用户发起请求 | DispatchServlet接受请求并处理(传递给SpringMVC层) |
2 | 请求查询Handler | DispatchServlet会根据servlet.xml中的配置找到对应处理器映射器HandlerMapping,获取到相应的处理器执行链HandlerExecutionChain、处理器Handler 其中HandlerExecutionChain(HandlerInterceptor1,HandlerInterceptor2,......Handler) |
3 | 请求执行处理器 | DispatchServlet通过获取的Handler选择合适的HandlerAdapter(处理器适配器),由HandlerAdapter负责调用Handler |
4 | Handler执行 | 由Handler处理请求,返回ModelAndView |
5 | 返回ModelAndView对象 | HandlerAdapter处理完请求之后会将返回的ModelAndView对象返回给DispatchServlet |
6 | 解析视图 | DispatchServlet将ModelAndView对象传递给ViewResolver进行视图解析,获取到具体的View |
7 | 渲染视图 | DispatchServlet对View进行渲染,并将模型数据填充到视图 |
8 | 响应用户请求 | DispatchServlet最终响应客户端请求 |
流程补充
上述内容仅针对核心流程进行讲解,实际上还有拦截器、本地解析、文件上传解析等内容嵌入
Filter(ServletFilter):进入Servlet前可以有preFilter,Servlet处理之后可以由postFilter
解析器:除了ViewResolver视图解析器,还有LocaleResolver(国际化)、ThemeResolver(视图样式)、MultipartResolver(文件上传请求解析)等解析器
2.案例分析
项目构建&配置
项目结构参考
项目构建
创建Maven工程(JDK1.8),配置Maven依赖
<?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.noob.framework</groupId>
<artifactId>springmvc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.3.9</spring.version>
<servlet.version>4.0.1</servlet.version>
</properties>
<dependencies>
<!-- 引入MVC相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- 工具类引入 -->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
业务代码编写(MVC层构建)
- User
@Data
public class User {
private int id;
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
- UserDaoImpl
@Repository
public class UserDaoImpl {
public List<User> findUser(){
return Collections.singletonList(new User("noob",18));
}
}
- UserService、UserServiceImpl
public interface UserService {
public List<User> findUserList();
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDaoImpl userDao;
@Override
public List<User> findUserList() {
return userDao.findUser();
}
}
- UserController
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user")
public ModelAndView list(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView();
mav.addObject("dateTime", new Date());
mav.addObject("userList",userService.findUserList());
mav.setViewName("userList");
return mav;
}
}
spring配置(springmvc.xml)
除却常见的Spring配置,此处加入了静态资源处理和视图解析器配置
如果xml的xmlns引入出现飘红,则根据idea提示获取到相应的文件来源,以解决飘红问题
<?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"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描注解 -->
<context:component-scan base-package="com.noob.framework.springmvc"/>
<!-- 静态资源处理 -->
<mvc:default-servlet-handler/>
<!-- 开启注解 -->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml 配置 (webapp/web.xml)
初始化启动指定spring配置文件位置:对应resources/springmvc.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_3_1.xsd"
version="3.1">
<display-name>SpringMVC DEMO</display-name>
<servlet>
<servlet-name>springmvc-demo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-demo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
视图层配置(JSP文件)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>User List</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<c:if test="${!empty dateTime}">当前系统时间:${dateTime}</c:if>
<c:if test="${!empty userList}">
<table class="table table-bordered table-striped">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>
</c:forEach>
</table>
</c:if>
</div>
</body>
</html>
项目部署测试
下载tomcat,并在idea中引入、配置
- tomcat =》Server配置:确认tomcat版本、依赖的JRE环境、相关端口号配置
- tomcat =》Deployment配置:添加部署包、配置context域
启动后查看启动日志,如果启动时console控制台打印tomcat日志出现乱码问题,可能是由于idea的console默认的编码格式配置和tomcat的不一致,需依次检查并改为一致即可。修改完成,重启确认日志是否打印正常
- idea的console配置检查:Settings=》Editor=》Console=》Default Encoding
- tomcat的日志配置检查:tomcat安装/解压目录下的
conf/logging.properties
项目测试
启动tomcat访问:http://localhost:8080/springmvc/user(此处springmvc为web应用上下文、user则映射到控制器部分)
项目部署失败排查思路
如果项目启动后访问url无法得到预期的结果,则进一步检查tomcat启动日志定位问题,确认项目是否部署成功。从console控制台可以跟踪到项目的部署路径,点击进去路径查看信息即可
C:\custom\develop\CONFIG\tomcat\tomcat-9.0.24-default\bin\catalina.bat run
[2024-06-10 02:03:29,483] Artifact springmvc-demo:war exploded: Waiting for server connection to start artifact deployment...
Using CATALINA_BASE: "C:\Users\holic-x\AppData\Local\JetBrains\IntelliJIdea2024.1\tomcat\501dae5f-649b-4e49-9db5-8bf2f7a162f4"
Using CATALINA_HOME: "C:\custom\develop\CONFIG\tomcat\tomcat-9.0.24-default"
Using CATALINA_TMPDIR: "C:\custom\develop\CONFIG\tomcat\tomcat-9.0.24-default\temp"
Using JRE_HOME: "C:\custom\develop\CONFIG\jdk\jdk1.8\jdk1.8.0_151"
Using CLASSPATH: "C:\custom\develop\CONFIG\tomcat\tomcat-9.0.24-default\bin\bootstrap.jar;C:\custom\develop\CONFIG\tomcat\tomcat-9.0.24-default\bin\tomcat-juli.jar"
Connected to the target VM, address: '127.0.0.1:62505', transport: 'socket'
10-Jun-2024 14:03:29.993 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log Server.服务器版本: Apache Tomcat/9.0.24
10-Jun-2024 14:03:29.995 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log Server.构建: Aug 14 2019 21:16:42 UTC
根据上述日志,可以检查相应的Modules有没有配置web.xml
配置完成重启项目,再次查看tomcat日志,访问正常则会查找对应的DispatchServlet
[RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.ApplicationContext.log Initializing Spring DispatcherServlet 'springmvc-demo'
如果项目部署成功则进一步确认项目配置,排查404访问错误等问题
SpringMVC扩展概念
上述介绍了SpringMVC的核心流程,在这个核心流程中会涉及到一些节点功能的扩展,用于适配不同的业务场景改开,此处进一步扩展说明
1.拦截器
所有 HandlerMapping
实现都支持处理拦截器,当想要将特定功能应用于某些请求时,这些拦截器很有用(例如检查主体)。拦截器必须使用 org.springframework.web.servlet
包中的三个方法实现 HandlerInterceptor
,这三个方法应该提供足够的灵活性来进行各种预处理和后处理:
preHandle(..)
:在实际 handler 之前执行postHandle(..)
:handler 之后执行afterCompletion(..)
:完成请求后执行
preHandle(..)
方法返回一个布尔值。可以使用此方法中断或继续执行链的处理。当此方法返回 true 时,处理程序执行链将继续。当它返回 false 时,DispatcherServlet
假定拦截器本身已经处理请求(并且,例如,呈现适当的视图)并且不会继续执行其他拦截器和执行链中的实际处理程序。
官方配置拦截器示例参考,可以通过在各个 HandlerMapping
实现上使用 setter 来直接注册拦截器
postHandle
方法对于 @ResponseBody
和 ResponseEntity
的方法不太有用,它们的响应是在 HandlerAdapter
中和 postHandle
之前编写和提交的。这意味着对响应进行任何更改都为时已晚,例如添加额外的标头。对于此类场景,可以实现 ResponseBodyAdvice
并将其声明为 Controller Advicebean 或直接在 RequestMappingHandlerAdapter
上进行配置
2.解析器
DispatcherServlet 会加载多种解析器来处理请求,常见解析器如下:
解析器 | 说明 |
---|---|
HandlerExceptionResolver | 解决异常的策略,可能将它们映射到处理程序、HTML 错误视图或其他目标 |
ViewResolver | 将从处理程序返回的基于字符串的逻辑视图名称解析为用于呈现响应的实际视图 |
LocaleResolver、LocaleContextResolver | 解析用户正在使用的本地化设置,可能还有他们的时区,以便能够提供国际化的视图 |
ThemeResolver | 解析 Web 应用程序可以使用的主题——例如,提供个性化布局 |
MultipartResolver | 通过一些 multipart 解析库的帮助解析 multipart 请求(例如,通过浏览器上传文件) |
HandlerExceptionResolver
在 WebApplicationContext
中声明的 HandlerExceptionResolver
用于解决请求处理期间抛出的异常。这些异常解析器允许自定义逻辑来解决异常。
对于 HTTP 缓存支持,处理程序可以使用 WebRequest
的 checkNotModified
方法,以及用于控制器的 HTTP 缓存中所述的带注释控制器的更多选项。
如果在请求映射期间发生异常或从请求处理程序(例如 @Controller
)抛出异常,则 DispatcherServlet
委托 HandlerExceptionResolver
链来解决异常并提供替代处理,这通常是错误响应。下表列出了可用的 HandlerExceptionResolver
实现:
HandlerExceptionResolver | 说明 |
---|---|
SimpleMappingExceptionResolver | 异常类名称和错误视图名称之间的映射。用于在浏览器应用程序中呈现错误页面 |
DefaultHandlerExceptionResolver | 解决由 Spring MVC 引发的异常并将它们映射到 HTTP 状态代码 |
ResponseStatusExceptionResolver | 使用 @ResponseStatus 注解解决异常,并根据注解中的值将它们映射到 HTTP 状态代码 |
ExceptionHandlerExceptionResolver | 通过在 @Controller 或 @ControllerAdvice 类中调用 @ExceptionHandler 方法来解决异常 |
(1)解析器链
可以通过在 Spring 配置中声明多个 HandlerExceptionResolver
bean 并根据需要设置它们的顺序属性来构成异常解析器链。order 属性越高,异常解析器的位置就越靠后。
HandlerExceptionResolver
的约定使它可以返回以下内容:
- 指向错误视图的
ModelAndView
- 如果异常是在解析器中处理的,则为空的
ModelAndView
- 如果异常仍未解决,则为 null,供后续解析器尝试,如果异常仍然存在,则允许向上冒泡到 Servlet 容器
MVC Config 自动为默认的 Spring MVC 异常、@ResponseStatus
注释的异常和对 @ExceptionHandler
方法的支持声明内置解析器。可以自定义该列表或替换它
(2)错误页面
如果异常仍未被任何 HandlerExceptionResolver
处理并因此继续传播,或者如果响应状态设置为错误状态(即 4xx、5xx),Servlet 容器可以在 HTML 中呈现默认错误页面。要自定义容器的默认错误页面,您可以在 web.xml
中声明一个错误页面映射。以下示例显示了如何执行此操作:
<error-page>
<location>/error</location>
</error-page>
在前面的示例中,当出现异常或响应具有错误状态时,Servlet 容器会在容器内将 ERROR 分派到配置的 URL(例如,/error
)。然后由 DispatcherServlet
处理,可能将其映射到 @Controller
,后者可以返回带有模型的错误视图名称或呈现 JSON 响应,如以下示例所示:
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
return map;
}
}
提示:Servlet API 不提供在 Java 中创建错误页面映射的方法。但是,可以同时使用
WebApplicationInitializer
和最小的web.xml
ViewResolver
Spring MVC 定义了 ViewResolver
和 View
接口,让用户可以在浏览器中渲染模型,而无需限定于特定的视图技术。ViewResolver
提供视图名称和实际视图之间的映射。View
解决了在移交给特定视图技术之前准备数据的问题。下表提供了有关 ViewResolver 一些子类:
ViewResolver | Description |
---|---|
AbstractCachingViewResolver | AbstractCachingViewResolver 的子类缓存它们解析的视图实例。缓存提高了某些视图技术的性能。您可以通过将 cache 属性设置为 false 来关闭缓存。此外,如果您必须在运行时刷新某个视图(例如,修改 FreeMarker 模板时),您可以使用 removeFromCache(String viewName, Locale loc) 方法。 |
UrlBasedViewResolver | ViewResolver 接口的简单实现,无需显式映射定义即可将逻辑视图名称直接解析为 URL。如果您的逻辑名称以直接的方式匹配您的视图资源的名称,而不需要任意映射,那么这是合适的。 |
InternalResourceViewResolver | UrlBasedViewResolver 的子类,支持 InternalResourceView (实际上是 Servlet 和 JSP)以及 JstlView 和 TilesView 等子类。可以使用 setViewClass(..) 为该解析器生成的所有视图指定视图类。 |
FreeMarkerViewResolver | UrlBasedViewResolver 的子类,支持 FreeMarkerView 和它们的自定义子类。 |
ContentNegotiatingViewResolver | ViewResolver 接口的实现,该接口根据请求文件名或 Accept 标头解析视图。 |
BeanNameViewResolver | 将视图名称解释为当前应用程序上下文中的 bean 名称的 ViewResolver 接口的实现。这是一个非常灵活的变体,允许根据不同的视图名称混合和匹配不同的视图类型。每个这样的“视图”都可以定义为一个 bean,例如 在 XML 或配置类中。 |
(1)处理
可以通过声明多个解析器来构成视图解析器链,可通过设置 order 属性来指定顺序(顺序属性越高,视图解析器在链中的位置就越靠后)
ViewResolver
的约定指定它可以返回 null 以指示找不到视图。但是,对于 JSP 和 InternalResourceViewResolver
,确定 JSP 是否存在的唯一方法是通过 RequestDispatcher
执行分派。因此,必须始终将 InternalResourceViewResolver
配置为在视图解析器的整体顺序中排在最后。
MVC Config 为视图解析器和添加无逻辑视图控制器提供了专用的配置 API,这对于没有控制器逻辑的 HTML 模板渲染很有用
(2)重定向
视图名称中的特殊前缀 redirect:
可以实现一个重定向。UrlBasedViewResolver
(及其子类)将此识别为需要重定向的指令。视图名称的其余部分是重定向 URL。
最终效果与控制器返回 RedirectView
相同,但现在控制器本身可以根据逻辑视图名称进行操作。逻辑视图名称(例如 redirect:/myapp/some/resource
)相对于当前 Servlet 上下文重定向,而名称(例如 redirect:https://myhost.com/some/arbitrary/path
)重定向到绝对 URL。
请注意,如果使用 @ResponseStatus
注解标记控制器方法,则注解值优先于 RedirectView
设置的响应状态
(3)转发
视图名称中的特殊前缀 forward:
可以实现一个转发。这将创建一个 InternalResourceView
,它执行 RequestDispatcher.forward()
。因此,此前缀对 InternalResourceViewResolver
和 InternalResourceView
(对于 JSP)没有用,但如果您使用另一种视图技术但仍想强制转发由 Servlet/JSP 引擎处理的资源,它可能会有所帮助
(4)内容协商
ContentNegotiatingViewResolver
本身不解析视图,而是委托给其他视图解析器并选择类似于客户端请求的表示的视图。可以从 Accept
头或查询参数(例如,"/path?format=pdf"
)确定表示形式。
ContentNegotiatingViewResolver
通过将请求媒体类型与其每个 ViewResolver
关联的 View
支持的媒体类型(也称为 Content-Type
)进行比较,来选择合适的 View
来处理请求。列表中第一个具有兼容 Content-Type
的视图将处理结果返回给客户端。如果 ViewResolver
链无法提供兼容的视图,则会查阅通过 DefaultViews
属性指定的视图列表。后一个选项适用于单例视图,它可以呈现当前资源的适当表示,而不管逻辑视图名称如何。Accept
标头可以包含通配符(例如 text/*
),在这种情况下,Content-Type
为 text/xml
的 View 是兼容的匹配项
LocaleResolver
大部分的 Spring 架构都支持国际化,就像 Spring web MVC 框架所做的那样。DispatcherServlet
允许您使用客户端的语言环境自动解析消息。这是通过 LocaleResolver
对象完成的。
当收到请求时,DispatcherServlet
会寻找语言环境解析器,如果找到,它会尝试使用它来设置 Locale 环境。通过使用 RequestContext.getLocale()
方法,您始终可以检索由 Locale 解析器解析的语言环境。
除了自动识别 Locale 环境之外,您还可以为 handle 映射附加拦截器,在特定情况下更改 Locale 环境设置(例如,基于请求中的参数)。
Locale 解析器和拦截器在 org.springframework.web.servlet.i18n
包中定义,并以正常方式在您的应用程序上下文中配置。Spring 中有以下 Locale 解析器可供选择。
- Time Zone(opens new window)
- Header Resolver(opens new window)
- Cookie Resolver(opens new window)
- Session Resolver(opens new window)
- Locale Interceptor
LocaleResolver
除了获取客户端的区域设置外,了解其时区通常也很有用。LocaleContextResolver
接口提供了 LocaleResolver
的扩展,让解析器提供更丰富的 LocaleContext
,其中可能包括时区信息。
如果可用,可以使用 RequestContext.getTimeZone()
方法获取用户的 TimeZone
。在 Spring 的 ConversionService
中注册的任何日期/时间 Converter
和 Formatter
对象会自动使用时区信息。
标头解析器
此 Locale 解析器检查客户端(例如网络浏览器)发送的请求中的 accept-language
头。通常,此头字段包含客户端操作系统的区域信息。请注意,此解析器不支持时区信息。
CookieLocaleResolver
This locale resolver inspects a Cookie
that might exist on the client to see if a Locale
or TimeZone
is specified. If so, it uses the specified details. By using the properties of this locale resolver, you can specify the name of the cookie as well as the maximum age. The following example defines a CookieLocaleResolver
:
此 Locale 解析器检查客户端上是否存在 Cookie
,以查看是否指定了 Locale
或 TimeZone
。如果是,它会使用指定的详细信息。通过使用此 Locale 解析器的属性,可以指定 cookie 的名称以及最长期限。以下示例定义了 CookieLocaleResolver
:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="clientlanguage"/>
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) -->
<property name="cookieMaxAge" value="100000"/>
</bean>
下表描述了 CookieLocaleResolver
的属性:
属性 | 默认值 | Description |
---|---|---|
cookieName | 类名 + LOCALE | cookie 名 |
cookieMaxAge | Servlet container default | cookie 在客户端上保留的最长时间。如果指定了“-1”,则不会保留 cookie。它仅在客户端关闭浏览器之前可用。 |
cookiePath | / | 将 cookie 的可见性限制在您网站的特定部分。当指定 cookiePath 时,cookie 仅对该路径及其下方的路径可见。 |
SessionLocaleResolver
SessionLocaleResolver
允许您从可能与用户请求相关联的会话中检索 Locale
和 TimeZone
。与 CookieLocaleResolver
相比,此策略将本地选择的 locale 设置存储在 Servlet 容器的 HttpSession
中。因此,这些设置对于每个会话都是临时的,因此会在每个会话结束时丢失。
注意,这与外部会话管理机制(例如 Spring Session 项目)没有直接关系。此 SessionLocaleResolver
根据当前 HttpServletRequest
评估和修改相应的 HttpSession
属性。
LocaleChangeInterceptor
可以通过将 LocaleChangeInterceptor
添加到一个 HandlerMapping
定义来启用区域设置更改。它检测请求中的参数并相应地更改 Locale 环境,在调度程序的应用程序上下文中调用 LocaleResolver
上的 setLocale
方法。下面的示例显示调用所有包含名为 siteLanguage
的参数的 *.view
资源,以更改语言环境。因此,例如,对 URL https://www.sf.net/home.view?siteLanguage=nl
的请求将站点语言更改为荷兰语。以下示例显示了如何拦截语言环境:
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="siteLanguage"/>
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor"/>
</list>
</property>
<property name="mappings">
<value>/**/*.view=someController</value>
</property>
</bean>
ThemeResolver
您可以应用 Spring Web MVC 框架主题来设置应用程序的整体外观,从而增强用户体验。主题是静态资源的集合,通常是样式表和图像,它们会影响应用程序的视觉风格。
要在 Web 应用程序中使用主题,必须设置 org.springframework.ui.context.ThemeSource
接口的实现。WebApplicationContext
接口扩展了 ThemeSource
但将其职责委托给了专门的实现。默认情况下,委托是 org.springframework.ui.context.support.ResourceBundleThemeSource
,它从类的根路径加载属性文件。要使用自定义的 ThemeSource
实现或配置 ResourceBundleThemeSource
的基本名称前缀,您可以在应用程序上下文中使用保留名称 themeSource
注册一个 bean。Web 应用程序上下文自动检测具有该名称的 bean 并使用它。
当使用 ResourceBundleThemeSource
时,主题是在一个简单的属性文件中定义的。属性文件列出了构成主题的资源,如以下示例所示:
styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg
属性的键是从视图代码中引用主题元素的名称。对于 JSP,通常使用 spring:theme
自定义标签来执行此操作,它与 spring:message
标签非常相似。以下 JSP 片段使用前面示例中定义的主题来自定义外观:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
<head>
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/>
</head>
<body style="background=<spring:theme code='background'/>">
...
</body>
</html>
默认情况下, ResourceBundleThemeSource
使用空的基本名称前缀。因此,属性文件是从类路径的根加载的。因此,可以将 cool.properties
主题定义放在类路径根目录中(例如,在 /WEB-INF/classes
中)。ResourceBundleThemeSource
使用标准的 Java 资源包加载机制,允许主题完全国际化。例如,可以有一个 /WEB-INF/classes/cool_nl.properties
,它引用一个带有荷兰语文本的特殊背景图像。
定义主题后,可以决定使用哪个要使用的主题。DispatcherServlet
查找名为 themeResolver
的 bean 以找出要使用的 ThemeResolver
实现。主题解析器的工作方式与 LocaleResolver
大致相同。它检测用于特定请求的主题,也可以更改请求的主题。下表描述了 Spring 提供的主题解析器:
Class | Description |
---|---|
FixedThemeResolver | 选择一个固定的主题,使用 defaultThemeName 属性设置。 |
SessionThemeResolver | 主题在用户的 HTTP 会话中维护。 它只需要为每个会话设置一次,但不会在会话之间持续存在。 |
CookieThemeResolver | 所选主题存储在客户端的 cookie 中。 |
Spring 还提供了一个 ThemeChangeInterceptor
,它允许使用一个简单的请求参数在每个请求上更改主题
MultipartResolver
org.springframework.web.multipart
包中的 MultipartResolver
是一种解析 multipart 请求(包括文件上传)的策略。 有一个基于容器的 StandardServletMultipartResolver
实现,用于 Servlet 多部分请求解析。 请注意,从具有新 Servlet 5.0+ 基线的 Spring Framework 6.0 开始,基于 Apache Commons FileUpload 的过时的 CommonsMultipartResolver
不再可用。
要启用 multipart 处理,需要在 DispatcherServlet
Spring 配置中声明一个名为 multipartResolver
的 MultipartResolver
。 DispatcherServlet
检测到它并将其应用于传入请求。 当接收到内容类型为 multipart/form-data
的 POST 时,解析器解析将当前 HttpServletRequest
包装为 MultipartHttpServletRequest
的内容,以提供对已解析文件的访问以及将部分作为请求参数公开。
Servlet 多部分解析需要通过 Servlet 容器配置启用。 为此:
- 在 Java 中,在 Servlet 注册上设置一个
MultipartConfigElement
。 - 在
web.xml
中,将<multipart-config>
部分添加到 servlet 声明。
以下示例显示如何在 Servlet 注册上设置 MultipartConfigElement
:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// ...
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
}
}
一旦 Servlet multipart 配置好,就可以添加一个名为 multipartResolver
的 StandardServletMultipartResolver
类型的 bean。