spring-boot-loader modules allows Spring Boot to support executable jar and
war files. If you’re using the Maven or Gradle plugin, executable jars are
automatically generated and you generally won’t need to know the details of how
If you need to create executable jars from a different build system, or if you are just curious about the underlying technology, this section provides some background.
Java does not provide any standard way to load nested jar files (i.e. jar files that are themselves contained within a jar). This can be problematic if you are looking to distribute a self contained application that you can just run from the command line without unpacking.
To solve this problem, many developers use ``shaded'' jars. A shaded jar simply packages all classes, from all jars, into a single 'uber jar'. The problem with shaded jars is that it becomes hard to see which libraries you are actually using in your application. It can also be problematic if the the same filename is used (but with different content) in multiple jars. Spring Boot takes a different approach and allows you to actually nest jars directly.
Spring Boot Loader compatible jar files should be structured in the following way:
example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-com | +-mycompany | + project | +-YouClasses.class +-lib +-dependency1.jar +-dependency2.jar
Dependencies should be placed in a nested
Spring Boot Loader compatible war files should be structured in the following way:
example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-WEB-INF +-classes | +-com | +-mycompany | +-project | +-YouClasses.class +-lib | +-dependency1.jar | +-dependency2.jar +-lib-provided +-servlet-api.jar +-dependency3.jar
Dependencies should be placed in a nested
WEB-INF/lib directory. Any dependencies
that are required when running embedded but are not required when deploying to
a traditional web container should be placed in
The core class used to support loading nested jars is
org.springframework.boot.loader.jar.JarFile. It allows you load jar
content from a standard jar file, or from nested child jar data. When first loaded, the
location of each
JarEntry is mapped to a physical file offset of the outer jar:
myapp.jar +---------+---------------------+ | | /lib/mylib.jar | | A.class |+---------+---------+| | || B.class | B.class || | |+---------+---------+| +---------+---------------------+ ^ ^ ^ 0063 3452 3980
The example above shows how
A.class can be found in
B.class from the nested jar can actually be found in
B.class is at position
Armed with this information, we can load specific nested entries by simply seeking to appropriate part if the outer jar. We don’t need to unpack the archive and we don’t need to read all entry data into memory.
Spring Boot Loader strives to remain compatible with existing code and libraries.
org.springframework.boot.loader.jar.JarFile extends from
should work as a drop-in replacement. The
RandomAccessJarFile.getURL() method will
URL that opens a
java.net.JarURLConnection compatible connection.
RandomAccessJarFile URLs can be used with Java’s
org.springframework.boot.loader.Launcher class is a special bootstrap class that
is used as an executable jars main entry point. It is the actual
Main-Class in your jar
file and it’s used to setup an appropriate
URLClassLoader and ultimately call your
There are 3 launcher subclasses (
Their purpose is to load resources (
.class files etc.) from nested jar files or war
files in directories (as opposed to explicitly on the classpath). In the case of the
[Jar|War]Launcher the nested paths are fixed (
the war case) so you just add extra jars in those locations if you want more. The
PropertiesLauncher looks in
lib/ by default, but you can add additional locations by
setting an environment variable
(comma-separated list of directories or archives).
You need to specify an appropriate
Launcher as the
Main-Class attribute of
META-INF/MANIFEST.MF. The actual class that you want to launch (i.e. the class that
you wrote that contains a
main method) should be specified in the
For example, here is a typical
MANIFEST.MF for an executable jar file:
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.mycompany.project.MyApplication
For a war file, it would be:
Main-Class: org.springframework.boot.loader.WarLauncher Start-Class: com.mycompany.project.MyApplication
You do not need to specify
PropertiesLauncher has a few special features that can be enabled with external
properties (System properties, environment variables, manifest entries or
Comma-separated Classpath, e.g.
Location of additional properties file, e.g.
Default arguments for the main method (space separated)
Name of main class to launch, e.g.
Name of properties file, e.g.
Path to properties file, e.g.
Boolean flag to indicate that all properties should be added to System properties
Manifest entry keys are formed by capitalizing initial letters of words and changing the
separator to “-” from “.” (e.g.
Loader-Path). The exception is
is looked up as
Start-Class in the manifest for compatibility with
Environment variables can be capitalized with underscore separators instead of periods.
loader.homeis the directory location of an additional properties file (overriding the default) as long as
loader.config.locationis not specified.
loader.pathcan contain directories (scanned recursively for jar and zip files), archive paths, or wildcard patterns (for the default JVM behavior).
There are a number of restrictions that you need to consider when working with a Spring Boot Loader packaged application.
ZipEntry for a nested jar must be saved using the
ZipEntry.STORED method. This
is required so that we can seek directly to individual content within the nested jar.
The content of the nested jar file itself can still be compressed, as can any other
entries in the outer jar.
Launched applications should use
Thread.getContextClassLoader() when loading classes
(most libraries and frameworks will do this by default). Trying to load nested jar
ClassLoader.getSystemClassLoader() will fail. Please be aware that
java.util.Logging always uses the system classloader, for this reason you should
consider a different logging implementation.