对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

上下文缓存

一旦 TestContext 框架为某个测试加载了一个 ApplicationContext(或 WebApplicationContext),该上下文就会被缓存,并在同一测试套件中所有声明了相同唯一上下文配置的后续测试中重复使用。要理解缓存的工作原理,关键在于理解“唯一”和“测试套件”的含义。spring-doc.cadn.net.cn

ApplicationContext 可以通过用于加载它的配置参数组合进行唯一标识。因此,该唯一的配置参数组合被用来生成一个键(key),在此键下缓存该上下文。TestContext 框架使用以下配置参数来构建上下文缓存键:spring-doc.cadn.net.cn

例如,如果 TestClassA@ContextConfigurationlocations(或 value)属性指定了 {"app-config.xml", "test-config.xml"},则 TestContext 框架会加载相应的 ApplicationContext,并将其存储在基于这些位置生成的键所标识的 static 上下文缓存中。因此,如果 TestClassB 也为其位置定义了 {"app-config.xml", "test-config.xml"}(无论是显式定义还是通过继承隐式定义),但未定义 @WebAppConfiguration、不同的 ContextLoader、不同的激活配置文件、不同的上下文初始化器、不同的测试属性源或不同的父上下文,那么这两个测试类将共享同一个 ApplicationContext。这意味着加载应用上下文的设置开销仅在每次测试套件中发生一次,后续测试执行速度将大幅提升。spring-doc.cadn.net.cn

测试套件与分叉进程

Spring TestContext 框架将应用程序上下文存储在静态缓存中。这意味着上下文实际上是存储在一个 static 变量中。换句话说,如果测试在不同的进程中运行,每次测试执行之间静态缓存都会被清空,这实际上会禁用缓存机制。spring-doc.cadn.net.cn

为了利用缓存机制,所有测试必须在同一个进程或测试套件中运行。这可以通过在 IDE 中将所有测试作为一个组执行来实现。同样,当使用 Ant、Maven 或 Gradle 等构建框架执行测试时,务必确保构建框架不会在测试之间派生新进程。例如,如果 Maven Surefire 插件的 forkMode 被设置为 alwayspertest,TestContext 框架将无法在测试类之间缓存应用上下文,从而导致构建过程显著变慢。spring-doc.cadn.net.cn

上下文缓存的大小是有界的,默认最大大小为 32。每当达到最大大小时,将使用最近最少使用(LRU)驱逐策略来驱逐并关闭过期的上下文。您可以通过设置名为 spring.test.context.cache.maxSize 的 JVM 系统属性,从命令行或构建脚本中配置最大大小。或者,您也可以通过 SpringProperties 机制设置相同的属性。spring-doc.cadn.net.cn

由于在给定的测试套件中加载大量应用上下文会导致该套件运行时间不必要地延长,因此通常有必要确切了解已加载并缓存了多少个上下文。要查看底层上下文缓存的统计信息,可以将 org.springframework.test.context.cache 日志类别设置为 DEBUG 级别。spring-doc.cadn.net.cn

在极少数情况下,如果某个测试破坏了应用程序上下文并需要重新加载(例如,通过修改 Bean 定义或应用程序对象的状态),您可以在测试类或测试方法上添加 @DirtiesContext 注解(参见Spring 测试注解中关于 ../../annotations/integration-spring/annotation-dirtiescontext.html 的讨论)。这会指示 Spring 从缓存中移除该上下文,并在运行下一个需要相同应用程序上下文的测试之前重新构建应用程序上下文。请注意,@DirtiesContext 注解的支持由 DirtiesContextBeforeModesTestExecutionListenerDirtiesContextTestExecutionListener 提供,这两个监听器默认已启用。spring-doc.cadn.net.cn

ApplicationContext 生命周期与控制台日志

当你需要调试使用 Spring TestContext 框架执行的测试时,分析控制台输出(即输出到 SYSOUTSYSERR 流的内容)可能会很有帮助。某些构建工具和 IDE 能够将控制台输出与特定的测试关联起来;然而,有些控制台输出却难以轻松地与某个具体测试关联。spring-doc.cadn.net.cn

关于由 Spring 框架自身或在 ApplicationContext 中注册的组件所触发的控制台日志记录,理解由 Spring TestContext 框架在测试套件中加载的 ApplicationContext 的生命周期非常重要。spring-doc.cadn.net.cn

测试的 ApplicationContext 通常在准备测试类实例时加载——例如,为了将依赖注入到测试实例的 @Autowired 字段中。这意味着在 ApplicationContext 初始化期间触发的任何控制台日志通常无法与单个测试方法关联。然而,如果根据 @DirtiesContext 语义,在测试方法执行之前立即关闭上下文,则将在测试方法执行之前加载一个新的上下文实例。在后一种场景中,IDE 或构建工具可能会将控制台日志与单个测试方法关联起来。spring-doc.cadn.net.cn

测试的 ApplicationContext 可通过以下任一场景关闭。spring-doc.cadn.net.cn

如果在某个测试方法执行后,根据 @DirtiesContext 语义关闭了上下文,IDE 或构建工具可能会将控制台日志与该具体的测试方法关联起来。如果在测试类执行后,根据 @DirtiesContext 语义关闭了上下文,则在 ApplicationContext 关闭期间触发的任何控制台日志都无法与某个具体的测试方法关联。同样地,通过 JVM 关闭钩子(shutdown hook)在关闭阶段触发的任何控制台日志也无法与某个具体的测试方法关联。spring-doc.cadn.net.cn

当 Spring ApplicationContext 通过 JVM 关闭钩子(shutdown hook)关闭时,在关闭阶段执行的回调会在一个名为 SpringContextShutdownHook 的线程上运行。因此,如果您希望禁用在通过 JVM 关闭钩子关闭 ApplicationContext 时触发的控制台日志记录,可以向您的日志框架注册一个自定义过滤器,以忽略该线程发起的所有日志记录。spring-doc.cadn.net.cn