Nowadays Java is used for building server applications and the Spring Framework is probably one of the most popular tools for creating microservices. However, Java is a 32-year-old programming language and for most of the time it was used for building UI applications - either as Java Applets or standalone desktop applications. And many of them have stayed with us until these days - for example IntelliJ IDE that is based on the Java Swing framework.
Of course it’s easy to say that in 2025 the state of Java UI frameworks, such as Swing, JavaFX or SWT is unclear:
I will focus on the Swing framework because in my opinion it is still the safest
option for building desktop applications in Java. And new features that were added
to JDK some time ago make this choice more compelling. From Java 16, developers
can use the jpackage
tool to build a distribution package that will contain a
Java runtime image with necessary libraries, an application and a launcher. To
run such an application, users don’t need to download and install the Java JRE
because runtime is part of the package. Furthermore we can adjust runtime shape
with jlink
and include only those modules that are required in our application.
For example, we can create runtime with java.base
and java.desktop
modules
that should be enough for running a basic desktop application.
A distribution package can be prepared with three Maven plugins:
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<id>exec-jar</id>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<finalName>my-app</finalName>
<outputDirectory>${project.build.directory}/exec</outputDirectory>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.milosz.apps.MyApp</mainClass>
</transformer>
</transformers>
</configuration>
</plugin>
Assembles JRE image with modules java.base
and java.desktop
. All files are
saved in directory linkimage
.
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase> package </phase>
<configuration>
<target>
<link modules="java.base,java.desktop
destDir="target/linkimage" modulepath="${java.home}/jmods"
includeHeaders="false"
includeManPages="false" debug="false">
</link>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Plugins takes Uber Jar exec/my-app.jar
(Step 1) and JRE runtime from linkimage
directory and compiles distribution in directory jpackage/my-app
.
<plugin>
<groupId>com.github.akman</groupId>
<artifactId>jpackage-maven-plugin</artifactId>
<version>0.1.5</version>
<executions>
<execution>
<id>native-package</id>
<phase>package</phase>
<goals>
<goal>jpackage</goal>
</goals>
</execution>
</executions>
<configuration>
<name>my-app</name>
<mainjar>my-app.jar</mainjar>
<input>${project.build.directory}/exec</input>
<appversion>${project.version}</appversion>
<type>IMAGE</type>
<runtimeimage>${project.build.directory}/linkimage</runtimeimage>
</configuration>
</plugin>
Default Swing's "Metal" L&F doesn't look good on HiDPI devices. The good alternative
are themes from FlatLaf. To use it,
Maven dependency must be present and L&F loade before UI is displayed with code
UIManager.setLookAndFeel("com.formdev.flatlaf.FlatIntelliJLaf");