Spring Boot 应用(官网文档解读)

Spring Boot 启动方式

SpringApplication.run(MyApplication.class, args);

Spring Boot 故障分析器

        在Spring Boot 项目启动发生错误的时候,我们通常可以看到上面的内容,即 APPLICATION FAILED TO START,以及后面的错误描述。这个功能是通过众多的FailureAnalyzer 故障分析器beans 实现来处理的。我们可以在Spring Boot 中的spring.factories 配置文件中看到 Failure Analyzers bean 的具体实现类。

Spring Boot 自定义故障分析器

  • 创建一个自定义错误

package person.wend.springbootlearnexample2.failureanalyzer;

import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;


public class CustomFailureAnalyzer extends AbstractFailureAnalyzer<CustomStartupException> {

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, CustomStartupException cause) {
        // 错误描述
        String description = "自定义启动错误:" + cause.getMessage();
        // 操作建议
        String action = "请检查相关配置或联系系统管理员。";
        return new FailureAnalysis(description, action, cause);
    }
}
  • 创建一个自定义故障分析器类

package person.wend.springbootlearnexample2.failureanalyzer;

import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;


public class CustomFailureAnalyzer extends AbstractFailureAnalyzer<CustomStartupException> {

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, CustomStartupException cause) {
        // 错误描述
        String description = "自定义启动错误:" + cause.getMessage();
        // 操作建议
        String action = "请检查相关配置或联系系统管理员。";
        return new FailureAnalysis(description, action, cause);
    }
}
  • 在META-INF\spring.factories 配置文件中注册该bean

org.springframework.boot.diagnostics.FailureAnalyzer=\
person.wend.springbootlearnexample2.failureanalyzer.CustomFailureAnalyzer
  • 启动报错示例

延迟初始化

        SpringApplication允许应用程序延迟初始化。启用延迟初始化后,Bean 将在需要时创建,而不是在应用程序启动期间创建。因此,启用延迟初始化可以减少应用程序启动所需的时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 Bean 在收到 HTTP 请求之前不会被初始化。

        延迟初始化的缺点是,它会延迟发现应用程序的问题。如果延迟初始化配置错误的 bean,则启动期间将不再发生故障,并且问题仅在初始化 bean 时才会显现出来。还必须注意确保 JVM 具有足够的内存来容纳应用程序的所有 bean,而不仅仅是那些在启动期间初始化的 bean。出于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。

实现延迟初始化的方式

  • 配置文件

spring: main: lazy-initialization: true

  • 启动项目时配置

 public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(SpringBootLearnExample2Application.class);
        springApplication.setLazyInitialization(true);
    }
  • 使用注解@Lazy(true)

自定义横幅

        在Spring Boot 中,我们可以使用banner.txt定义自定义横幅,一般将banner.txt 放在resource文件夹即可。以下是关于自定义横幅的属性列表:

  • spring.banner.location:定义banner.txt 的文件位置

  • spring.banner.charset:定义banner.txt 的编码类型

  • banner.txt可以使用Environment 配置中的任何属性,以及支持一些默认的属性

  • 变量描述

    ${application.version}

    应用程序版本号

    ${application.formatted-version}

    应用程序格式化的版本号

    ${spring-boot.version}

    您正在使用的 Spring Boot 版本。例如3.4.2

    ${spring-boot.formatted-version}

    您正在使用的 Spring Boot 格式化的版本号

    ${Ansi.NAME}(或者${AnsiColor.NAME}${AnsiBackground.NAME}${AnsiStyle.NAME}

    AnsiPropertySource 类是 Spring Boot 框架里用于处理 ANSI(美国国家标准化组织)转义序列相关属性源的类,主要在处理控制台输出样式方面发挥作用。

    ${application.title}

     应用程序标题

编程方式实现横幅

  • 启动类增加Banner配置

springApplication.setBanner(…​)

  •  自定义Banner 类
package person.wend.springbootlearnexample.config;

import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;

import java.io.PrintStream;

public class CustomBanner implements Banner {

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
        StringBuilder banner = new StringBuilder();
        banner.append("==================================================\n");
        banner.append("*                                                *\n");
        banner.append("*           Welcome to My Custom App             *\n");
        banner.append("*                                                *\n");
        banner.append("==================================================\n");
        out.println(banner.toString());
    }
}
  • 实现效果

