Shipping a service means producing something that runs with java -jar; each approach has trade-offs.
A plain mvn package jar contains only your classes; dependencies are not included and the manifest has no Main-Class. Options for runnable artifacts:
- Jar plugin + classpath: set
<manifest><mainClass>and<addClasspath>true</addClasspath>, and copy dependencies next to it withdependency:copy-dependencies. Clean and fast, but you ship a folder. - maven-shade-plugin: merges all dependency classes into one uber jar (
java -jar app.jar). Use theManifestResourceTransformerforMain-ClassandServicesResourceTransformerto mergeMETA-INF/servicesfiles, or service loading (JDBC drivers, Jackson modules) silently breaks. Shade can also relocate packages to avoid conflicts, which is how libraries bundle dependencies safely. Signed jars must have their signatures excluded. - maven-assembly-plugin with the
jar-with-dependenciesdescriptor: simpler but merges files naively. - spring-boot-maven-plugin
repackage: nests dependency jars inside a launcher jar, avoiding merging problems; also builds OCI images withbuild-image. - jlink/jpackage plugins produce custom runtimes and native installers.
Resources from src/main/resources are copied into the jar; enable <filtering> to inject ${project.version} into files like application.properties, but keep binaries out of filtered directories.
Set project.build.outputTimestamp for reproducible builds so the same source yields a byte-identical jar, which the Maven Release and verification tooling increasingly expect.
Example
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.acme.App</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter> <!-- drop signatures of signed dependencies -->
<artifact>*:*</artifact>
<excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- mvn package && java -jar target/shop-api-1.0.0-SNAPSHOT.jar -->Remember
- A default jar has no dependencies and no Main-Class
- Shade merges classes; use transformers for manifests and META-INF/services
- Spring Boot's repackage nests jars instead of merging
- Set project.build.outputTimestamp for reproducible jars
Watch out for
- Shading without ServicesResourceTransformer, so JDBC drivers or Jackson modules are not found at run time.