Java 如何在春季运行@PostConstruct non-blocking?

Java 如何在春季运行@PostConstruct non-blocking?,java,spring,spring-boot,spring-async,Java,Spring,Spring Boot,Spring Async,如果我用上面的代码启动spring应用程序,它将阻止应用程序启动 我想要的是在启动后直接执行一个方法,但是异步执行。也就是说,它不应该延迟启动,也不应该阻止应用程序在出现故障时运行 如何使初始化异步?您可以从方法中删除@PostConstruct,并让该方法成为普通方法。然后,当ApplicationContext已经加载并且应用程序已经启动时,您可以手动调用它 @PostConstruct public void performStateChecks() { throw new Runt

如果我用上面的代码启动
spring
应用程序,它将阻止应用程序启动

我想要的是在启动后直接执行一个方法,但是异步执行。也就是说,它不应该延迟启动,也不应该阻止应用程序在出现故障时运行


如何使初始化异步?

您可以从方法中删除@PostConstruct,并让该方法成为普通方法。然后,当ApplicationContext已经加载并且应用程序已经启动时,您可以手动调用它

@PostConstruct
public void performStateChecks() {
   throw new RuntimeException("test");
}
@springboot应用程序
公共类ServiceLauncher{
公共静态void main(字符串[]args){
ConfigurableApplicationContext上下文=新的SpringApplication(ServiceLauncher.class).run(args);
试一试{

context.getBean(YourBean.class).performStateChecks();//您可以从方法中删除@PostConstruct,并让该方法成为普通方法。然后,您可以在ApplicationContext已加载且应用程序已启动时手动调用它

@PostConstruct
public void performStateChecks() {
   throw new RuntimeException("test");
}
@springboot应用程序
公共类ServiceLauncher{
公共静态void main(字符串[]args){
ConfigurableApplicationContext上下文=新的SpringApplication(ServiceLauncher.class).run(args);
试一试{

context.getBean(YourBean.class).performStateChecks();//有多种方法可以做到这一点

首先,简单的单线程解决方案是创建并启动一个新线程

 @SpringBootApplication
 public class ServiceLauncher {

 public static void main(String[] args) {

   ConfigurableApplicationContext context = new SpringApplication(ServiceLauncher.class).run(args);

    try {
        context.getBean(YourBean.class).performStateChecks(); // <-- this will run only after context initialization finishes
        } catch (Exception e) {
        //catch any exception here so it does not go down
        }
    }
  }
}
抛出的异常只会中断单独的线程,不会阻止或阻止应用程序启动。如果您对任务的结果或结果不感兴趣,这将非常有用。请注意,不建议这样做,因为它会在spring托管上下文之外启动单独的线程

其次是使用executor服务并向其提交任务。Spring提供了一个默认的
ThreadPoolTaskExecutor
,可用于提交任务。这将允许您访问任务的未来对象,并在以后对其进行处理

@PostConstruct
public void performStateChecks() {
  new Thread(() -> { throw new RuntimeException("test"); }).start();
}
然后像这样使用它

@EnableAsync
@Component
public class AsyncService {

    @Async
    public void doActualTest() {
      throw new RuntimeException("test");
    }
}

有多种方法可以做到这一点

首先,简单的单线程解决方案是创建并启动一个新线程

 @SpringBootApplication
 public class ServiceLauncher {

 public static void main(String[] args) {

   ConfigurableApplicationContext context = new SpringApplication(ServiceLauncher.class).run(args);

    try {
        context.getBean(YourBean.class).performStateChecks(); // <-- this will run only after context initialization finishes
        } catch (Exception e) {
        //catch any exception here so it does not go down
        }
    }
  }
}
抛出的异常只会中断单独的线程,不会阻止或阻止应用程序启动。如果您对任务的结果或结果不感兴趣,这将非常有用。请注意,不建议这样做,因为它会在spring托管上下文之外启动单独的线程

其次是使用executor服务并向其提交任务。Spring提供了一个默认的
ThreadPoolTaskExecutor
,可用于提交任务。这将允许您访问任务的未来对象,并在以后对其进行处理

@PostConstruct
public void performStateChecks() {
  new Thread(() -> { throw new RuntimeException("test"); }).start();
}
然后像这样使用它

@EnableAsync
@Component
public class AsyncService {

    @Async
    public void doActualTest() {
      throw new RuntimeException("test");
    }
}

我能看到的最简单的方法是使用EventListeners和异步任务执行器

添加此代码段将完成以下工作:

private final AsyncService asyncService; // make sure to inject this via constructor

@PostConstruct 
public void performStateChecks() {
    asyncService.doActualTest();
}

我能看到的最简单的方法是使用EventListeners和异步任务执行器

添加此代码段将完成以下工作:

private final AsyncService asyncService; // make sure to inject this via constructor

@PostConstruct 
public void performStateChecks() {
    asyncService.doActualTest();
}

您可以使用
EventListener
而不是
PostConstruct
,它支持
@Async

    @Component
    public class AsyncStartupRunner {

        @Bean(name = "applicationEventMulticaster")
        public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
            SimpleApplicationEventMulticaster eventMulticaster =
                    new SimpleApplicationEventMulticaster();

            eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
            return eventMulticaster;
        }

        @EventListener(ApplicationReadyEvent.class)
        public void executeAfterStartup() {
            throw new RuntimeException("Oops");
        }
    }
不要忘记通过
@EnableAsync
注释启用异步支持


您还可以使用一些其他事件,请参见
SpringApplicationEvent
类的继承者您可以使用
EventListener
而不是
PostConstruct
,它支持
@Async

    @Component
    public class AsyncStartupRunner {

        @Bean(name = "applicationEventMulticaster")
        public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
            SimpleApplicationEventMulticaster eventMulticaster =
                    new SimpleApplicationEventMulticaster();

            eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
            return eventMulticaster;
        }

        @EventListener(ApplicationReadyEvent.class)
        public void executeAfterStartup() {
            throw new RuntimeException("Oops");
        }
    }
不要忘记通过
@EnableAsync
注释启用异步支持


您还可以使用一些其他事件,请参见
SpringApplicationEvent
类的继承者使用
@Async
?或者只使用一个简单的
TaskExecutor
@Async
@PostConstruct
不是异步执行的。因此,如果这是唯一的机会,我当然可以从PostConstruct中调用另一个异步bean。不要使用<代码>@PostConstruct
。它作为应用程序初始化/启动的一部分执行,错误将停止(正如您所注意到的)。因此,不要使用
@PostConstruct
,也不要在该方法中使用适当的异常处理。但您可能最好使用事件侦听器来初始化某些内容。使用
@Async
?或者只使用一个简单的
任务执行器
@Async
@PostConstruct
不是异步执行的。因此我当然可以调用postconstruct中的另一个异步bean,如果这是唯一的机会的话。不要为此使用
@postconstruct
。它是作为应用程序初始化/启动的一部分执行的,错误将停止(正如您所注意到的)。因此,不要为此使用
@PostConstruct
,也不要在该方法中使用适当的异常处理。但您可能最好使用事件侦听器来初始化某些内容。我不会调用前两个解决方案,因为它们将启动自己的线程。您应该委托给
@Async
方法(作为第三个解决方案)或者注入一个托管线程池来启动线程。您不应该自己启动线程。您不应该启动自己的线程,而且创建一个非共享执行器服务看起来就像一个代码smell@M.Deinum谢谢,我同意你的意见。更新了答案。我不会把前两个解决方案称为“解决方案”,因为这两个解决方案将开始使用n个线程。您应该委托给
@Async
方法(作为第三个解决方案)或者注入一个托管线程池来启动线程。您不应该自己启动线程。您不应该启动自己的线程,而且创建一个非共享执行器服务看起来就像一个代码smell@M.Deinum谢谢,同意你的评论。更新了答案我不太喜欢这个解决方案,因为它直接放在里面主方法我不太喜欢这个解决方案,因为它直接放在主方法中