关闭横幅

springApplication.setBannerMode(Banner.Mode.OFF);


 SpringApplicationBuilder

        我们可以使用 SpringApplicationBuilder 来构建SpringBoot 的启动类。

new SpringApplicationBuilder()
    .sources(Parent.class)
    .child(Application.class)
    .bannerMode(Banner.Mode.OFF)
    .run(args);
  • sources(Parent.class):指定父上下文的主配置类为 Parent.class,Spring Boot 会根据这个类来创建和初始化父应用上下文。

  • child(Application.class):创建一个子应用上下文,并将 Application.class 作为子上下文的主配置类。子上下文会继承父上下文的 Bean 定义,并且可以有自己独立的配置。

  • bannerMode(Banner.Mode.OFF):设置启动时不显示 Spring Boot 的 Banner。

  • run(args):启动应用,同时启动父上下文和子上下文。

Spring Boot 可用性状态概述

        Spring Boot 支持 “存活”(Liveness)和 “就绪”(Readiness)两种可用性状态。当应用部署在如 Kubernetes 这样的平台上时,可通过这些状态向平台提供自身的可用性信息。若使用 Spring Boot 的 “actuator” 支持,这些状态会作为健康端点组暴露出来,也可通过注入 ApplicationAvailability 接口到自定义 Bean 中获取可用性状态。

存活状态(Liveness State)

  • 含义:表明应用的内部状态是否允许其正常工作,或者在当前出现故障时能否自行恢复。若存活状态不佳,意味着应用处于无法恢复的状态,平台基础设施应重启该应用。

  • 判断依据:通常不应基于外部检查(如健康检查)来确定存活状态,因为外部系统(如数据库、Web API、外部缓存)故障可能会导致平台大规模重启和级联故障。Spring Boot 应用的内部状态主要由 Spring ApplicationContext 表示,一旦应用上下文成功刷新,就认为应用处于有效状态,即存活。

就绪状态(Readiness State)

  • 含义:表示应用是否准备好处理流量。若就绪状态不佳,告知平台目前不应将流量路由到该应用。这通常发生在应用启动期间(如 CommandLineRunnerApplicationRunner 组件正在处理时),或者应用认为自身过于繁忙无法处理额外流量时。

  • 判断依据:一旦调用了应用和命令行运行器,就认为应用已就绪。启动期间预期运行的任务应通过 CommandLineRunnerApplicationRunner 组件执行,而不是使用 @PostConstruct 等 Spring 组件生命周期回调。

使用示例

  • 添加 spring-boot-actuator依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 创建一个 Bean 来注入 ApplicationAvailability 接口,并检查存活和就绪状态:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.stereotype.Component;

@Component
public class AvailabilityChecker {

    private final ApplicationAvailability applicationAvailability;

    @Autowired
    public AvailabilityChecker(ApplicationAvailability applicationAvailability) {
        this.applicationAvailability = applicationAvailability;
    }

    public void checkAvailability() {
        LivenessState livenessState = applicationAvailability.getLivenessState();
        ReadinessState readinessState = applicationAvailability.getReadinessState();

        System.out.println("Liveness State: " + livenessState);
        System.out.println("Readiness State: " + readinessState);
    }
}
  • 在主类中调用检查方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication implements CommandLineRunner {

    @Autowired
    private AvailabilityChecker availabilityChecker;

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        availabilityChecker.checkAvailability();
    }
}

