Eğitim Portalı/Java/Build Araçları ve Bağımlılık Yönetimi (Maven / Gradle)
Java01-java/18-build-araclari-ve-bagimlilik

Build Araçları ve Bağımlılık Yönetimi (Maven / Gradle)

Tek dosyalık örneklerden gerçek projelere geçtiğinde yeni bir ihtiyaç doğar: onlarca kaynak dosyayı derlemek, başkalarının yazdığı kütüphaneleri (bağımlılıkları) indirip projeye eklemek, testleri çalıştırmak ve sonund…

Build Araçları ve Bağımlılık Yönetimi (Maven / Gradle)

Tek dosyalık örneklerden gerçek projelere geçtiğinde yeni bir ihtiyaç doğar: onlarca kaynak dosyayı derlemek, başkalarının yazdığı kütüphaneleri (bağımlılıkları) indirip projeye eklemek, testleri çalıştırmak ve sonunda dağıtılabilir bir paket (jar) üretmek. Bunları elle yapmak imkânsıza yakındır. İşte build araçları — Java dünyasında Maven ve Gradle — bu işi otomatikleştirir.

Neden build aracı?

Diyelim ki projende JSON işlemek için bir kütüphane kullanmak istiyorsun. Build aracı olmadan: jar'ı bul, indir, doğru klasöre koy, classpath'e ekle, onun da bağımlılıklarını bul... Build aracıyla ise tek bir satır yazarsın; gerisini o halleder:

  • Bağımlılık yönetimi: Kütüphaneleri (ve onların bağımlılıklarını) otomatik indirir.
  • Derleme ve test: Kaynakları derler, testleri çalıştırır.
  • Paketleme: Çalıştırılabilir jar/war üretir.
  • Yaşam döngüsü: Tüm bu adımları standart, tekrarlanabilir hale getirir.

Kütüphaneler merkezi bir depodan (genelde Maven Central) çekilir ve groupId:artifactId:version koordinatıyla tanımlanır (örn. com.google.code.gson:gson:2.11.0).

Maven

Maven, XML tabanlı pom.xml dosyasıyla yapılandırılır ve katı bir yaşam döngüsü (compile → test → package → install) izler:

<project>
  <groupId>com.egitim</groupId>
  <artifactId>uygulamam</artifactId>
  <version>1.0.0</version>
  <dependencies>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.11.0</version>
    </dependency>
  </dependencies>
</project>

Sık komutlar: mvn compile, mvn test, mvn package.

Gradle

Gradle daha esnek ve kısadır; Groovy veya Kotlin DSL kullanır. Bu eğitim deposundaki Spring örnekleri de Gradle ile çalışır:

