Spring Boot Application class does not existed in the test module(JPMS)

Viewed 418

I tried to use Java 9+ built-in JPMS(Java Platform Module System) to modulize a Spring Boot application, check the complete sample codes here.

In IDEA, when running the tests, it worked well.

But when I ran it via mvn clean verify in the root folder, and the greeting.application.test will fail due to an exception "can not find the package com.example.greeting.application and class GreetingApplication".

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project greeting-application-test: Compilation failure: Compilation fai
lure:
[ERROR] /D:/hantsylabs/spring-puzzles/modular-jpms/greeting-application-test/src/main/java/com/example/greeting/application/test/GreetingApplicationTests.java:[3,40] package com.exampl
e.greeting.application does not exist
[ERROR] /D:/hantsylabs/spring-puzzles/modular-jpms/greeting-application-test/src/main/java/com/example/greeting/application/test/GreetingApplicationTests.java:[13,19] cannot find symbo
l
[ERROR]   symbol: class GreetingApplication

The module-info.java in the greeting.application.test like this.

module greeting.application.test {
    requires greeting.application;

    requires spring.web;
    requires spring.core;
    requires spring.beans;
    requires spring.context;

    requires spring.boot;
    requires spring.boot.autoconfigure;
    requires spring.webflux;

    requires spring.test;
    requires spring.boot.test;
    requires spring.boot.test.autoconfigure;

    requires org.junit.jupiter;
    requires org.junit.jupiter.api;
    requires org.junit.jupiter.params;
    requires org.junit.jupiter.engine;
    requires org.junit.platform.commons;
    requires org.assertj.core;
    requires org.mockito.junit.jupiter;
    requires transitive net.bytebuddy;

    opens com.example.greeting.application.test  to spring.core, org.junit.platform.commons;
}
1 Answers

The issue is not really with a "test" module, but any other module trying to access the application you have built.

This is caused by spring-boot-maven-plugin used to repackage the maven module greeting-application corresponding to its java module.

The #Classifier section in the documentation talks about the reason clearly further(highlighted by me):

By default, the repackage goal replaces the original artifact with the repackaged one. That is a sane behavior for modules that represent an application but if your module is used as a dependency of another module, you need to provide a classifier for the repackaged one. The reason for that is that application classes are packaged in BOOT-INF/classes so that the dependent module cannot load a repackaged jar’s classes.

Hence, in order to fix the issue, you could work out with

<build>
    <plugins>
        <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>
    </plugins>
</build>

Quoting the documentation further and relative to the config update:

This configuration will generate two artifacts: the original one and the repackaged counter part produced by the repackage goal. Both will be installed/deployed transparently.

Related