Late answer to an old question but i've just been working on this for the past hours. Even though the previous answers were helpfull they don't explain why this problem is happening.
For others that may comes accross this issue here is a detailed explnation why it's not working
In a Nutshell
Usualy Maven package your application as a regular .jar with all compiled class being in a well known location in the .jar file.
So it's pretty straighforward for a compiler to import the .jar as a library and to load the available .class.
But the spring-boot-maven-plugin
is actually modifying the .jar structure to leverage spring-boot logic when you start the .jar application.
In short, the .class are not available to be imported as a "library" from the resulting .jar, because the spring class have took the well known location for itself.
Detailed explanation
Let's explore the problem with an example
Project structure
Let's imagine a project with multiple maven modules like so
my-app/ -- The parent project
├─ pom.xml
├─ application/
│ ├─ pom.xml
├─ integration-tests/
│ ├─ pom.xml
Given the following pom.xml files:
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>
Why is it not building
Let's try to package our app
/my-app$ mvn package
Of course it will miserably fail with an error cannot find symbol
, but why is it so?
Let's take a look at our architecture after the failed build:
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/
The spring-boot-maven-plugin
has done several things to the application module output:
- renamed the compiled
application-1.0.0-SNAPSHOT.jar
to application-1.0.0-SNAPSHOT.jar.original
- created it's own .jar with the name of
application-1.0.0-SNAPSHOT.jar
Let's explore the structure of the 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
As you can see the .class files at the root of your .jar are the spring boot loader classes, not our own .class that are relegated to the BOOT-INF/classes/
folder.
This is not conventional, and when the .jar is imported as a dependency it won't search here for class to import.
Because of that, when maven try to package the integration-tests module, it fails because the class present in the application-1.0.0-SNAPSHOT.jar
are actually a bunch of spring class instead of the one you are trying to import from application module.
If you were to look at the structure of the application-1.0.0-SNAPSHOT.jar.origial
it would be something like so:
META-INF/
com/
├─ me/ -- The compiled .class of your project reside here
Solution
Getting rid of spring-boot-maven-plugin
is not an acceptable solution; Of course your project will be buildable, but the resulting .jar won't be a spring boot standalone running .jar.
Instead you can instruct the spring-boot-maven-plugin
to not replace the original jar and to build to spring boot jar with another name.
To do so you'll need to configure the spring-boot-maven-plugin
in the application module:
<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>
Now when you build your project you'll have something like that:
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