[spring-projects/spring-boot]开发工具导致配置错误的命令行应用程序以 0 退出

2024-05-14 648 views
6

我有一个非常简单的 spring 应用程序,但配置错误:

@SpringBootApplication()
public class SimpleApplication implements CommandLineRunner {
    public static void main(String[] args) {
        var ctx = SpringApplication.run(SimpleApplication.class, args);
        System.exit(SpringApplication.exit(ctx));
    }

    public static class MyObject {}

    //Oops - No bean provided!!!
    @Autowired MyObject myObject;

    @Override public void run(String... args) { }
}

我收到以下错误(在 intellij 中):

...
Consider defining a bean of type 'test.app.SimpleApplication$MyObject' in your configuration.

Process finished with exit code 1

这正是我所期望的。

如果我spring-boot-devtools在maven中添加依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

然后我得到以下信息:

...
Consider defining a bean of type 'test.app.SimpleApplication$MyObject' in your configuration.

Process finished with exit code 0

看来添加这个包会导致spring boot返回错误的代码。

回答

4

这是我完整的 Maven 配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springboot-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.5.5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <!-- This dependency causes the issue -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>
9
  1. 由于 devtools,该应用程序在 RestartLauncher 中运行 https://github.com/spring-projects/spring-boot/blob/v2.5.6/spring-boot-project/spring-boot-devtools/src/main/java/org /springframework/boot/devtools/restart/RestartLauncher.java#L46-L53

  2. 当 SpringApplication 处理失败时,它会调用 SpringBootExceptionHandler.registerLoggedException 来处理 UnsatisfiedDependencyException

  3. SpringApplication 做的最后一件事是ReflectionUtils.rethrowRuntimeException(exception); SpringApplication.java

  4. 这会导致 RestartLauncher 中出现的异常为包含 target UnsatisfiedDependencyException 的 java.lang.reflect.InvocationTargetException

  5. 最后,SpringBootExceptionHandler有一个已注册的异常UnsatisfiedDependencyException ..没有InvokingTargetException作为已注册的异常,因此退出代码为0

注册异常与实际异常

理想情况下,在这种情况下,它应该调查异常的目标并匹配它

2
26894 位于类似区域。
8

@cdmatta @wilkinsona 当依赖developmentOnly'org.springframework.boot:spring-boot-devtools'时,SpringBoot项目启动时会调度devtools中的org.springframework.boot.devtools.restart.RestartLauncher#run()函数,而异常状态码0是由devtools的异常状态返回的,我猜测这个问题应该与spring-boot-devtools的工作原理有关。

0

@msmerc @cdmatta 您可以通过以下方法进行调试。 图像 图像

@wilkinsona我认为这种情况不是一个错误,不是吗?

5

如果这是预期的行为,那么我认为 spring-boot-devtools 需要通知用户它已更改退出代码!否则这是非常不明显的。我花了很长时间才找到罪魁祸首。

9

对我来说这看起来像是一个错误。preventNonZeroExitCode()仅当 JVM 由于开发工具自身的SilentExitException.对于任何其他失败,目的是退出代码应保持不变。根据 @cdmatta 上面的描述,这是一个因此不应涉及UnsatisfiedDependencyException特定的代码。SilentExitException

8

@wilkinsona @msmerc 针对上述问题,我再次跟踪,得到如下结果:

1、首先是在https://github.com/spring-projects/spring-boot/blob/3478e1912cbd545b83788187bf92a06a7453167f/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework中设置SilentExitExceptionHandler /boot/devtools/restart/Restarter.java#L139

2、暂停主线程https://github.com/spring-projects/spring-boot/blob/3478e1912cbd545b83788187bf92a06a7453167f/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot /devtools/restart/Restarter.java#L296,然后获取子线程的错误

3、处理https://github.com/spring-projects/spring-boot/blob/3478e1912cbd545b83788187bf92a06a7453167f/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot中获得的错误/devtools/restart/Restarter.java#L262-L273

4、当我们跟踪https://github.com/spring-projects/spring-boot/blob/3478e1912cbd545b83788187bf92a06a7453167f/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools /restart/Restarter.java#L180,它将返回 SilentExitException

5、此时,主线程的SilentExitExceptionHandler处理SilentExitException并静默终止JVM https://github.com/spring-projects/spring-boot/blob/16f54d2c5c27813be63bffd7703285b499ee3892/sp ​​ring-boot-project/spring-boot-devtools /src/main/java/org/springframework/boot/devtools/restart/SilentExitExceptionHandler.java#L37-L49

我认为devtool并没有对子线程产生的异常进行分类并主动处理,而是选择通过日志监听器返回异常信息,并选择静默结束程序。当没有引入developmentOnly'org.springframework.boot:spring-boot-devtools'时,JVM返回的退出码应该是1。

3

@wilkinsona 如果这个逻辑不合理并且我想解决它,请给予一些指导。

9

如果您希望我们查看此问题,请提供所需的信息。如果在接下来的 7 天内未提供信息,此问题将被关闭。

8

@wilkinsona我不太确定我理解。显然,应用程序无意配置错误!

我们的应用程序有多个模块。基本上是一个 Web 应用程序和一堆支持 CommandLineRunner 实用程序。这些模块都有共同的依赖关系(包括 DevTools)——可以说这是不正确的,因为 devtools 仅对 web 应用程序有意义,但非 spring 专家几乎不会意识到这一点。

我们有一个测试,通过在持续集成阶段在代码上运行 spring 命令行实用程序之一来验证 spring 配置。尽管应用程序配置错误,但测试还是通过了,因为退出为 0,因此我们的测试没有发现配置问题。

另一个问题是开发工具的问题并不明显。输出中没有任何内容告诉您 devtools 正在更改退出代码。

8

谢谢,@msmerc。了解该使用是否是偶然的,或者是否存在我们忽略的用例,对于帮助我们确定修复的优先级非常有价值。如果我们忽略了一个用例,那么建议您避免在命令行应用程序中使用开发工具不会有太大帮助。