plugins { id 'java' }
repositories { mavenCentral() }
dependencies {
    implementation 'com.google.code.gson:gson:2.11.0'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

Sık komutlar: gradle build, gradle test, gradle run. Gradle artımlı (incremental) derleme ve build cache ile büyük projelerde hız avantajı sunar.

Semantic Versioning (SemVer)

Bağımlılıkların sürümleri rastgele değildir; çoğu kütüphane MAJOR.MINOR.PATCH kuralını izler:

  • MAJOR: Geriye dönük uyumluluğu kıran değişiklik (örn. 1.x2.0).
  • MINOR: Geriye uyumlu yeni özellik (1.21.3).
  • PATCH: Geriye uyumlu hata düzeltmesi (1.2.01.2.1).

Bu kural, build aracının "hangi sürüm güvenle yükseltilebilir?" sorusunu yanıtlamasını sağlar. Örnek 1 (./Ornek1.java) SemVer karşılaştırmasını ve ^1.2.0 türü uyumluluk kuralını modelleyen küçük bir program içerir.

Modüller, bağımlılıklar ve bağlama

Bir bağımlılık, aslında "başkasının yazdığı, yeniden kullandığın bir modül"dür. Örnek 2 (./Ornek2.java) bunu bir adım küçükte gösterir: kendi MetinAraclari yardımcı sınıfını bir "kütüphane" gibi yazıp kullanır. Gerçek projede bu sınıf ayrı bir jar olur ve build aracı onu projene eklerdi; mantık aynıdır — hazır, test edilmiş kodu yeniden kullanmak.

Projeyi oluştururken modülleri birbirine bağlamak (wiring) gerekir. Örnek 3 (./Ornek3.java) elle bağımlılık enjeksiyonunu gösterir: üst seviye bir servis, alt seviye modüllere somut sınıflar yerine arayüzler üzerinden bağlanır ve bağımlılıklarını constructor'dan alır. Bu "gevşek bağlılık" fikri, az sonra Spring'de IoC container'ın otomatikleştirdiği şeyin ta kendisidir.

Özet

Build araçlarının neden vazgeçilmez olduğunu; Maven (pom.xml) ve Gradle (build.gradle) yaklaşımlarını; bağımlılık koordinatlarını, Maven Central'ı ve Semantic Versioning'i öğrendik. Modülleri/bağımlılıkları arayüzler üzerinden bağlamanın gevşek bağlı, sürdürülebilir tasarımın anahtarı olduğunu gördük. Sırada, tecrübeyle damıtılmış çözüm kalıpları: tasarım desenleri.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Semantic Versioning (SemVer) mantığını modelleyen küçük bir program.
2// Build araçları bağımlılıkları "1.2.3" gibi sürümlerle yönetir; bu örnek o mantığı gösterir.
3// Çalıştırma: java Ornek1.java
4public class Ornek1 {
5
6    // MAJOR.MINOR.PATCH — anlamsal sürümleme.
7    record Surum(int major, int minor, int patch) implements Comparable<Surum> {
8        static Surum ayristir(String s) {
9            String[] p = s.split("\\.");
10            return new Surum(Integer.parseInt(p[0]), Integer.parseInt(p[1]), Integer.parseInt(p[2]));
11        }
12
13        @Override public int compareTo(Surum o) {
14            if (major != o.major) return Integer.compare(major, o.major);
15            if (minor != o.minor) return Integer.compare(minor, o.minor);
16            return Integer.compare(patch, o.patch);
17        }
18
19        // "^1.2.3" kuralı: aynı MAJOR içindeki, >= bu sürüm olanlar uyumludur.
20        boolean uyumlu(Surum diger) {
21            return diger.major == this.major && diger.compareTo(this) >= 0;
22        }
23
24        @Override public String toString() { return major + "." + minor + "." + patch; }
25    }
26
27    public static void main(String[] args) {
28        Surum istenen = Surum.ayristir("1.2.0");
29        System.out.println("İstenen sürüm (^" + istenen + "):\n");
30
31        String[] adaylar = {"1.2.0", "1.4.5", "1.9.9", "2.0.0", "1.1.0"};
32        for (String a : adaylar) {
33            Surum s = Surum.ayristir(a);
34            System.out.printf("  %-7s -> %s%n", s, istenen.uyumlu(s) ? "uyumlu" : "UYUMSUZ");
35        }
36
37        System.out.println("\nKural: MAJOR değişince geriye dönük uyumluluk KIRILIR.");
38        System.out.println("Bu yüzden build araçları sürüm aralıklarını dikkatle çözer.");
39    }
40}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Bir "kütüphane" (yeniden kullanılabilir modül) ve onu kullanan kod.
2// Build araçlarının asıl işi, bunun gibi modülleri/bağımlılıkları bir araya getirmektir.
3// Burada bağımlılığı tek dosyada simüle ediyoruz.
4// Çalıştırma: java Ornek2.java
5public class Ornek2 {
6
7    public static void main(String[] args) {
8        // Gerçek projede MetinAraclari ayrı bir kütüphane (jar) olurdu ve build aracı
9        // (Maven/Gradle) onu projeye eklerdi. Mantık aynı: hazır kodu yeniden kullanmak.
10        System.out.println("kapitalize : " + MetinAraclari.kapitalize("merhaba dünya"));
11        System.out.println("kisalt     : " + MetinAraclari.kisalt("Bu oldukça uzun bir metindir", 10));
12        System.out.println("sluglastir : " + MetinAraclari.sluglastir("Java & Spring Eğitimi"));
13        System.out.println("tekrar     : " + MetinAraclari.tekrar("ab", 3));
14    }
15}
16
17// Yeniden kullanılabilir yardımcı "kütüphane".
18class MetinAraclari {
19    private MetinAraclari() {} // util sınıfı: nesne oluşturulmaz
20
21    static String kapitalize(String s) {
22        if (s == null || s.isEmpty()) return s;
23        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
24    }
25
26    static String kisalt(String s, int maks) {
27        return s.length() <= maks ? s : s.substring(0, maks) + "...";
28    }
29
30    static String sluglastir(String s) {
31        return s.toLowerCase()
32                .replaceAll("[^a-z0-9\\s-]", "")
33                .trim()
34                .replaceAll("\\s+", "-");
35    }
36
37    static String tekrar(String s, int kez) {
38        return s.repeat(Math.max(0, kez));
39    }
40}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Elle bağımlılık enjeksiyonu (manual dependency injection).
2// Modülleri birbirine bağlamak (wiring) build/uygulama kurulumunun kalbidir.
3// Spring bunu otomatik yapar; burada elle yaparak fikri görüyoruz.
4// Çalıştırma: java Ornek3.java
5public class Ornek3 {
6
7    public static void main(String[] args) {
8        // "Composition root": bağımlılıkları burada kurar ve birbirine bağlarız.
9        // Üst seviye (SiparisServisi) alt seviyelere ARAYÜZ üzerinden bağlıdır;
10        // somut sınıfları bilmez. Bu, gevşek bağlılık (loose coupling) demektir.
11        MesajGonderici eposta = new EpostaGonderici();
12        Depo depo = new BellekDepo();
13
14        SiparisServisi servis = new SiparisServisi(depo, eposta);
15        servis.siparisOlustur("Klavye");
16        servis.siparisOlustur("Mouse");
17
18        System.out.println("\nToplam sipariş: " + depo.adet());
19        System.out.println("\nNot: Spring'de bu bağlama işini @Autowired / IoC container yapar.");
20    }
21}
22
23// Soyutlamalar (sözleşmeler)
24interface MesajGonderici { void gonder(String mesaj); }
25interface Depo { void kaydet(String kayit); int adet(); }
26
27// Somut uygulamalar
28class EpostaGonderici implements MesajGonderici {
29    public void gonder(String mesaj) { System.out.println("  [e-posta] " + mesaj); }
30}
31class BellekDepo implements Depo {
32    private int sayac = 0;
33    public void kaydet(String kayit) { sayac++; System.out.println("  [depo] kaydedildi: " + kayit); }
34    public int adet() { return sayac; }
35}
36
37// Üst seviye servis: bağımlılıkları CONSTRUCTOR ile dışarıdan alır (injection).
38class SiparisServisi {
39    private final Depo depo;
40    private final MesajGonderici gonderici;
41
42    SiparisServisi(Depo depo, MesajGonderici gonderici) {
43        this.depo = depo;
44        this.gonderici = gonderici;
45    }
46
47    void siparisOlustur(String urun) {
48        depo.kaydet(urun);
49        gonderici.gonder("Siparişiniz alındı: " + urun);
50    }
51}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.