Eğitim Portalı/Java/Paketleme ve Dağıtım (JAR, Multi-Release JAR, jlink, jpackage)
Java01-java/94-paketleme-ve-dagitim

Paketleme ve Dağıtım (JAR, Multi-Release JAR, jlink, jpackage)

Kodu yazıp derlemek bir şey; onu başkalarının çalıştırabileceği bir artefakt olarak paketleyip dağıtmak başka bir şeydir. Bu konu, Java uygulamalarını paketleme ve dağıtma araçlarını ele alır: klasik JAR'dan, sürüme d…

Paketleme ve Dağıtım (JAR, Multi-Release JAR, jlink, jpackage)

Kodu yazıp derlemek bir şey; onu başkalarının çalıştırabileceği bir artefakt olarak paketleyip dağıtmak başka bir şeydir. Bu konu, Java uygulamalarını paketleme ve dağıtma araçlarını ele alır: klasik JAR'dan, sürüme duyarlı Multi-Release JAR'a, özel çalışma zamanı üreten jlink'e ve yerel kurulum paketi yapan jpackage'a kadar. Build araçları konusunda (topic 18) bağımlılık yönetimini gördük; burada "çıktıyı nasıl dağıtırım?" sorusuna odaklanıyoruz.

JAR (Java ARchive)

Bir JAR, derlenmiş .class dosyalarını ve kaynakları (resources: config, görsel, şablon) tek bir ZIP tabanlı arşivde toplar. İçinde bir META-INF/MANIFEST.MF dosyası bulunur (meta-veri: ana sınıf, sürüm, classpath).

jar --create --file uygulama.jar --main-class com.app.Main -C build/classes .
java -jar uygulama.jar          # MANIFEST'teki Main-Class çalışır

Sınıflar ister bir dizinden ister bir JAR'dan yüklensin, çalışma anında nereden geldiği görülebilir (getProtectionDomain().getCodeSource()). Kaynaklar dosya yoluyla değil, ClassLoader üzerinden okunur (getResource/getResourceAsStream) — çünkü JAR içindedirler. Örnek 1 (./Ornek1.java) bunu gösterir.

Fat/Uber JAR

Sıradan bir JAR yalnızca senin sınıflarını içerir; bağımlılıkların ayrıca classpath'te olmalıdır. Fat JAR (uber JAR), tüm bağımlılıkları da içine alır → tek dosyayla çalışır. Spring Boot'un çalıştırılabilir JAR'ı budur: java -jar uygulama.jar ile gömülü Tomcat dahil her şey çalışır (bu portalın backend'i de böyle paketlenebilir). Maven Shade / Gradle Shadow eklentileri üretir.

Multi-Release JAR (MR-JAR)

Java 9 ile gelen MR-JAR, tek bir JAR içinde farklı Java sürümleri için farklı sınıflar tutar:

uygulama.jar
├── com/app/Util.class            (varsayılan / eski JDK)
└── META-INF/versions/
    ├── 17/com/app/Util.class     (Java 17 için)
    └── 21/com/app/Util.class     (Java 21 için)

JVM, kendi sürümüne uygun sınıfı otomatik seçer. Bu, çalışma anında Runtime.version() ile tespit edilen sürüme göre en uygun kodun çalışmasını sağlar (Örnek 2, ./Ornek2.java). Kütüphaneler için idealdir: tek artefakt hem eski hem yeni JDK'da en iyi yolu kullanır. MANIFEST'te Multi-Release: true bulunur.

jlink: özel çalışma zamanı

jlink (Java 9+), uygulamanın ihtiyaç duyduğu yalnızca gerekli modülleri içeren, küçük ve bağımsız bir çalışma zamanı (JRE) üretir:

jlink --add-modules java.base,java.sql --output ozel-runtime

