SpringCore完整学习教程6,入门级别

简介: 本文是Spring Core学习教程的第六部分,涵盖了Spring Boot中的任务执行和调度、异步处理,以及使用@SpringBootTest进行集成测试的多种方式,包括使用MockMvc和WebTestClient进行模拟测试和使用随机端口启动服务器进行测试。

本章从第7章开始:

7. Task Execution and Scheduling

在上下文中没有Executor bean的情况下,Spring Boot会自动配置一个ThreadPoolTaskExecutor,它具有合理的默认值,可以自动关联到异步任务执行(@EnableAsync)和Spring MVC异步请求处理。

如果你在上下文中定义了自定义Executor,常规任务执行(即@EnableAsync)将透明地使用它,但不会配置Spring MVC支持,因为它需要AsyncTaskExecutor实现(名为applicationTaskExecutor)。根据您的目标安排,您可以将Executor更改为ThreadPoolTaskExecutor或定义ThreadPoolTaskExecutor和AsyncConfigurer封装您的自定义Executor。

自动配置的TaskExecutorBuilder允许您轻松地创建实例来重现默认情况下自动配置所做的事情。

线程池使用8个核心线程,这些线程可以根据负载增长和收缩。这些默认设置可以使用spring.task.execution命名空间进行微调,如下例所示:

spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s

测试:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class TestService {

    @Async("taskExecutor")
    public void test() {
        // do something
    }
}

在这个测试代码中,@Async注解表示这个方法是异步执行的,"taskExecutor"表示使用的线程池的名称。在使用时,只需要注入TestService并调用test()方法即可。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
@ConfigurationPropertiesScan(basePackages = "com.example.demo.demos")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DemoApplication.class);
        application.run(args);
    }

}

开启一下Spring的异步特性的。

这会将线程池更改为使用有界队列,以便当队列满时(100个任务),线程池增加到最多16个线程。当线程空闲10秒(而不是默认的60秒)时回收它们,池的收缩会更加激进。

如果需要与计划任务执行相关联(例如使用@ enablesscheduling), ThreadPoolTaskScheduler也可以自动配置。线程池默认使用一个线程,可以使用spring.task.scheduling命名空间对其设置进行微调,如下例所示:

spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2

如果需要创建自定义执行器或调度器,那么TaskExecutorBuilder bean和TaskSchedulerBuilder bean都可以在上下文中使用。

8. Testing

Spring Boot提供了许多实用工具和注释,可以在测试应用程序时提供帮助。测试支持由两个模块提供:spring-boot-test包含核心项,spring-boot-test-autoconfigure支持测试的自动配置。
大多数开发人员使用Spring - Boot - Starter -test“Starter”,它导入Spring - Boot测试模块以及JUnit Jupiter、AssertJ、Hamcrest和许多其他有用的库。

如果您有使用JUnit 4的测试,那么可以使用JUnit 5的老式引擎来运行它们。要使用复古引擎,需要在junit-vintage-engine上添加一个依赖项,如下例所示:

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

8.1. Test Scope Dependencies

spring-boot-starter-test“Starter”(在测试范围内)包含以下提供的库:

  • JUnit 5:单元测试Java应用程序的事实标准

  • Spring Test & Spring Boot Test: 对Spring Boot应用程序的实用程序和集成测试支持。

  • AssertJ: 一个流畅的断言库

  • Hamcrest: 匹配器对象库(也称为约束或谓词)。

  • Mockito: 一个Java模拟框架。

  • JSONassert: 一个JSON断言库。

  • JsonPath: 用于JSON的XPath。

我们通常发现这些公共库在编写测试时很有用。如果这些库不能满足您的需求,您可以添加自己的额外测试依赖项。

8.2. Testing Spring Applications

依赖注入的一个主要优点是,它可以使代码更容易进行单元测试。您可以通过使用new操作符实例化对象,甚至不需要使用Spring。您还可以使用模拟对象来代替真正的依赖项。

通常,您需要超越单元测试并开始集成测试(使用Spring ApplicationContext)。能够在不需要部署应用程序或连接到其他基础设施的情况下执行集成测试是很有用的。

