如何中止 Spring-Boot 启动?

How can I abort Spring-Boot startup?

我正在编写一个 Spring-Boot 应用程序来监视目录并处理添加到其中的文件。我在配置 class:

中用 WatchService 注册目录
@Configuration
public class WatchServiceConfig {

    private static final Logger logger = LogManager.getLogger(WatchServiceConfig.class);

    @Value("${dirPath}")
    private String dirPath;

    @Bean
    public WatchService register() {
        WatchService watchService = null;

        try {
            watchService = FileSystems.getDefault().newWatchService();
            Paths.get(dirPath).register(watchService, ENTRY_CREATE);
            logger.info("Started watching \"{}\" directory ", dlsDirPath);
        } catch (IOException e) {
            logger.error("Failed to create WatchService for directory \"" + dirPath + "\"", e);
        }

        return watchService;
    }
}

如果注册目录失败,我想中止 Spring 正常启动。有人知道我该怎么做吗?

https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-application-exit

Each SpringApplication will register a shutdown hook with the JVM to ensure that the ApplicationContext is closed gracefully on exit. All the standard Spring lifecycle callbacks (such as the DisposableBean interface, or the @PreDestroy annotation) can be used.

In addition, beans may implement the org.springframework.boot.ExitCodeGenerator interface if they wish to return a specific exit code when the application ends.

您应该实现释放资源/文件的@PreDestroy 方法。然后在启动期间,当您检测到一些错误时,您可以抛出一些 RuntimeException - 它开始关闭应用程序上下文。

获取应用上下文,例如:

@Autowired
private ConfigurableApplicationContext ctx;

找不到目录就调用close方法:

ctx.close();

这会正常关闭应用程序上下文,从而关闭 Spring 引导应用程序本身。

更新:

基于问题中提供的代码的更详细示例。

主要Class

@SpringBootApplication
public class GracefulShutdownApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(GracefulShutdownApplication.class, args);
        try{
            ctx.getBean("watchService");
        }catch(NoSuchBeanDefinitionException e){
            System.out.println("No folder to watch...Shutting Down");
            ctx.close();
        }
    }

}

WatchService 配置

@Configuration
public class WatchServiceConfig {

    @Value("${dirPath}")
    private String dirPath;

    @Conditional(FolderCondition.class)
    @Bean
    public WatchService watchService() throws IOException {
        WatchService watchService = null;
        watchService = FileSystems.getDefault().newWatchService();
        Paths.get(dirPath).register(watchService, ENTRY_CREATE);
        System.out.println("Started watching directory");
        return watchService;
    }

文件夹条件

public class FolderCondition implements Condition{

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String folderPath = conditionContext.getEnvironment().getProperty("dirPath");
        File folder = new File(folderPath);
        return folder.exists();
    }
}

根据目录是否存在创建 WatchService Bean @Conditional。然后在您的 Main Class 中,检查 WatchService Bean 是否存在,如果不存在,则通过调用 close().

关闭 Application Context

接受的答案是正确的,但不必要地复杂。不需要一个Condition,然后检查bean是否存在,然后关闭ApplicationContext。在 WatchService 创建期间简单地检查目录是否存在,并抛出异常将由于创建 bean 失败而中止应用程序启动。

如果您对当前 IOException 中的消息传递没有问题,您可以让 bean 抛出它以中止启动:

@Bean
public WatchService watchService() throws IOException {
    WatchService watchService = FileSystems.getDefault().newWatchService();
    Paths.get(dirPath).register(watchService, ENTRY_CREATE);
    logger.info("Started watching \"{}\" directory ", dlsDirPath);
}

如果您想要比默认 IOException 更友好的错误消息(以更好地帮助用户指出错误),您可以使用自定义异常消息抛出您自己的异常:

@Bean
public WatchService watchService() {
    try {
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Paths.get(dirPath).register(watchService, ENTRY_CREATE);
        logger.info("Started watching \"{}\" directory ", dlsDirPath);
        return watchService;
    } catch (IOException e) {
        throw new IllegalStateException(
                "Failed to create WatchService for directory \"" + dirPath + "\"", e);
    }
}