13

NOTE: Please, before marking this question as a duplicate make sure you know the difference between executable JAR and fully executable SpringBoot JAR.

The official Spring Boot documentation describes how to build fully executable JAR. Then generated JAR file can be linked from /etc/init.d/ and started/stopped/restarted/statused as a normal unix service without additional scripts or tools like JSVC.

But the generated JAR contains all libraries and can be big enough in size (in my case 70Mb+).

I want to generate such fully executable JAR without libraries, but then to be able to run it as SystemV service on Linux and link external libraries (JARs) somehow.

UPDATE

I want to reduce the artifact size in order to speed up deploy->test->fix cycle. Sometimes I'm working via mobile network and big file size can decrease my job speed dramatically.

In case there is no a simple configuration property or a profile or a command line option I would use a kind of hack.

At the beginning, I can generate a build containing all dependencies. Then I can unzip it and move all libraries to a special folder.

Then I need to pack it again as fully executable somehow and run with pointing to the folder with libraries.

I don't think this can be done with jar utility because file utility recognizes fully executable jar as data

$ file fully-executable.jar
file fully-executable: data

unlike the usual jar

$ file usual.jar
usual.jar: Java Jar file data (zip)
humkins
  • 9,635
  • 11
  • 57
  • 75
  • Why? Then you'll just have to maintain all the dependencies externally and that will require another deployment step, etc. – Gregg Jun 22 '17 at 19:20
  • @Gregg I've updated the post. Please let me know if it is still unclear. – humkins Jun 22 '17 at 19:36
  • You can execute it as an exploded jar. So you could create the fat jar, unzip it, deploy just the artifacts you want, leaving the rest "cached". See this article for a similar issue: http://bsideup.blogspot.com/2015/04/spring-boots-fat-jars-vs-docker.html – Gregg Jun 22 '17 at 19:50
  • 2
    @JarrodRoberson That is NOT a duplicate at all. I know how to create a simple JAR, executable JAR. But I'm asking about FULLY executable JAR. I've explained the difference between them. – humkins Jun 22 '17 at 20:01
  • @Gregg no, exploded means "unpacked". Fully executable JAR can be executed like an ordinary ELF file, for example `./myapp.jar`, without prepending `java -jar`. – humkins Jun 22 '17 at 21:00
  • What part of *Spring Boot Executable Jar File Without Dependencies* is not exactly the same as your question? Just because you do not like the answers on that question does not make it not a duplicate. –  Jun 22 '17 at 23:09
  • the semantics are no difference in the implementation if you leave out the word `fully`. An executable is executable or it is not. It implies that there is a *partially* executable state. That makes no sense and does not exist as a term. The duplicate question asks the exact same thing you are **What is the easiest way to build spring boot jar file without its dependencies? Basically I should be able to keep dependency jar files in a separate folder.** –  Jun 23 '17 at 02:32
  • 3
    @JarrodRoberson There is a difference between a fully-executable jar file and an executable jar file. Both terms exist in Spring Boot's documentation. The former has a bash script prepended to the file and can be run with `./foo.jar`. The latter does not and can only be run with `java -jar foo.jar`. I've reopened the question as it is not a duplicate – Andy Wilkinson Jun 23 '17 at 04:05

2 Answers2

15

You may want to consider using Spring Boot Thin Launcher. It creates a jar file with your application code but none of its dependencies. It adds a special thin launcher that knows how to resolve your application's dependences from a remote Maven repository or from a local cache when the jar is executed. Judging by the description of what you want to do, you'd utilise the local cache option.

The configuration of Spring Boot's Maven plugin to produce a fully executable jar that uses the thin launcher looks like this:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot.experimental</groupId>
                    <artifactId>spring-boot-thin-layout</artifactId>
                    <version>1.0.3.RELEASE</version>
                </dependency>
            </dependencies>
            <configuration>
                <executable>true</executable>
            </configuration>
        </plugin>
    </plugins>
</build>
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
0

I can use this setting to create spring boot jar without dependency jars.

Copy dependency jars to dist/lib

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <includeScope>runtime</includeScope>
                <outputDirectory>${project.build.directory}/dist/lib</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

Specify 'lib' as classpath prefix, so MAINFEST.MF will be created like this:

Class-Path:lib/httpcore-nio-4.4.14.jar lib/guava-24.1.1-jre.jar...

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>com.xxx.MyMainClass</mainClass>
            </manifest>
        </archive>
        <outputDirectory>${project.build.directory}/dist</outputDirectory>
    </configuration>
</plugin>

Let spring boot plugin includes dependency which is not existing, will cause spring boot jar exclude all dependencies.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <layout>ZIP</layout>
        <includes>
            <include>
                <groupId>not-exist-in-my-project</groupId>
                <artifactId>not-exist-in-my-project</artifactId>
            </include>
        </includes>
        <outputDirectory>${project.build.directory}/dist</outputDirectory>
    </configuration>
</plugin>
  • Can you please explain: "Let spring boot plugin includes dependency which is not existing, will cause spring boot jar exclude all dependencies."? Does it mean that if we keep the `...` empty, no dependency will be included in the final runnable jar? – Jugal Mistry Jul 28 '22 at 10:13
  • In my case, If I don't include an dependency which actually not exist, spring-boot-maven-plugin still package all dependencies in jar. – markhuang1994 Aug 05 '22 at 06:23