Issue
I have a Spring Boot application that allows the loading of additional components from the class path (if they are subclasses of an interface defined in my application). This worked great in Spring Boot 1.3.8, however I would like to upgrade to Spring Boot 1.4.2 but the href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#executable-jar-layout" rel="nofollow noreferrer">new layout of executable JARs is causing some issues.
When I now run my application with an additional JAR on the class path, I get a ClassNotFoundException for the interface I'm trying to subclass. This suggests to me that although the application knows where my classes are in BOOT-INF, because they are not in a standard location the additional components can't find them.
Does anyone know how to work around this?
I have tried the different layout options in spring-boot-maven-plugin, but none of them seem to do what I want.
Solution
Spring Boot executable jar uses its own ClassLoader created by a Launcher at runtime. Classes loaded by System ClassLoader (specified by -cp
classpaths) are not aware of classes inside the executable jar which are loaded by a child ClassLoader. Hence the ClassNotFoundException in your case.
Executable jars in JAR
or WAR
layout use simple Launcher
s, that only care about classes packaged in the bundle. You have to use PropertiesLauncher
to instruct Spring Boot's ClassLoader to load your classes by specifying loader.path
application property in either way:
Method 1. Run PropertiesLauncher class directly:
$ java -Dloader.path=... -cp myjar.jar org.springframework.boot.loader.PropertiesLauncher [args...]
This command will bypass the default Luncher (listed in the Main-Class
entry in myjar.jar!/META-INF/MANIFEST.MF) in your executable jar/war and directly invoke the PropertiesLauncher
class.
Method 2. Set PropertiesLauncher as default (by using ZIP
layout)
ZIP layout, as described in Spring Boot documentation:
Similar to JAR layout, using
PropertiesLauncher
.
Packaged executable in the layout will by default look for loader.*
properties, so you can run it just like:
$ java -Dloader.path=... -jar myjar.jar [args...]
For Maven projects, setting layout to ZIP in your POM.xml:
<project>
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
</configuration>
</plugin>
</plugins>
</build>
</project>
For Gradle projects:
springBoot {
layout = 'ZIP'
}
Answered By - tan9
Answer Checked By - David Marino (JavaFixing Volunteer)