Spring框架包括一个专门用于这种集成测试的测试模块。你可以直接声明一个依赖于org.springframework:spring-test或使用spring-boot-starter-test“Starter”来传递地拉入它。

8.3. Testing Spring Boot Applications

Spring Boot应用程序是一个Spring ApplicationContext,所以除了通常使用普通Spring上下文所做的测试之外,不需要做任何特别的事情来测试它。

默认情况下,只有在使用SpringApplication创建时,Spring Boot的外部属性、日志记录和其他特性才会安装在上下文中。

SpringBoot提供了一个@SpringBootTest注释,当您需要SpringBoot特性时,它可以作为标准Spring -test @ContextConfiguration注释的替代。注释的工作原理是通过SpringApplication创建测试中使用的ApplicationContext。除了@SpringBootTest之外,还提供了许多其他注释,用于测试应用程序的更具体的片段。

如果您使用的是JUnit 4,不要忘记在测试中添加@RunWith(sprinrunner .class),否则注释将被忽略。如果你使用的是JUnit 5,就不需要添加@ extendwith (SpringExtension.class)作为@ springboottest和其他@…Test注释已经用它进行了注释

默认情况下,@SpringBootTest不会启动服务器。你可以使用@SpringBootTest的webEnvironment属性来进一步优化测试的运行方式:

MOCK(默认):加载web ApplicationContext并提供模拟web环境。使用此注释时,嵌入式服务器不会启动。如果在你的类路径上没有一个web环境可用,这种模式就会透明地退回到创建一个常规的非web ApplicationContext。它可以与@AutoConfigureMockMvc或@AutoConfigureWebTestClient一起使用,用于基于模拟的web应用程序测试。

RANDOM_PORT:加载WebServerApplicationContext并提供一个真实的web环境。嵌入式服务器启动并在随机端口上侦听。

DEFINED_PORT:加载WebServerApplicationContext并提供一个真实的web环境。嵌入式服务器在一个定义的端口(来自application.properties)上启动并侦听,或者在默认端口8080上侦听。

NONE:通过使用SpringApplication加载ApplicationContext,但不提供任何web环境(mock或其他)。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class DemoApplicationTests {

或者:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTests {

如果您的测试是@Transactional的,默认情况下,它会在每个测试方法结束时回滚事务。然而,使用RANDOM_PORT或DEFINED_PORT的这种安排隐式地提供了一个真正的servlet环境,HTTP客户端和服务器在单独的线程中运行,因此在单独的事务中运行。在这种情况下,服务器上发起的任何事务都不会回滚。

@SpringBootTest with webEnvironment = webEnvironment。如果应用程序为管理服务器使用不同的端口,RANDOM_PORT还将在一个单独的随机端口上启动管理服务器。

8.3.1. Detecting Web Application Type

如果Spring MVC可用,则配置一个常规的基于MVC的应用程序上下文。如果你只有Spring WebFlux,我们会检测并配置一个基于WebFlux的应用上下文。
如果两者都存在,则优先考虑Spring MVC。如果你想在这种情况下测试一个响应式web应用程序,你必须设置spring.main.web-application-type属性:

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {

    // ...

}
8.3.2. Detecting Test Configuration

如果您熟悉Spring测试框架,您可能习惯于使用@ContextConfiguration(classes=…)来指定加载哪个Spring @Configuration。或者,您可能经常在测试中使用嵌套的@Configuration类。

在测试Spring Boot应用程序时,通常不需要这样做。Spring Boot的@*Test注释在您没有显式定义主配置时自动搜索主配置。

搜索算法从包含测试的包开始查找,直到找到带有@SpringBootApplication或@SpringBootConfiguration注释的类。只要以一种合理的方式组织代码,通常就能找到主配置。

如果您使用测试注释来测试应用程序中更具体的部分,那么您应该避免在主方法的应用程序类中添加特定于特定区域的配置设置。
@SpringBootApplication的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。如果你在@ springbootapplication注释的类上使用显式的@ComponentScan指令,请注意这些过滤器将被禁用。如果您正在使用切片,则应该重新定义它们。

如果你想定制主配置,你可以使用嵌套的@TestConfiguration类。与嵌套的@Configuration类不同,嵌套的@TestConfiguration类是用来代替应用程序的主要配置的,而嵌套的@TestConfiguration类是用来代替应用程序的主要配置的。

Spring的测试框架在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论如何发现它),加载上下文的潜在耗时过程只会发生一次。

8.3.3. Excluding Test Configuration

如果您的应用程序使用组件扫描(例如,如果您使用@SpringBootApplication或@ComponentScan),您可能会发现仅为特定测试创建的顶级配置类意外地在各处被拾取。
正如我们前面看到的,@TestConfiguration可以在测试的内部类上使用,以定制主配置。当放置在顶级类上时,@TestConfiguration表示src/test/java中的类不应该被扫描。然后你可以在需要的地方显式导入这个类,如下面的例子所示:

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {

    @Test
    void exampleTest() {
        // ...
    }

}

如果你直接使用@ComponentScan(也就是说,不是通过@SpringBootApplication),你需要注册TypeExcludeFilter。

8.3.4. Using Application Arguments

如果应用程序需要参数,可以让@SpringBootTest使用args属性注入参数。

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {

    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }

}
8.3.5. Testing With a Mock Environment

