Spring Boot测试多模块Maven应用程序

7

我有一个使用Spring Boot的多模块Maven应用程序:

- spring boot parent
    - myproject parent (both parent and module pom)
        - module1
        - module2
        - module-it (integration tests)

在我的模块-it中,我将其他模块作为依赖项添加。

当我使用maven构建项目时,我收到“构建成功”的消息:

mvn clean install

目前为止一切顺利。
然而,我希望在构建结束时,我的每个模块都是可执行的jar包。使用上述设置,清单未定义,因此该jar包无法执行。 为了解决这个问题,我已经在module1和module2的pom文件中添加了以下内容:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

使用这个设置,我的jar文件是可执行的,但是我不能再构建了。我在我的模块中使用的类找不到了。

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project module-it: Compilation failure: Compilation failure:
[ERROR] /home/user/<path-to-project>/testing/module-it/src/test/java/com/mycompany/testing/greeting/GreetingControllerIT.java:[20,17] cannot find symbol
[ERROR] symbol:   class GreetingController
[ERROR] location: class com.mycompany.testing.greeting.GreetingControllerIT
[ERROR] /home/user/<path-to-project>/testing/module-it/src/test/java/com/mycompany/testing/hello/HelloControllerIT.java:[20,17] cannot find symbol
[ERROR] symbol:   class HelloController
[ERROR] location: class com.mycompany.testing.hello.HelloControllerIT
[ERROR] /home/user/<path-to-project>/testing/module-it/src/test/java/com/mycompany/testing/greeting/GreetingControllerIT.java:[16,27] cannot find symbol
[ERROR] symbol: class GreetingController
[ERROR] /home/user/<path-to-project>/testing/module-it/src/test/java/com/mycompany/testing/hello/HelloControllerIT.java:[16,27] cannot find symbol
[ERROR] symbol: class HelloController

你能帮我理解为什么spring-boot-maven-plugin会导致我的构建失败以及如何解决这个问题吗?

非常感谢您的帮助。

4个回答

4
为了解决这个问题,我们可以按照文档中描述的方式添加分类器 custom repackage classifier
插件变成了:
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
            <configuration>
                <classifier>exec</classifier>
            </configuration>
        </execution>
    </executions>
</plugin>

2
此外,您可以将重新打包目标参数附加设置为false:
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <attach>false</attach>
                    </configuration>
                </execution>
            </executions>
        </plugin>

1
晚回答一个旧问题,但我刚刚在过去几个小时里一直在处理这个问题。尽管之前的答案有帮助,但它们没有解释为什么会出现这个问题。
对于其他可能遇到此问题的人,这里是一个详细的解释为什么不起作用。
简而言之,
通常,Maven将您的应用程序打包为一个常规的.jar文件,其中所有编译类都位于.jar文件中的一个众所周知的位置。 因此,对于编译器来说,导入.jar作为库并加载可用的.class文件非常简单。
但是,spring-boot-maven-plugin实际上修改了.jar结构,以便在启动.jar应用程序时利用spring-boot逻辑。 简而言之,由于spring类占用了众所周知的位置,.class文件无法作为“库”从生成的.jar中导入。
详细解释如下:
让我们通过一个例子来探讨这个问题。
项目结构
假设有一个包含多个Maven模块的项目,如下所示
my-app/        -- The parent project
├─ pom.xml 
├─ application/
│  ├─ pom.xml
├─ integration-tests/
│  ├─ pom.xml


给定以下的pom.xml文件:

my-app/pom.xml:

<project [...]>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
        <relativePath/>
    </parent>

    <groupId>com.me</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>application</module>
        <module>integration-tests</module>
    </modules>
</project>

my-app/application/pom.xml:

<project [...]>
    <parent>
        <groupId>com.me</groupId>
        <artifactId>my-app</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>application</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

my-app/integration-tests/pom.xml:

<project [...]>
    <parent>
        <groupId>com.me</groupId>
        <artifactId>my-app</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>integration-tests</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>com.me</groupId>
            <artifactId>application</artifactId>
        </dependency>
    </dependencies>

</project>

为什么它没有构建?
让我们尝试打包我们的应用。
/my-app$ mvn package

当然,它会以一个错误的“找不到符号”而悲惨地失败,但为什么会这样呢?
让我们在构建失败后来看一下我们的架构:
my-app/        -- The parent project
├─ pom.xml 
├─ application/
│  ├─ pom.xml
│  ├─ target/
│  │  ├─ application-1.0.0-SNAPSHOT.jar
│  │  ├─ application-1.0.0-SNAPSHOT.jar.original
├─ integration-tests/
│  ├─ pom.xml
│  ├─ target/

spring-boot-maven-plugin应用程序模块的输出进行了几项操作:

  • 将编译后的application-1.0.0-SNAPSHOT.jar重命名为application-1.0.0-SNAPSHOT.jar.original
  • 创建了自己的.jar文件,名称为application-1.0.0-SNAPSHOT.jar

让我们来探索一下application-1.0.0-SNAPSHOT.jar的结构:

BOOT-INF/
├─ classes/
│  ├─ com/
│  │  ├─ me/    -- The compiled .class of your project reside here
META-INF/
org/
├─ springframework/
│  ├─ boot/     -- contains the spring boot loader classes

如您所见,您的.jar文件根目录下的.class文件是Spring Boot加载器类,而不是我们自己的.class文件,这些文件被放置在BOOT-INF/classes/文件夹中。 这并不常规,当将.jar文件作为依赖导入时,它不会在这里搜索要导入的类。
正因为如此,当Maven尝试打包integration-tests模块时,会失败,因为application-1.0.0-SNAPSHOT.jar中存在的类实际上是一堆Spring类,而不是您尝试从application模块导入的类。
如果您查看application-1.0.0-SNAPSHOT.jar.origial的结构,会发现如下所示:
META-INF/
com/
├─ me/          -- The compiled .class of your project reside here

解决方案

摆脱spring-boot-maven-plugin并不是一个可接受的解决方案;当然您的项目将可构建,但生成的.jar文件将不是一个独立运行的Spring Boot .jar文件。

相反,您可以指示spring-boot-maven-plugin 不要替换原始的jar,并以另一个名称构建spring boot jar

为此,您需要在应用程序模块中配置spring-boot-maven-plugin

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <configuration>
                            <classifier>exec</classifier>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

现在当您构建您的项目时,您将会得到类似这样的内容:

my-app/        -- The parent project
├─ pom.xml 
├─ application/
│  ├─ pom.xml
│  ├─ target/
│  │  ├─ application-1.0.0-SNAPSHOT.jar -- the original untouched .jar
│  │  ├─ application-1.0.0-SNAPSHOT-exec.jar -- the spring boot executable .jar
├─ integration-tests/
│  ├─ pom.xml
│  ├─ target/
│  │  ├─ integration-tests-1.0.0-SNAPSHOT.jar


0

至少对于 spring-boot-starter-parent:2.6.0 pom 文件,spring-boot-maven-plugin 的配置包含一个用于执行 repackage 目标的 <id>repackage</id>

所以我也必须添加这行代码 <id>repackage</id>

完整配置:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>repackage</id>
            <goals>
                <goal>repackage</goal>
            </goals>
            <configuration>
                <classifier>exec</classifier>
            </configuration>
        </execution>
    </executions>
</plugin>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接