Spring Boot 测试多模块专业应用

bgtovc5b  于 2022-12-26  发布在  Spring
关注(0)|答案(4)|浏览(154)

我有一个使用Spring引导的多模块maven应用程序:

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

在我的module-it中,我添加了其他模块作为依赖项。
当我用maven构建我的项目时,我得到了"构建成功":

mvn clean install

到目前为止一切顺利。
然而我希望我的每个模块在构建结束时都是可执行的jar。使用上面的设置,manifest没有定义,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会让我的构建失败,以及我该如何解决这个问题吗?
先谢谢你的帮助。

7bsow1i6

7bsow1i61#

要解决此问题,我们可以添加一个分类器,如文档自定义重新打包分类器中所述
然后插件变为:

<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>
afdcj2ne

afdcj2ne2#

您也可以将 repackage 目标参数 attach 设置为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>
nx7onnlm

nx7onnlm3#

至少对于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>
7kqas0il

7kqas0il4#

对一个老问题的迟来的回答,但是我在过去的几个小时里一直在研究这个问题。即使以前的回答是有帮助的,他们也没有解释为什么这个问题会发生。
对于其他可能遇到这个问题的人,这里有一个详细的解释,为什么它不工作
∮在一个果壳里∮
通常Maven会把你的应用程序打包成一个普通的. jar文件,所有编译好的类都放在. jar文件中一个众所周知的位置,所以编译器导入. jar作为一个库并加载可用的. class是非常直接的。
但是spring-boot-maven-plugin实际上是在修改. jar结构,以便在启动. jar应用程序时利用spring-boot逻辑,简而言之,. class不能作为"库"从生成的. jar中导入,因为spring类已经占用了众所周知的位置。

详细说明

让我们通过一个示例来探讨这个问题

项目结构

让我们设想一个包含多个maven模块的项目,如下所示

my-app/        -- The parent project
├─ pom.xml 
├─ application/
│  ├─ pom.xml
├─ integration-tests/
│  ├─ pom.xml

给定以下pom.xml文件:

    • 我的应用程序/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>
    • 我的应用程序/应用程序/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>
    • 我的应用程序/集成测试/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

当然,它会悲惨地失败,错误为cannot find symbol,但为什么会这样呢?
让我们看看失败构建后的架构:

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 * 模块输出执行了以下操作:

  • 将编译的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引导加载程序的类,而不是我们自己的. 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/          -- contains the spring boot loader classes

溶液

摆脱spring-boot-maven-plugin不是一个可以接受的解决方案;当然,您的项目将是可构建的,但生成的. jar不会是一个 Spring 启动的独立运行. jar。
相反,您可以指示spring-boot-maven-plugin替换原始jar,并使用另一个名称构建 * spring boot jar *。
为此,您需要在 * application * 模块中配置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

相关问题