默认情况下,@SpringBootTest不会启动服务器,而是为测试web端点设置一个模拟环境。
使用Spring MVC,我们可以使用MockMvc或WebTestClient查询我们的web端点,如下面的例子所示:

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

    @Test
    void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }

    // If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
    @Test
    void testWithWebTestClient(@Autowired WebTestClient webClient) {
        webClient
                .get().uri("/")
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("Hello World");
    }

}

@AutoConfigureMockMvc它可以自动配置MockMvc并将其注入到测试类中。在使用Spring Boot进行单元测试时,我们可以使用它来模拟HTTP请求和响应,以便测试我们的控制器是否按预期工作。

如果你只想关注web层,而不想启动一个完整的ApplicationContext,可以考虑使用@WebMvcTest。

使用Spring WebFlux端点,你可以使用WebTestClient,如下例所示:

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}

在模拟环境中进行测试通常比在完整的servlet容器中运行要快。然而,由于mock发生在Spring MVC层,依赖于低级servlet容器行为的代码不能直接用MockMvc进行测试。
例如,Spring Boot的错误处理基于servlet容器提供的“错误页”支持。这意味着,虽然您可以按照预期测试MVC层抛出和处理异常,但您不能直接测试是否呈现了特定的自定义错误页面。如果您需要测试这些较低级别的关注点,您可以启动一个完全运行的服务器,如下一节所述。

8.3.6. Testing With a Running Server

如果您需要启动一个完整运行的服务器,我们建议您使用随机端口。如果您使用@SpringBootTest(webEnvironment= webEnvironment . random_port),那么每次测试运行时都会随机选择一个可用的端口。

可以使用@LocalServerPort注释将实际使用的端口注入到测试中。为方便起见,需要对已启动服务器进行REST调用的测试可以另外@Autowire一个WebTestClient,它解析到正在运行的服务器的相对链接,并附带一个专用的API来验证响应,如下例所示:

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}

WebTestClient既可以用于活动服务器,也可以用于模拟环境。

这个设置需要类路径上的spring-webflux。如果你不能或不打算添加webflux, Spring Boot也提供了一个TestRestTemplate工具:

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }

}

记得点关注

目录
相关文章
|
23天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
15天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
19天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2570 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
17天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
1天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
152 2
|
19天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1566 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
2天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
921 14
|
16天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
689 9
|
15天前
|
存储 监控 调度
云迁移中心CMH:助力企业高效上云实践全解析
随着云计算的发展,企业上云已成为创新发展的关键。然而,企业上云面临诸多挑战,如复杂的应用依赖梳理、成本效益分析等。阿里云推出的云迁移中心(CMH)旨在解决这些问题,提供自动化的系统调研、规划、迁移和割接等功能,简化上云过程。CMH通过评估、准备、迁移和割接四个阶段,帮助企业高效完成数字化转型。未来,CMH将继续提升智能化水平,支持更多行业和复杂环境,助力企业轻松上云。