Von Dr. Cassian Holt, Testing & Performance Engineering bei Java Fleet in Essen-Rüttenscheid


📚 Was bisher geschah – GraalVM-Serie

Bereits veröffentlicht:

Heute: Teil 4 zeigt End-to-End, wie wir eine Java-Swing-App als Native Image bauen – macOS, Linux, Windows.


⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden

Wir bauen eine kleine Java-Swing App (JFrame, Icon, Menü) und erzeugen dafür ein Native Image.
Du bekommst: ein minimales Projektgerüst, eine pom.xml mit GraalVM Native Build Tools für Maven, OS-VoraussetzungenRessourcen-Einbindung (Icons, Properties), Reflection/Agent-Workflow für Spezialfälle, sowie Troubleshooting.
Build-Kommando: mvn -Pnative -DskipTests package – fertig ist das GUI-Binary pro OS.


Hi, Java-Fleet-Crew! 👋

Cassian hier – heute schieben wir eine Desktop-App über die Linie: Swing → Native Image.
Ziel: schneller Starteinfache Verteilungkeine JVM-Installation beim Endnutzer.


Projekt anlegen

1) Minimaler Code (Swing)

package com.example.swingnative;

import javax.swing.*;
import java.awt.*;
import java.net.URL;

public class Main {
    public static void main(String[] args) {
        System.setProperty("java.awt.headless", "false");

        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame("Swing Native Image");
            f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            f.setSize(480, 320);
            f.setLocationRelativeTo(null);

            URL iconUrl = Main.class.getResource("/icons/app.png");
            if (iconUrl != null) {
                ImageIcon icon = new ImageIcon(iconUrl);
                f.setIconImage(icon.getImage());
            }

            f.add(new JLabel("Hello, Native Image! 🚀", SwingConstants.CENTER), BorderLayout.CENTER);

            JMenuBar bar = new JMenuBar();
            JMenu file = new JMenu("Datei");
            file.add(new JMenuItem("Beenden"));
            bar.add(file);
            f.setJMenuBar(bar);

            f.setVisible(true);
        });
    }
}

Maven einrichten

2) Basis-POM

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>swing-native</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <graalvm.native.plugin.version>0.11.0</graalvm.native.plugin.version>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>com.example.swingnative.Main</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.graalvm.buildtools</groupId>
        <artifactId>native-maven-plugin</artifactId>
        <version>${graalvm.native.plugin.version}</version>
        <extensions>true</extensions>
        <configuration>
          <imageName>swing-native</imageName>
          <mainClass>com.example.swingnative.Main</mainClass>
          <fallback>false</fallback>
          <verbose>true</verbose>
          <buildArgs>
            <buildArg>--no-fallback</buildArg>
            <buildArg>--initialize-at-build-time</buildArg>
            <buildArg>--enable-https</buildArg>
          </buildArgs>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <profile>
      <id>native</id>
      <properties>
        <skipTests>true</skipTests>
      </properties>
      <build>
        <plugins>
          <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <version>${graalvm.native.plugin.version}</version>
            <executions>
              <execution>
                <id>build-native</id>
                <goals>
                  <goal>compile-no-fork</goal>
                </goals>
                <phase>package</phase>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

Ressourcen & Reflection korrekt einbinden

3) Ressourcen (Icons, Properties)

{
  "resources": {
    "includes": [
      { "pattern": "icons/.*\.png$" },
      { "pattern": ".*\.properties$" }
    ]
  }
}

4) Reflection / Dynamik (falls gebraucht)

mvn -Pnative -Dagent=true test
mvn -Pnative -Dagent=true -DskipTests -DskipNativeBuild=true package exec:exec@java-agent

OS-Voraussetzungen & Build

5) GraalVM JDK & Tools

  • macOS: Xcode CLT
  • Linux: build-essential / gcc
  • Windows: MS Visual Studio Build Tools

6) Build-Befehle

macOS/Linux:

mvn -Pnative -DskipTests package
./target/swing-native

Windows:

mvn -Pnative -DskipTests package
target\swing-native.exe

🚀 Ergebnis

Ein klickbares Binary pro OS, ohne JVM. Start spürbar schneller, Distribution einfacher.


🗓️ Nächste Woche: Packaging & Distribution mit jpackage

„macOS .app, Windows .msi, Linux .deb/.rpm – mit jpackage & Codesigning“


❓ FAQ – Swing Native Image Edition

Q: Brauche ich immer resource-config.json?
A: Nur für Klassenpfad-Ressourcen.

Q: Wie gehe ich mit Reflection um?
A: Agent laufen lassen, Konfigs übernehmen.

Q: Kann ich ohne das Maven-Plugin bauen? 
A: Ja, via CLI und dem Befehl native-image.

Q: Wie aktiviere ich HTTPS?
A:  Über –enable-https.

Q: Windows-Tipp? 
A: Immer „x64 Native Tools Command Prompt“ nutzen.


📖 GraalVM – Alle Teile im Überblick

  • Teil 1: Der heilige Graal
  • Teil 2: Native Images – Wo Java schnell wird
  • Teil 3: Enterprise Production
  • Teil 4: Swing Native Image
  • Teil 5: Packaging mit jpackage

Tags: #GraalVM #NativeImage #Swing #Java #Maven #macOS #Linux #Windows

Autor

  • Cassian Holt

    43 Jahre alt, promovierter Informatiker mit Spezialisierung auf Programming Language Theory. Cassian arbeitet als Senior Architect bei Java Fleet Systems Consulting und bringt eine einzigartige wissenschaftliche Perspektive in praktische Entwicklungsprojekte. Seine Leidenschaft: Die Evolution von Programmiersprachen und warum "neue" Features oft alte Konzepte sind.