跳至主要內容

SpringMVC-MVC核心

holic-x...大约 23 分钟JAVA框架

SpringMVC-MVC核心

学习核心

  • SpringMVC的核心概念
  • Spring流程分析(处理流程)
  • 全局异常处理
  • 参数校验
  • 拦截器

学习资料

MVC模式

MVC:Model(模型)、View(视图)和Controller(控制)

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。

View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。

Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

SpringMVC

SpringMVC

  • 【版本1】视图阶段(一些老旧项目):JSP等
  • 【版本2】前后端分离阶段:接口开发、异步请求等

image-20240610194854331

// 版本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开发

架构核心流程说明

image-20240610094140025

​ Spring框架核心组件:

  • DispatchServlet:前端控制器
  • HandlerMapping:处理器映射器
  • HandlerAdapter:处理器适配器
  • Handler:处理器
  • ViewResolver:视图解析器
步骤操作说明
1用户发起请求DispatchServlet接受请求并处理(传递给SpringMVC层)
2请求查询HandlerDispatchServlet会根据servlet.xml中的配置找到对应处理器映射器HandlerMapping,获取到相应的处理器执行链HandlerExecutionChain、处理器Handler
其中HandlerExecutionChain(HandlerInterceptor1,HandlerInterceptor2,......Handler)
3请求执行处理器DispatchServlet通过获取的Handler选择合适的HandlerAdapter(处理器适配器),由HandlerAdapter负责调用Handler
4Handler执行由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.案例分析

项目构建&配置

项目结构参考

image-20240610145112624

项目构建

​ 创建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提示获取到相应的文件来源,以解决飘红问题

image-20240612082751591

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

项目部署测试

下载tomcatopen in new window,并在idea中引入、配置

tomcat官网下载open in new window

  • tomcat =》Server配置:确认tomcat版本、依赖的JRE环境、相关端口号配置
  • tomcat =》Deployment配置:添加部署包、配置context域

image-20240610140945307image-20240610141351401

​ 启动后查看启动日志,如果启动时console控制台打印tomcat日志出现乱码问题,可能是由于idea的console默认的编码格式配置和tomcat的不一致,需依次检查并改为一致即可。修改完成,重启确认日志是否打印正常

  • idea的console配置检查:Settings=》Editor=》Console=》Default Encoding

image-20240610141910128

  • tomcat的日志配置检查:tomcat安装/解压目录下的conf/logging.properties

image-20240610142104123

项目测试

​ 启动tomcat访问:http://localhost:8080/springmvc/user(此处springmvc为web应用上下文、user则映射到控制器部分)

image-20240610144947671

项目部署失败排查思路

​ 如果项目启动后访问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

image-20240610143458481

​ 根据上述日志,可以检查相应的Modules有没有配置web.xml

image-20240610143656774

​ 配置完成重启项目,再次查看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 假定拦截器本身已经处理请求(并且,例如,呈现适当的视图)并且不会继续执行其他拦截器和执行链中的实际处理程序。

官方配置拦截器示例参考open in new window,可以通过在各个 HandlerMapping 实现上使用 setter 来直接注册拦截器

postHandle 方法对于 @ResponseBodyResponseEntity 的方法不太有用,它们的响应是在 HandlerAdapter 中和 postHandle 之前编写和提交的。这意味着对响应进行任何更改都为时已晚,例如添加额外的标头。对于此类场景,可以实现 ResponseBodyAdvice 并将其声明为 Controller Adviceopen in new windowbean 或直接在 RequestMappingHandlerAdapter 上进行配置

2.解析器

​ DispatcherServlet 会加载多种解析器来处理请求,常见解析器如下:

解析器说明
HandlerExceptionResolveropen in new window解决异常的策略,可能将它们映射到处理程序、HTML 错误视图或其他目标
ViewResolveropen in new window将从处理程序返回的基于字符串的逻辑视图名称解析为用于呈现响应的实际视图
LocaleResolveropen in new windowLocaleContextResolveropen in new window解析用户正在使用的本地化设置,可能还有他们的时区,以便能够提供国际化的视图
ThemeResolveropen in new window解析 Web 应用程序可以使用的主题——例如,提供个性化布局
MultipartResolveropen in new window通过一些 multipart 解析库的帮助解析 multipart 请求(例如,通过浏览器上传文件)

HandlerExceptionResolver

​ 在 WebApplicationContext 中声明的 HandlerExceptionResolver 用于解决请求处理期间抛出的异常。这些异常解析器允许自定义逻辑来解决异常。

