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ç | Üretir | Kullanım |
|---|---|---|
jar | .jar arşivi | Temel paketleme |
| Fat/Shadow JAR | Bağımlılıklı tek JAR | Sunucu uygulamaları (Spring Boot) |
| Multi-Release JAR | Sürüme duyarlı JAR | Çok-JDK destekli kütüphaneler |
jlink | Özel/küçük çalışma zamanı | Konteyner, minimal dağıtım |
jpackage | Yerel 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ılabilir1// 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}Ornek2
çalıştırılabilir1// 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}