Faydası: tam JDK yerine çok daha küçük bir imaj → konteynerler (Docker) ve dağıtım için ideal. (Java 11'den beri ayrı JRE indirmesi olmadığı için, minimal çalışma zamanını jlink ile üretirsin — topic 85.)

jpackage: yerel kurulum paketi

jpackage (Java 14+), uygulamanı işletim sistemine özel yerel kurulum paketine dönüştürür: Windows'ta .exe/.msi, macOS'ta .dmg/.pkg, Linux'ta .deb/.rpm. İçine bir çalışma zamanı (jlink ile) gömer, böylece kullanıcının ayrıca Java kurmasına gerek kalmaz:

jpackage --name Uygulamam --input lib --main-jar uygulama.jar --type dmg

Masaüstü uygulamaları (Swing/JavaFX) dağıtmak için idealdir.

Hangi araç ne için?

AraçÜretirKullanım
jar.jar arşiviTemel paketleme
Fat/Shadow JARBağımlılıklı tek JARSunucu uygulamaları (Spring Boot)
Multi-Release JARSürüme duyarlı JARÇok-JDK destekli kütüphaneler
jlinkÖzel/küçük çalışma zamanıKonteyner, minimal dağıtım
jpackageYerel kurulum (.exe/.dmg/.deb)Masaüstü uygulama dağıtımı

Üretimde bunlar genelde build araçlarıyla otomatikleşir (Maven/Gradle eklentileri) ve konteyner imajlarına (Docker) gömülür.

Özet

Java uygulamalarını paketleme ve dağıtma araçlarını öğrendik: kaynakları toplayan JAR ve manifest, bağımlılıkları içeren fat JAR (Spring Boot); sürüme duyarlı Multi-Release JAR ve Runtime.version() (Örnek 1–2); minimal çalışma zamanı üreten jlink ve yerel kurulum yapan jpackage. Doğru paketleme, kodun başkalarına kolay ve güvenilir ulaşmasını sağlar.

Kod Örnekleri(2)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Sınıf nereden yüklendi? — code source, classpath ve kaynak (resource) erişimi.
2// (Paketleme: sınıflar bir dizinden mi yoksa JAR'dan mı geliyor — çalışma anında görülebilir.)
3// Çalıştırma: java Ornek1.java
4import java.security.CodeSource;
5
6public class Ornek1 {
7
8    public static void main(String[] args) {
9        // Bu sınıf NEREDEN yüklendi? (bir dizin veya bir .jar dosyası)
10        CodeSource cs = Ornek1.class.getProtectionDomain().getCodeSource();
11        System.out.println("Bu sınıfın kaynağı: " + (cs != null ? cs.getLocation() : "(bootstrap)"));
12
13        // JDK çekirdek sınıfları modüllerden gelir (jrt: şeması):
14        System.out.println("String sınıfı modülü: " + String.class.getModule());
15
16        // Classpath: JVM sınıfları nerede arar?
17        System.out.println("\nClasspath: " + System.getProperty("java.class.path"));
18
19        // Kaynak (resource) erişimi: paketlenmiş dosyalar ClassLoader üzerinden okunur.
20        // (Gerçek projede: config.properties, şablonlar, görseller JAR içinde paketlenir.)
21        var url = Ornek1.class.getResource("Ornek1.class");
22        System.out.println("\nKendi .class konumu (getResource): " + url);
23
24        System.out.println("""
25
26                --- Paketleme: sınıf yükleme ve kaynaklar ---
27                Derlenen sınıflar ya bir DİZİNDE (geliştirme) ya da bir JAR'da (dağıtım) bulunur.
28                getProtectionDomain().getCodeSource(): bir sınıfın nereden (dizin/jar) yüklendiğini söyler.
29                java.class.path: JVM'in sınıf/kaynak ararken baktığı yollar.
30                Kaynaklar (resource): kod olmayan dosyalar (config, görsel, şablon) JAR'a paketlenir ve
31                  ClassLoader.getResource/getResourceAsStream ile okunur (dosya yolu DEĞİL — classpath'ten).
32                JDK çekirdeği modüllerdedir (jrt:); senin kodun classpath veya module path'ten yüklenir.""");
33    }
34}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Çalışma zamanı sürümü — Multi-Release JAR'ların dayandığı Runtime.version().
2// Çalıştırma: java Ornek2.java
3public class Ornek2 {
4
5    public static void main(String[] args) {
6        // Runtime.version() (Java 9+): çalışan JDK'nın sürümünü yapısal olarak verir.
7        Runtime.Version v = Runtime.version();
8        System.out.println("Çalışan sürüm: " + v);
9        System.out.println("  feature (ana sürüm): " + v.feature());   // 21, 17 ...
10        System.out.println("  interim            : " + v.interim());
11        System.out.println("  update             : " + v.update());
12
13        // Multi-Release JAR mantığı: aynı JAR, FARKLI Java sürümleri için farklı sınıf sunar.
14        // Çalışma anında sürüme göre uygun uygulamayı seçmek (basit simülasyon):
15        int feature = v.feature();
16        String strateji;
17        if (feature >= 21) strateji = "Java 21+ yolu (ör. sanal thread'ler)";
18        else if (feature >= 17) strateji = "Java 17 yolu (sealed/records)";
19        else strateji = "eski yol (uyumluluk)";
20        System.out.println("\nSürüme göre seçilen strateji: " + strateji);
21
22        // Koşullu özellik kullanımı için sürüm karşılaştırma:
23        boolean modern = v.feature() >= 17;
24        System.out.println("Modern (17+) özellikler kullanılabilir mi? " + modern);
25
26        System.out.println("""
27
28                --- Multi-Release JAR ve sürüm tespiti ---
29                Runtime.version(): çalışan JDK sürümünü yapısal verir (feature/interim/update/patch).
30                MULTI-RELEASE JAR (MR-JAR, Java 9+): tek bir JAR içinde, farklı Java sürümleri için
31                  FARKLI derlenmiş sınıflar bulunabilir (META-INF/versions/17/, .../21/ ...).
32                  JVM, çalışırken kendi sürümüne uygun sınıfı OTOMATİK seçer.
33                Faydası: tek artefakt hem eski hem yeni JDK'da en uygun kodu çalıştırır (kütüphaneler için ideal).
34                MANIFEST'te: Multi-Release: true. Üretimi: 'jar --release 21 ...' veya build eklentileri.""");
35    }
36}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.