管理应用程序可用性状态

    • 应用程序组件可以随时通过注入接口ApplicationAvailability并调用其方法来检索当前可用性状态。更常见的是,应用程序希望监听状态更新或更新应用程序的状态。

    • Spring Boot通过 Actuator Health Endpoints 为 Kubernetes 提供了“活跃度”和“就绪度”的 HTTP 探测。

    • 例如,我们可以将应用程序的“就绪”状态导出到文件,以便 Kubernetes“exec Probe”可以查看此文件:

    @Component
    public class MyReadinessStateExporter {
    
    	@EventListener
    	public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
    		switch (event.getState()) {
    			case ACCEPTING_TRAFFIC -> {
    				// create file /tmp/healthy
    			}
    			case REFUSING_TRAFFIC -> {
    				// remove file /tmp/healthy
    			}
    		}
    	}
    
    }
    • 当应用程序崩溃且无法恢复时,我们还可以更新应用程序的状态:

    @Component
    public class MyLocalCacheVerifier {
    
    	private final ApplicationEventPublisher eventPublisher;
    
    	public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
    		this.eventPublisher = eventPublisher;
    	}
    
    	public void checkLocalCache() {
    		try {
    			// ...
    		}
    		catch (CacheCompletelyBrokenException ex) {
    			AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
    		}
    	}
    
    }

    应用程序事件监听器

    • 在Spring Boot项目中,我们一般可以使用ContextRefreshedEvent来监听一个事件。ContextRefreshedEvent 是 Spring 框架中的一个事件,当 ApplicationContext 被初始化或者刷新时会发布该事件。

    • 我们可以使用SpringApplication.addListeners(…​)或SpringApplicationBuilder.listeners(…​)注册一个监听器。

    • 我们也可以通过在 META-INF/spring.factories添加监听器的方式注册一个监听器的Bean。

    示例代码

    package person.wend.springbootlearnexample2.event;
    
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            // 获取事件源,即 ApplicationContext
            var applicationContext = event.getApplicationContext();
    
            // 可以在这里执行一些初始化操作,比如获取 Bean 等
            System.out.println("ContextRefreshedEvent 事件被触发,ApplicationContext 已刷新。");
            System.out.println("当前 ApplicationContext 中的 Bean 数量: " + applicationContext.getBeanDefinitionCount());
        }
    }
    

    Spring Boot 事件触发顺序

            Spring Boot 应用在运行过程中,各类事件按照以下顺序依次执行:

    1. ApplicationStartingEvent:应用启动时触发,此时仅完成监听器和初始化器的注册,其他处理工作尚未开展。

    2. ApplicationEnvironmentPreparedEvent:当明确了应用上下文要使用的环境配置,但上下文还未创建时触发该事件。

    3. ApplicationContextInitializedEvent:应用上下文准备完毕,且上下文初始化器完成调用,但 Bean 定义尚未加载时触发此事件。

    4. ApplicationPreparedEvent:Bean 定义加载完成后,上下文刷新操作启动之前触发该事件。

    5. ApplicationStartedEvent:上下文刷新完成,但应用程序和命令行运行器还未调用时触发此事件。

    6. AvailabilityChangeEvent(LivenessState.CORRECT):在 ApplicationStartedEvent 之后立即触发,表明应用处于存活状态。

    7. ApplicationReadyEvent:应用程序和命令行运行器调用完成后触发此事件。

    8. AvailabilityChangeEvent(ReadinessState.ACCEPTING_TRAFFIC):在 ApplicationReadyEvent 之后立即触发,意味着应用已准备好处理请求。

    9. ApplicationFailedEvent:若应用在启动过程中出现异常,则触发该事件。

            上面的列表仅包括绑定到SpringApplication的SpringApplicationEvents。除此之外,以下事件也在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发布:

    • WebServer就绪后发送WebServerInitializedEvent。ServletWebServerInitializedEvent和ReactiveWebServerIninitializedEvent分别是servlet和反应式变体。

    • 刷新ApplicationContext时发送ContextRefreshedEvent。

    Web环境

            SpringApplication试图代表您创建正确类型的ApplicationContext。用于确定WebApplicationType的算法如下:

    • 如果存在Spring MVC,则使用AnnotationConfigServletWebServerApplicationContext

    • 如果Spring MVC不存在,并且Spring WebFlux存在,则使用AnnotationConfigReactiveWebServerApplicationContext

    • 否则,将使用AnnotationConfigApplicationContext

            这意味着,如果您在同一应用程序中使用SpringMVC和SpringWebFlux中的新WebClient,则默认情况下将使用Spring MVC。

    • 您可以通过调用setWebApplicationType(WebApplicationType)轻松地覆盖它。

    • 还可以完全控制通过调用setApplicationContextFactory(…)修改使用的ApplicationContext类型。

    ApplicationArguments 访问应用程序命令参数

       ApplicationArguments 是 Spring Boot 中的一个接口,用于在应用启动时访问传递给应用的命令行参数。以下从功能、使用场景、使用方式等方面详细介绍其作用。

    主要功能

    • 解析命令行参数ApplicationArguments 接口可以将传递给 Spring Boot 应用的命令行参数解析为选项参数(以 -- 开头的参数)和非选项参数,方便开发者对不同类型的参数进行处理。

    • 提供参数访问方法:该接口提供了一系列方法,用于获取选项参数的名称、值,以及非选项参数的列表,开发者可以根据需要灵活使用这些方法。

    使用场景

    • 配置应用行为:通过命令行参数可以动态配置应用的行为,例如指定应用的运行模式(开发模式、生产模式)、数据库连接信息等。

    • 传递自定义参数:在应用启动时传递一些自定义的参数,用于执行特定的任务或进行初始化操作。

    • 测试和调试:在测试和调试过程中,可以通过命令行参数传递不同的配置,方便对应用进行测试和排查问题。

    示例代码

    import org.springframework.boot.ApplicationArguments;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyCommandLineArgsHandler {
    
        public MyCommandLineArgsHandler(ApplicationArguments args) {
            // 获取所有选项参数的名称
            System.out.println("选项参数名称: " + args.getOptionNames());
    
            // 检查是否包含某个选项参数
            if (args.containsOption("debug")) {
                System.out.println("应用以调试模式启动。");
            }
    
            // 获取某个选项参数的值
            if (args.containsOption("port")) {
                System.out.println("指定的端口号: " + args.getOptionValues("port"));
            }
    
            // 获取非选项参数的列表
            System.out.println("非选项参数: " + args.getNonOptionArgs());
        }
    }

    CommandLineRunner 

       CommandLineRunner 接口位于 org.springframework.boot 包下,是一个函数式接口,只包含一个抽象方法 run,定义如下:

    package org.springframework.boot;
    
    @FunctionalInterface
    public interface CommandLineRunner {
        void run(String... args) throws Exception;
    }

       run 方法接收一个 String 数组参数 args,这个参数包含了应用程序启动时传递的命令行参数。该方法会在 Spring 应用上下文完全初始化之后被调用。

    • 使用示例:
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyCommandLineRunner implements CommandLineRunner {
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println("应用启动后执行的代码逻辑...");
            for (String arg : args) {
                System.out.println("命令行参数: " + arg);
            }
        }
    }

    ApplicationRunner

       ApplicationRunner 接口同样位于 org.springframework.boot 包下,也是一个函数式接口,其抽象方法 run 接收一个 ApplicationArguments 类型的参数,定义如下:

    package org.springframework.boot;
    
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    
    @FunctionalInterface
    public interface ApplicationRunner extends Ordered {
        void run(ApplicationArguments args) throws Exception;
    }

       ApplicationArguments 是一个封装了命令行参数的对象,它提供了更方便的方法来处理选项参数和非选项参数。

    • 使用示例
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyApplicationRunner implements ApplicationRunner {
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println("应用启动后执行的代码逻辑...");
            System.out.println("选项参数: " + args.getOptionNames());
            System.out.println("非选项参数: " + args.getNonOptionArgs());
        }
    }

    使用场景

    • 数据初始化:在应用启动后,你可能需要初始化一些数据到数据库或者缓存中,比如加载配置文件、初始化字典数据等。

    • 资源预热:提前加载一些常用的资源,以减少后续请求的响应时间,例如预热缓存、初始化连接池等。

    • 启动脚本:执行一些启动时的脚本任务,如检查系统环境、创建必要的文件目录等。

    两者的区别

    • 参数类型CommandLineRunnerrun 方法接收的是一个 String 数组,需要开发者自己解析命令行参数;而 ApplicationRunnerrun 方法接收的是一个 ApplicationArguments 对象,它对命令行参数进行了封装,提供了更方便的方法来处理选项参数和非选项参数。

    • 使用场景:如果只是简单地处理命令行参数,CommandLineRunner 就足够了;如果需要更复杂的参数处理,如区分选项参数和非选项参数,ApplicationRunner 会更合适。

    @Order 控制顺序执行

            如果程序中存在多个CommandLineRunnner 或者 ApplicationRunner,我们可以使用@Order注解来控制bean 类的最终执行顺序。

    应用程序退出

            在 Spring Boot 中,SpringApplication.exit() 方法用于优雅地关闭 Spring Boot 应用程序。该方法会执行一系列的关闭操作,包括:

    • 触发 DisposableBean 接口的 destroy() 方法,让实现该接口的 Bean 有机会进行资源清理。

    • 调用 SmartLifecycle 接口的 stop() 方法,停止那些实现了该接口的生命周期组件。

    • 关闭 ApplicationContext,释放相关资源。

      SpringApplication.exit() 方法会返回一个退出码(int 类型),这个退出码可以用于指示应用程序的退出状态,通常 0 表示正常退出,非 0 表示异常退出。

    使用场景

    • 程序正常结束:当应用程序完成了它的任务,需要正常关闭时,可以调用该方法。例如,一个定时任务应用在完成所有任务后,主动退出。

    • 异常处理:在应用程序遇到严重错误,无法继续运行时,可以调用该方法并返回一个非 0 的退出码,以指示应用程序异常退出。

    • 自动化测试:在自动化测试中,测试完成后可以调用该方法关闭应用程序,确保资源被正确释放。

    示例代码

    正常退出
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class NormalExitExample implements CommandLineRunner {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(NormalExitExample.class, args);
        }
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println("应用程序开始执行任务...");
            // 模拟执行一些任务
            Thread.sleep(2000);
            System.out.println("任务执行完成,准备退出应用程序。");
    
            // 获取当前的应用上下文
            ConfigurableApplicationContext context = SpringApplication.run(NormalExitExample.class, args);
            // 调用 exit 方法正常退出应用程序,返回退出码 0
            int exitCode = SpringApplication.exit(context, () -> 0);
            System.exit(exitCode);
        }
    }
    异常退出
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class ExceptionExitExample implements CommandLineRunner {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(ExceptionExitExample.class, args);
        }
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println("应用程序开始执行任务...");
            try {
                // 模拟抛出异常
                throw new RuntimeException("发生了严重错误!");
            } catch (RuntimeException e) {
                System.err.println("捕获到异常: " + e.getMessage());
                System.err.println("准备异常退出应用程序。");
    
                // 获取当前的应用上下文
                ConfigurableApplicationContext context = SpringApplication.run(ExceptionExitExample.class, args);
                // 调用 exit 方法异常退出应用程序,返回退出码 1
                int exitCode = SpringApplication.exit(context, () -> 1);
                System.exit(exitCode);
            }
        }
    }

    ExitCodeGenerator 定义退出代码

     @Bean
        public ExitCodeGenerator exitCodeGenerator() {
            return () -> 42;
        }
    
        public static void main(String[] args) {
            System.exit(SpringApplication.exit(SpringApplication.run(SpringBootLearnExample2Application.class, args)));
        }

    管理功能

            我们可以通过指定 spring.application.admin.enabled 属性可开启应用的管理相关功能,开启后会在平台的 MBeanServer 上暴露 SpringApplicationAdminMXBean,可用于远程管理 Spring Boot 应用,对服务包装器实现也有帮助。

    应用程序启动追踪

            在应用启动期间,SpringApplication 和 ApplicationContext 会执行众多与应用生命周期、Bean 生命周期及应用事件处理相关的任务。借助 ApplicationStartup,Spring 框架允许使用 StartupStep 对象跟踪应用的启动序列,这些数据可用于性能分析或帮助更好地理解应用启动过程。

            在设置 SpringApplication 实例时可选择 ApplicationStartup 的实现类。例如,可使用 BufferingApplicationStartup,代码示例展示了如何配置该实现类。

    @SpringBootApplication
    public class MyApplication {
    
    	public static void main(String[] args) {
    		SpringApplication application = new SpringApplication(MyApplication.class);
    		application.setApplicationStartup(new BufferingApplicationStartup(2048));
    		application.run(args);
    	}
    
    }
    • 具体实现类介绍

      • FlightRecorderApplicationStartup:由 Spring 框架提供,它将 Spring 特定的启动事件添加到 Java Flight Recorder 会话中,用于分析应用程序并关联 Spring 上下文生命周期与 JVM 事件。配置后,可通过启用 Java Flight Recorder 来记录数据。

      • BufferingApplicationStartup:Spring Boot 自带的实现类,用于缓冲启动步骤并将其输出到外部指标系统。应用程序可在任何组件中获取该类型的 Bean。

    • 额外配置:Spring Boot 还可配置暴露一个启动端点,以 JSON 文档形式提供启动跟踪信息。

    虚拟线程

            在 Java 21 及更高版本中,Spring Boot 支持使用虚拟线程。虚拟线程是 Java 平台引入的轻量级线程,它旨在简化并发编程,提高应用程序的吞吐量和资源利用率。与传统的操作系统线程相比,虚拟线程的创建和销毁成本更低,能够在相同的硬件资源下处理更多的并发任务。

    • 启用虚拟线程:要在 Spring Boot 应用中启用虚拟线程,需要设置 spring.threads.virtual.enabled 属性为 true

    • 线程池配置的变化:当启用虚拟线程后,配置线程池的属性将不再生效。这是因为虚拟线程是在 JVM 全局的平台线程池上进行调度,而不是在专用的线程池上。

    • 处理守护线程问题:虚拟线程是守护线程,这意味着如果 JVM 中的所有线程都是守护线程,JVM 将退出。例如,当使用 @Scheduled 注解的 Bean 来保持应用程序运行时,可能会出现问题。因为调度器线程是虚拟线程,也就是守护线程,无法使 JVM 保持运行。为了解决这个问题,可以设置 spring.main.keep-alive 属性为 true,确保即使所有线程都是虚拟线程,JVM 也能保持运行。

    示例代码

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @SpringBootApplication
    @EnableScheduling
    public class VirtualThreadExampleApplication {
    
        public static void main(String[] args) {
            SpringApplication application = new SpringApplication(VirtualThreadExampleApplication.class);
            // 模拟在代码中设置属性
            application.setDefaultProperties(java.util.Map.of(
                    "spring.threads.virtual.enabled", "true",
                    "spring.main.keep-alive", "true"
            ));
            application.run(args);
        }
    }
    
    @Component
    class ScheduledTask {
    
        @Scheduled(fixedRate = 5000)
        public void performTask() {
            System.out.println("Scheduled task is running...");
        }
    }

            在上述示例中,我们启用了虚拟线程和定时任务,并设置了 spring.main.keep-alive 属性为 true,以确保 JVM 不会因为虚拟线程是守护线程而提前退出。

    参考文献

    SpringApplication :: Spring Boot

      本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977252.html

      如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

      相关文章

      从单片机的启动说起一个单片机到点灯发生了什么下——使用GPIO点一个灯

      目录 前言 HAL库对GPIO的抽象 核心分析&#xff1a;HAL_GPIO_Init 前言 我们终于到达了熟悉的地方&#xff0c;对GPIO的初始化。经过漫长的铺垫&#xff0c;我们终于历经千辛万苦&#xff0c;来到了这里。关于GPIO的八种模式等更加详细的细节&#xff0c;由于只是点个灯&am…

      【JavaWeb学习Day19】

      Tlias智能学习系统&#xff08;员工管理&#xff09; 删除员工&#xff1a; 需求分析&#xff1a; 其实&#xff0c;删除单条数据也是一种特殊的批量删除&#xff0c;所以&#xff0c;删除员工的功能&#xff0c;我们只需要开发一个接口就行了。 三层架构&#xff1a; Cont…

      Windows 上源码安装 FastGPT

      FastGPT 是一个强大的 AI RAG 平台&#xff0c;值得我们去学习了解。与常见的 Python 体系不同&#xff0c;Fast GPT 采用 Node.js/Next.js 平台&#xff08;对于广大 JS 开发者或前端开发者比较亲切友好&#xff09;&#xff0c;安装或部署比较简单。虽然一般情况下推荐简单的…

      FreiHAND (handposeX-json 格式)数据集-release >> DataBall

      FreiHAND &#xff08;handposeX-json 格式&#xff09;数据集-release 注意&#xff1a; 1)为了方便使用&#xff0c;按照 handposeX json 自定义格式存储 2)使用常见依赖库进行调用,降低数据集使用难度。 3)部分数据集获取请加入&#xff1a;DataBall-X数据球(free) 4)完…

      Sinusoidal、RoPE和可学习嵌入的详细介绍及它们增强位置感知能力的示例

      前文,我们已经构建了一个小型的字符级语言模型,是在transformer架构基础上实现的最基本的模型,我们肯定是希望对该模型进行改进和完善的。所以我们的另外一篇文章也从数据预处理、模型架构、训练策略、评估方法、代码结构、错误处理、性能优化等多个方面提出具体的改进点,但…

      【JavaEE进阶】Spring Boot配置文件

      欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗 如有错误&#xff0c;欢迎指出~ 目录 SpringBoot配置⽂件 举例: 通过配置文件修改端口号 配置⽂件的格式 properties基本语法 读取配置⽂件 properties配置文件的缺点 yml配置⽂件 yml基本语法 yml和proper…

      BUUCTF--[极客大挑战 2019]RCE ME

      目录 URL编码取反绕过 异或绕过 异或的代码 flag 借助蚁剑中的插件进行绕过 利用动态链接库 编写恶意c语言代码 进行编译 然后再写一个php文件 将这两个文件上传到/var/tmp下 运行payload 直接看代码 <?php error_reporting(0); if(isset($_GET[code])){$code$_G…

      Tag标签的使用

      一个非常适合运用在vue项目中的组件&#xff1a;Tag标签。 目录 一、准备工作 1、安装element-plus库 2、配置element-plus库 二、Tag标签入门 1、打开element官网&#xff0c;搜索tag标签 2、体验Tag标签的基础用法 三、Tag标签进阶训练1 1、定义一个数组&#xff0c;…

      学习threejs,使用createMultiMaterialObject创建多材质对象

      &#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.SceneUtils 场景操控…

      [C++]使用纯opencv部署yolov12目标检测onnx模型

      yolov12官方框架&#xff1a;sunsmarterjie/yolov12 【算法介绍】 在C中使用纯OpenCV部署YOLOv12进行目标检测是一项具有挑战性的任务&#xff0c;因为YOLOv12通常是用PyTorch等深度学习框架实现的&#xff0c;而OpenCV本身并不直接支持加载和运行PyTorch模型。然而&#xff…

      MQ(Message Queue)

      目录 MQ(Message Queue)基本概念 为什么要使用消息队列&#xff1f; 使用消息队列有什么缺点&#xff1f; 如何保证消息不丢失?(如何保证消息的可靠性传输?/如何处理消息丢失的问题?) 通用的MQ场景&#xff1a; RabbitMQ如何保证消息不丢失&#xff1f; 生产者丢数据…

      Linux 第三次脚本作业

      源码编译安装httpd 2.4&#xff0c;提供系统服务管理脚本并测试&#xff08;建议两种方法实现&#xff09; 一、第一种方法 1、把 httpd-2.4.63.tar.gz 这个安装包上传到你的试验机上 2、 安装编译工具 (俺之前已经装好了&#xff09; 3、解压httpd包 4、解压后的httpd包的文…

      项目实战--网页五子棋(匹配模块)(4)

      上期我们完成了游戏大厅的前端部分内容&#xff0c;今天我们实现后端部分内容 1. 维护在线用户 在用户登录成功后&#xff0c;我们可以维护好用户的websocket会话&#xff0c;把用户表示为在线状态&#xff0c;方便获取到用户的websocket会话 package org.ting.j20250110_g…

      浏览器下载vue.js.devtools,谷歌浏览器和edg浏览器

      1、谷歌浏览器下载&#xff1a; 情况一&#xff1a;如果谷歌应用商店可以打开&#xff0c;那么就直接到谷歌应用商店下载&#xff0c;直接搜索vue.js.devtools添加扩展即可。 情况二&#xff1a;谷歌浏览器的谷歌应用商城打不开&#xff0c;那么就百度搜索极简插件找到vue.js.…

      基于TensorFlow.js与Web Worker的智能证件照生成方案

      功能简介 本文基于TensorFlow.js与Web Worker实现了常用的“证件照”功能&#xff0c;可以对照片实现抠图并替换背景。值得一提的是&#xff0c;正常抠图的操作应该由后端进行&#xff0c;这里只是主要演示该功能实现步骤&#xff0c;并不建议该功能由前端全权处理。 限于个人技…

      3D模型在线转换工具:轻松实现3DM转OBJ

      3D模型在线转换是一款功能强大的在线工具&#xff0c;支持多种3D模型格式的在线预览和互转。无论是工业设计、建筑设计&#xff0c;还是数字艺术领域&#xff0c;这款工具都能满足您的需求。 3DM与OBJ格式简介 3DM格式&#xff1a;3DM是一种广泛应用于三维建模的文件格式&…

      GEO数据结构

      目录 1. GEOADD 2. GEODIST 3. GEOHASH 3. GEOHASH 4. GEOPOS 6. GEOSEARCH 7. GEOSEARCHSTORE 应用场景 代码的逻辑分解&#xff1a; 比较难懂的部分&#xff1a; Redis GEO 查询与分页 results 的结构&#xff1a; 分页处理与截取数据 附加距离信息 1. GEOADD…

      Java基础常见的面试题(易错!!)

      面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…

      基于Python/Flask/机器学习链家网新房数据可视化及预测系统+万字文档+答辩PPT+指导搭建视频

      技术栈&#xff1a; 编程语言&#xff1a;python 涉及技术&#xff1a;requests爬虫、mysql数据库、flask框架、scikit-learn机器学习预测算法、多元线性回归、Echarts可视化。 ①.需求分析&#xff1a; 1.数据爬取&#xff1a;自动化获取链家网新房数据。 2.数据存储&…

      【DeepSeek-R1背后的技术】系列十一:RAG原理介绍和本地部署(DeepSeekR1+RAGFlow构建个人知识库)

      【DeepSeek-R1背后的技术】系列博文&#xff1a; 第1篇&#xff1a;混合专家模型&#xff08;MoE&#xff09; 第2篇&#xff1a;大模型知识蒸馏&#xff08;Knowledge Distillation&#xff09; 第3篇&#xff1a;强化学习&#xff08;Reinforcement Learning, RL&#xff09;…