Maven ile Java Proje Yönetimi ve Bağımlılık Kurulumu
Bir Java projesini düzgün yönetmek, bağımlılıkları takip etmek ve build süreçlerini otomatize etmek… Bunların hepsi bir sistem yöneticisi veya geliştirici için ciddi zaman alan işler. Maven tam da bu noktada devreye giriyor ve “her şeyi elle yapmak zorunda değilsin” diyor. Bu yazıda Maven’ı sıfırdan kuracak, temel kavramlarını anlayacak ve gerçek dünya senaryolarında nasıl kullanacağımıza bakacağız.
Maven Nedir ve Neden Kullanmalısınız?
Maven, Apache tarafından geliştirilen bir Java proje yönetim ve build aracıdır. Ama bunu sadece “build aracı” olarak tanımlamak haksızlık olur. Maven aynı zamanda bağımlılık yönetimi, proje yapısı standardizasyonu ve deployment otomasyonu da sağlar.
Düşünün: Bir projeye 5 farklı kütüphane eklemeniz gerekiyor. Her birinin kendi bağımlılıkları var. Bu bağımlılıkların belirli versiyonları birbirleriyle çakışıyor. Maven olmadan bu işi elle yapmak, gün içinde saatlerce sürebilir. Maven ile pom.xml dosyanıza birkaç satır ekleyip mvn install diyorsunuz, gerisini Maven hallediyor.
Maven’in temel avantajları:
- Merkezi bir repository’den otomatik bağımlılık indirme
- Standart proje dizin yapısı
- Build lifecycle yönetimi
- Plugin ekosistemi ile genişletilebilirlik
- CI/CD pipeline’larıyla kolay entegrasyon
Java ve Maven Kurulumu
Java Kurulumu (Temel Gereksinim)
Maven çalışabilmek için Java’ya ihtiyaç duyar. Sisteminizde Java yoksa önce onu kuruyoruz.
Ubuntu/Debian üzerinde:
sudo apt update
sudo apt install openjdk-17-jdk -y
# Kurulumu doğrula
java -version
javac -version
# JAVA_HOME ayarla
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc
echo 'export PATH=$PATH:$JAVA_HOME/bin' >> ~/.bashrc
source ~/.bashrc
CentOS/RHEL/Rocky Linux üzerinde:
sudo dnf install java-17-openjdk-devel -y
# JAVA_HOME bul ve ayarla
alternatives --list | grep java
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk' >> ~/.bashrc
source ~/.bashrc
Maven Kurulumu
Maven kurulumu için iki yol var: paket yöneticisi veya manuel kurulum. Paket yöneticisi hızlı ama genellikle güncel versiyonu getirmez. Özellikle production ortamlarda belirli bir versiyon üzerinde çalışmak istiyorsanız manuel kurulum daha mantıklı.
Paket yöneticisi ile (Hızlı yol):
# Ubuntu/Debian
sudo apt install maven -y
# CentOS/RHEL
sudo dnf install maven -y
# Versiyon kontrolü
mvn -version
Manuel kurulum (Önerilen yol):
# İndir
cd /opt
sudo wget https://downloads.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
# Çıkart
sudo tar -xzf apache-maven-3.9.6-bin.tar.gz
sudo ln -s apache-maven-3.9.6 maven
# Ortam değişkenlerini ayarla
cat >> ~/.bashrc << 'EOF'
export M2_HOME=/opt/maven
export MAVEN_HOME=/opt/maven
export PATH=${M2_HOME}/bin:${PATH}
EOF
source ~/.bashrc
# Doğrula
mvn -version
Çıktı şöyle görünmeli:
Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
Maven home: /opt/maven
Java version: 17.0.9, vendor: Eclipse Adoptium
Maven Proje Yapısı
Maven’in en büyük katkılarından biri standart dizin yapısıdır. Bu yapıyı bilen herhangi bir geliştirici, projenize bakınca nerede ne bulacağını bilir. “Convention over configuration” prensibi burada gerçekten hayat kurtarıyor.
Standart Maven proje yapısı şöyle görünür:
my-project/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/ (kaynak kodlar)
│ │ └── resources/ (config dosyaları, properties)
│ └── test/
│ ├── java/ (test sınıfları)
│ └── resources/ (test kaynakları)
└── target/ (build çıktıları - .gitignore'a ekleyin)
POM.xml – Projenin Kalbi
pom.xml (Project Object Model), Maven projesinin kalbidir. Bu dosyada proje metadata’sı, bağımlılıklar, build konfigürasyonu ve plugin ayarları bulunur.
Temel bir pom.xml şöyle görünür:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Proje kimliği -->
<groupId>com.sirketim</groupId>
<artifactId>uygulama-adi</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Uygulama Adı</name>
<description>Proje açıklaması</description>
<!-- Java versiyonu -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- Bağımlılıklar -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Sadece test için -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Dependency Scope Nedir?
Bağımlılıklarda scope parametresi kritik öneme sahip. Yanlış scope kullanımı, gereksiz JAR’ların production build’ına girmesine yol açar.
- compile: Varsayılan scope. Her aşamada kullanılabilir
- provided: Compile ve test için gerekli, runtime’da container tarafından sağlanır (örn: servlet-api)
- runtime: Compile’da gerekmez, runtime ve test’te kullanılır (örn: JDBC sürücüleri)
- test: Sadece test derleme ve çalıştırma aşamasında kullanılır
- system: Yerel bir JAR’a işaret eder (kaçınılması önerilir)
Yeni Proje Oluşturma
Maven’in Archetype sistemi, proje şablonları oluşturmanızı sağlar. Sıfırdan dizin yapısı oluşturmak yerine bu sistemi kullanmak çok daha verimli.
# Interaktif proje oluşturma
mvn archetype:generate
-DgroupId=com.sirketim
-DartifactId=ilk-proje
-DarchetypeArtifactId=maven-archetype-quickstart
-DarchetypeVersion=1.4
-DinteractiveMode=false
# Oluşturulan yapıyı incele
cd ilk-proje
find . -type f | head -20
Web projesi için:
mvn archetype:generate
-DgroupId=com.sirketim
-DartifactId=web-uygulamasi
-DarchetypeArtifactId=maven-archetype-webapp
-DinteractiveMode=false
Maven Build Lifecycle
Maven’in build lifecycle’ı belirli fazlardan oluşur ve sıralı çalışır. Bir fazı çalıştırdığınızda, o fazdan önceki tüm fazlar da otomatik çalışır.
Temel fazlar şunlar:
- validate: Projenin doğru yapılandırıldığını kontrol eder
- compile: Kaynak kodu derler
- test: Unit testleri çalıştırır
- package: Kodu JAR/WAR/EAR olarak paketler
- verify: Entegrasyon testlerini çalıştırır
- install: Paketi yerel repository’e yükler
- deploy: Paketi uzak repository’e yükler
Günlük kullandığınız komutlar:
# Sadece derle
mvn compile
# Derle ve test et
mvn test
# Paketleme (JAR/WAR oluştur)
mvn package
# Testleri atlayarak paketleme (CI'da acil durum için)
mvn package -DskipTests
# Temizle ve yeniden paketle
mvn clean package
# Yerel Maven repository'e yükle
mvn clean install
# Uzak repository'e deploy et
mvn clean deploy
Gerçek Dünya Senaryosu: Microservice Projesi
Diyelim ki bir microservice projesi kuracaksınız. Spring Boot, veritabanı bağlantısı ve API dokümantasyonu gerekiyor. İşte böyle bir pom.xml nasıl görünür:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<groupId>com.sirketim</groupId>
<artifactId>kullanici-servisi</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<dependencies>
<!-- Web katmanı -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Veritabanı -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Validasyon -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Lombok - boilerplate azaltma -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Bağımlılık Çakışmalarını Çözmek
Gerçek projelerde en sık karşılaşılan sorunlardan biri bağımlılık çakışmasıdır. A kütüphanesi X’in 1.0 versiyonunu, B kütüphanesi X’in 2.0 versiyonunu isteyebilir. Maven bunu çözmek için “nearest definition” kuralını kullanır ama bazen elle müdahale gerekir.
# Bağımlılık ağacını görüntüle
mvn dependency:tree
# Belirli bir artifact'ı filtrele
mvn dependency:tree -Dincludes=com.fasterxml.jackson.core
# Çakışmaları analiz et
mvn dependency:analyze
Çakışmayı çözmek için exclusion kullanırsınız:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Logback'i dışla, Log4j2 kullanacağız -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Maven Profilleri ile Ortam Yönetimi
Farklı ortamlar için (dev, staging, prod) farklı konfigürasyonlar gerekebilir. Maven profilleri tam bu iş için var.
<profiles>
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<db.url>jdbc:postgresql://localhost:5432/devdb</db.url>
<log.level>DEBUG</log.level>
</properties>
</profile>
<profile>
<id>production</id>
<properties>
<db.url>jdbc:postgresql://prod-server:5432/proddb</db.url>
<log.level>WARN</log.level>
</properties>
</profile>
</profiles>
Profil ile build almak:
# Development profili (zaten default)
mvn clean package
# Production profili
mvn clean package -Pproduction
# Aktif profilleri listele
mvn help:active-profiles
Yerel Repository ve Nexus Entegrasyonu
Maven bağımlılıkları varsayılan olarak ~/.m2/repository dizinine indirir. Kurumsal ortamlarda ise genellikle Nexus veya Artifactory gibi bir özel repository kullanılır.
# Yerel repository boyutunu kontrol et
du -sh ~/.m2/repository
# Bozuk veya eksik artifact'ları temizle
find ~/.m2/repository -name "*.lastUpdated" -delete
# Tüm yerel cache'i temizle (dikkatli kullanın!)
rm -rf ~/.m2/repository
Kurumsal Nexus repository için ~/.m2/settings.xml konfigürasyonu:
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus.sirketim.com:8081/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>nexus</id>
<username>deploy-user</username>
<password>gizli-sifre</password>
</server>
</servers>
</settings>
CI/CD Pipeline’larında Maven
Jenkins veya GitLab CI’da Maven kullanırken dikkat etmeniz gereken bazı noktalar var. Özellikle build sürelerini kısaltmak için ~/.m2 dizinini cache’lemek önemli.
GitLab CI örneği:
# .gitlab-ci.yml
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end"
cache:
paths:
- .m2/repository
stages:
- build
- test
- package
build:
stage: build
image: maven:3.9-openjdk-17
script:
- mvn $MAVEN_CLI_OPTS compile
test:
stage: test
image: maven:3.9-openjdk-17
script:
- mvn $MAVEN_CLI_OPTS test
package:
stage: package
image: maven:3.9-openjdk-17
script:
- mvn $MAVEN_CLI_OPTS clean package -DskipTests
artifacts:
paths:
- target/*.jar
expire_in: 1 week
only:
- main
- tags
Sık Karşılaşılan Hatalar ve Çözümleri
“Could not resolve dependencies” hatası:
# Offline moddan çık
mvn clean install -U
# Force update ile tüm snapshot'ları güncelle
mvn clean install -U -Dsnapshot.updatePolicy=always
“OutOfMemoryError” build sırasında:
# Maven JVM bellek ayarları
export MAVEN_OPTS="-Xmx2048m -Xms512m"
# Veya .mvn/jvm.config dosyası oluştur
mkdir -p .mvn
echo "-Xmx2048m -Xms512m" > .mvn/jvm.config
Build süresini hızlandırma:
# Paralel build (dikkatli kullanın - tüm projeler desteklemez)
mvn clean install -T 4
# Thread sayısını CPU core sayısına göre ayarla
mvn clean install -T 1C
# Incremental build (değişen modülleri derle)
mvn clean install --also-make-dependents
Maven Wrapper Kullanımı
Ekip içinde herkesin aynı Maven versiyonunu kullanmasını garantilemek için Maven Wrapper harika bir çözüm. Projeyi klonlayan biri Maven kurmak zorunda kalmaz.
# Wrapper oluştur
mvn wrapper:wrapper
# Wrapper ile build
./mvnw clean package # Linux/Mac
mvnw.cmd clean package # Windows
# Belirli Maven versiyonu için
mvn wrapper:wrapper -Dmaven=3.9.6
Bu şekilde .mvn/wrapper/maven-wrapper.properties dosyası oluşur ve tüm ekip aynı versiyonu kullanır. Bu dosyayı Git’e eklemeyi unutmayın.
Sonuç
Maven, Java ekosisteminde bağımlılık yönetimi ve build otomasyonunun temel taşıdır. Bu yazıda kurulumdan gerçek dünya senaryolarına kadar kapsamlı bir Maven turunu tamamladık.
Şu ana kadar şunları öğrendik: Java ve Maven kurulumunu nasıl yapacağımızı, standart proje yapısını ve pom.xml anatomisini, bağımlılık yönetimini ve scope kavramını, build lifecycle fazlarını ve nasıl kullanacağımızı, bağımlılık çakışmalarını nasıl tespit edip çözeceğimizi, profiller ile ortam yönetimini ve CI/CD entegrasyonunu.
Bir sonraki adım olarak Gradle’a göz atmanızı öneririm. Gradle, Maven’ın bazı sınırlılıklarını Groovy veya Kotlin DSL kullanarak çözer. Ancak Maven hala kurumsal Java dünyasında en yaygın kullanılan araç ve bu bilgileri taşıyacağınız her Java projesinde işinize yarayacak.
Son olarak şunu söyleyeyim: mvn dependency:tree komutunu sık kullanın. Projenize ne girdiğini bilmek, güvenlik açıklarını erken tespit etmek ve gereksiz şişkinliği önlemek açısından kritik önem taşıyor. Özellikle production’a gidecek JAR boyutlarını takip edin, küçük bir alışkanlık büyük sorunları önler.