​ 对于 HTTP 缓存支持,处理程序可以使用 WebRequestcheckNotModified 方法,以及用于控制器的 HTTP 缓存中所述的带注释控制器的更多选项。

​ 如果在请求映射期间发生异常或从请求处理程序(例如 @Controller)抛出异常,则 DispatcherServlet 委托 HandlerExceptionResolver 链来解决异常并提供替代处理,这通常是错误响应。下表列出了可用的 HandlerExceptionResolver 实现:

HandlerExceptionResolver说明
SimpleMappingExceptionResolver异常类名称和错误视图名称之间的映射。用于在浏览器应用程序中呈现错误页面
DefaultHandlerExceptionResolveropen in new window解决由 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 定义了 ViewResolverView 接口,让用户可以在浏览器中渲染模型,而无需限定于特定的视图技术。ViewResolver 提供视图名称和实际视图之间的映射。View 解决了在移交给特定视图技术之前准备数据的问题。下表提供了有关 ViewResolver 一些子类:

ViewResolverDescription
AbstractCachingViewResolverAbstractCachingViewResolver 的子类缓存它们解析的视图实例。缓存提高了某些视图技术的性能。您可以通过将 cache 属性设置为 false 来关闭缓存。此外,如果您必须在运行时刷新某个视图(例如,修改 FreeMarker 模板时),您可以使用 removeFromCache(String viewName, Locale loc) 方法。
UrlBasedViewResolverViewResolver 接口的简单实现,无需显式映射定义即可将逻辑视图名称直接解析为 URL。如果您的逻辑名称以直接的方式匹配您的视图资源的名称,而不需要任意映射,那么这是合适的。
InternalResourceViewResolverUrlBasedViewResolver 的子类,支持 InternalResourceView(实际上是 Servlet 和 JSP)以及 JstlViewTilesView 等子类。可以使用 setViewClass(..) 为该解析器生成的所有视图指定视图类。
FreeMarkerViewResolverUrlBasedViewResolver 的子类,支持 FreeMarkerView 和它们的自定义子类。
ContentNegotiatingViewResolverViewResolver 接口的实现,该接口根据请求文件名或 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()。因此,此前缀对 InternalResourceViewResolverInternalResourceView(对于 JSP)没有用,但如果您使用另一种视图技术但仍想强制转发由 Servlet/JSP 引擎处理的资源,它可能会有所帮助

(4)内容协商

ContentNegotiatingViewResolver 本身不解析视图,而是委托给其他视图解析器并选择类似于客户端请求的表示的视图。可以从 Accept 头或查询参数(例如,"/path?format=pdf")确定表示形式。

ContentNegotiatingViewResolver 通过将请求媒体类型与其每个 ViewResolver 关联的 View 支持的媒体类型(也称为 Content-Type)进行比较,来选择合适的 View 来处理请求。列表中第一个具有兼容 Content-Type 的视图将处理结果返回给客户端。如果 ViewResolver 链无法提供兼容的视图,则会查阅通过 DefaultViews 属性指定的视图列表。后一个选项适用于单例视图,它可以呈现当前资源的适当表示,而不管逻辑视图名称如何。Accept 标头可以包含通配符(例如 text/*),在这种情况下,Content-Typetext/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 解析器可供选择。

LocaleResolver

除了获取客户端的区域设置外,了解其时区通常也很有用。LocaleContextResolver 接口提供了 LocaleResolver 的扩展,让解析器提供更丰富的 LocaleContext,其中可能包括时区信息。

如果可用,可以使用 RequestContext.getTimeZone() 方法获取用户的 TimeZone。在 Spring 的 ConversionService 中注册的任何日期/时间 ConverterFormatter 对象会自动使用时区信息。

标头解析器

此 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,以查看是否指定了 LocaleTimeZone。如果是,它会使用指定的详细信息。通过使用此 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类名 + LOCALEcookie 名
cookieMaxAgeServlet container defaultcookie 在客户端上保留的最长时间。如果指定了“-1”,则不会保留 cookie。它仅在客户端关闭浏览器之前可用。
cookiePath/将 cookie 的可见性限制在您网站的特定部分。当指定 cookiePath 时,cookie 仅对该路径及其下方的路径可见。
SessionLocaleResolver

SessionLocaleResolver 允许您从可能与用户请求相关联的会话中检索 LocaleTimeZone。与 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 提供的主题解析器:

ClassDescription
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 配置中声明一个名为 multipartResolverMultipartResolverDispatcherServlet 检测到它并将其应用于传入请求。 当接收到内容类型为 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 配置好,就可以添加一个名为 multipartResolverStandardServletMultipartResolver 类型的 bean。

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3