Spring Boot 3.4 Virtual Threads: Mülakat Soruları ve Performans Karşılaştırmaları
Java 21 Virtual Threads'i Spring Boot 3.4 ile öğrenin: 15 mülakat sorusu, performans karşılaştırmaları ve teknik mülakatlarda fark yaratacak göç desenleri.

Virtual Threads, Java 21'in en önemli yeniliklerinden biridir ve Spring Boot 3.4 onları yerel olarak destekler. Project Loom'un bu özelliği, backend uygulamalarında eşzamanlılık yönetimini köklü biçimde değiştirir. Teknik mülakatlar artık iç mekanizmaların, uygun kullanım senaryolarının ve sık rastlanan tuzakların kavranmasını ölçer.
Mülakatçılar Virtual Threads'i gerçekten anlayan adayları, onları körlemesine kullananlardan ayırır. Ne zaman KULLANILMAMASI gerektiğini bilmek, faydalarını bilmek kadar önemlidir.
Virtual Threads Temelleri
Soru 1: Virtual Thread nedir ve Platform Thread'den nasıl farklıdır?
Virtual Thread, işletim sistemi yerine JVM tarafından yönetilen hafif bir thread'dir. Platform Threads'in (klasik thread'ler) aksine Virtual Threads doğrudan OS thread'lerine eşlenmez. JVM, bunlardan milyonlarcasını çok düşük bellek kullanımıyla oluşturabilir.
// Comparing thread creation approaches
public class VirtualThreadDemo {
public void demonstrateDifference() {
// Platform Thread: ~1MB stack per thread
// Practical limit: a few thousand on a standard JVM
Thread platformThread = new Thread(() -> {
performBlockingOperation();
});
// Virtual Thread: ~a few KB per thread
// Can create millions without issues
Thread virtualThread = Thread.ofVirtual().start(() -> {
performBlockingOperation();
});
}
// A Virtual Thread "mounts" onto a Platform Thread (carrier)
// During I/O blocking, it releases the carrier for other Virtual Threads
private void performBlockingOperation() {
try {
Thread.sleep(1000); // Virtual Thread detaches from carrier here
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}Temel fark, bloklanma sırasındaki davranıştadır. Platform Thread bloklandığında (I/O, sleep) OS thread'ine bağlı kalır. Virtual Thread ise taşıyıcı thread'den "unmount" olur ve taşıyıcının başka Virtual Threads tarafından kullanılmasına izin verir.
Soru 2: Spring Boot 3.4'te Virtual Threads nasıl etkinleştirilir?
Spring Boot 3.4, Virtual Threads etkinleştirmesini tek bir konfigürasyon özelliğine indirgemiştir. Tüm framework otomatik olarak adapte olur: Tomcat, REST controller'ları ve bloklayıcı çağrılar bu optimizasyondan anında yararlanır.
# application.yml
# Global Virtual Threads activation
spring:
threads:
virtual:
enabled: true
# Optional Tomcat pool configuration
server:
tomcat:
threads:
max: 200 # Less critical with Virtual Threads
min-spare: 10// Programmatic activation if needed
@Configuration
public class WebConfig {
@Bean
public TomcatProtocolHandlerCustomizer<?> virtualThreadCustomizer() {
// Each HTTP request runs on a Virtual Thread
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}Etkinleştirme Tomcat'in davranışını değiştirir: sabit bir thread havuzu yerine her istek kendi Virtual Thread'ini alır. Bu yaklaşım, klasik thread havuzu darboğazını ortadan kaldırır.
Soru 3: "Mounting" ve "unmounting" kavramlarını açıklayın
Mounting, bir Virtual Thread'in taşıyıcı thread'e (Platform Thread) bağlanmasını ifade eder. Unmounting ise bloklayıcı işlemler sırasında gerçekleşir ve taşıyıcıyı diğer Virtual Threads için serbest bırakır. Bu mekanizma CPU kaynaklarının optimum kullanımını sağlar.
// Illustrating the mounting/unmounting cycle
public class MountingDemo {
public void demonstrateMounting() {
Thread.ofVirtual().start(() -> {
// MOUNTED: Virtual Thread is using a carrier thread
System.out.println("Carrier: " + getCurrentCarrier());
// UNMOUNTING: releases carrier during blocking
performDatabaseQuery(); // Blocking JDBC call
// REMOUNTED: may be on a different carrier
System.out.println("New carrier: " + getCurrentCarrier());
});
}
// Blocking operations trigger unmounting automatically
private void performDatabaseQuery() {
// JDBC connection, file read, network call...
// Virtual Thread detaches during I/O wait
}
private String getCurrentCarrier() {
// Gets the current carrier thread name
return Thread.currentThread().toString();
}
}Bu mekanizma geliştirici için saydamdır. Kod klasik imperatif tarzda yazılır, ancak JVM taşıyıcı kullanımını otomatik olarak optimize eder. Birkaç taşıyıcıdan oluşan bir havuz milyonlarca Virtual Thread'e hizmet verebilir.
Taşıyıcı thread sayısı varsayılan olarak CPU çekirdek sayısına eşittir. JVM bu havuzu ForkJoinPool aracılığıyla dinamik biçimde ayarlar.
Kullanım Senaryoları ve Anti-Patternler
Soru 4: Virtual Threads ne zaman performans kazancı sağlar?
Virtual Threads, I/O ağırlıklı yüklerde parlar: harici REST çağrıları, veritabanı sorguları, dosya okumaları. Bu işlemler zamanın büyük kısmını beklemekle geçirir; bu sırada Virtual Thread taşıyıcısını serbest bırakır.
// Ideal case for Virtual Threads
@Service
public class IOBoundService {
private final RestClient restClient;
private final UserRepository userRepository;
// Each call involves network and database waiting
// Virtual Threads shine here
public UserProfile enrichUserProfile(Long userId) {
// DB call - VT detaches during SQL query
User user = userRepository.findById(userId).orElseThrow();
// External REST call - VT detaches during HTTP wait
ExternalData externalData = restClient
.get()
.uri("/api/external/{id}", userId)
.retrieve()
.body(ExternalData.class);
// Data aggregation
return new UserProfile(user, externalData);
}
}Kazanç çok daha fazla eşzamanlı isteğin işlenmesinden gelir. 200 Platform Thread ve 100ms süren isteklerle maksimum throughput 2.000 req/s'dir. Virtual Threads ile aynı makinede 50.000+ req/s mümkündür.
Soru 5: Virtual Threads ile hangi anti-patternlerden kaçınmak gerekir?
Virtual Threads, CPU ağırlıklı yükler ve "pinning"e neden olan durumlar için uygun değildir. Pinning, bir Virtual Thread'in bloklanmaya rağmen taşıyıcısına bağlı kalmasıyla ortaya çıkar ve sanallaştırmanın faydalarını ortadan kaldırır.
// Examples of cases to avoid
@Service
public class AntiPatterns {
// ANTI-PATTERN 1: CPU-intensive computation
// Virtual Threads provide no benefit here
public BigInteger computeFactorial(int n) {
// 100% CPU, no I/O, no unmounting possible
BigInteger result = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
result = result.multiply(BigInteger.valueOf(i));
}
return result; // Carrier is monopolized throughout
}
// ANTI-PATTERN 2: Synchronized causes pinning
private final Object lock = new Object();
public void pinnedOperation() {
synchronized (lock) { // PINNING: VT stays on carrier
performDatabaseQuery(); // Unmounting doesn't happen!
}
}
// SOLUTION: Use ReentrantLock
private final ReentrantLock reentrantLock = new ReentrantLock();
public void unpinnedOperation() {
reentrantLock.lock();
try {
performDatabaseQuery(); // Unmounting possible
} finally {
reentrantLock.unlock();
}
}
}Pinning, kaynak açısından Virtual Thread'i Platform Thread'e dönüştürür. Başlıca nedenler synchronized blokları ve yerel JNI çağrılarıdır. ReentrantLock'a geçiş ilk durumu çözer.
Soru 6: Bir uygulamada pinning nasıl tespit edilir?
JVM, pinning vakalarını tespit etmek için tanılama seçenekleri sunar. Bu bilgi, Virtual Threads'e geçişte kritik öneme sahiptir; çünkü pinning performansı iyileştirmek yerine bozabilir.
// Configuration and pinning detection
public class PinningDiagnostics {
// JVM option to log pinning
// -Djdk.tracePinnedThreads=full (detailed)
// -Djdk.tracePinnedThreads=short (summary)
// Example code causing pinning
public void demonstratePinning() {
Thread.ofVirtual().start(() -> {
synchronized (this) {
// This log will appear with tracePinnedThreads enabled
try {
Thread.sleep(100); // Pinned during sleep!
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
}
}# Typical output with -Djdk.tracePinnedThreads=full
Thread[#23,VirtualThread[#1]/runnable@ForkJoinPool-1-worker-1,5,CarrierThreads]
java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:183)
java.base/java.lang.VirtualThread.parkOnCarrierThread(VirtualThread.java:661)
com.example.PinningDiagnostics.demonstratePinning(PinningDiagnostics.java:15)Pinning loglarının analizi düzeltilmesi gereken sıcak noktaları ortaya çıkarır. Sık pinning yaşayan bir uygulama Virtual Threads'i tam kullanamaz ve Platform Threads'ten bile yavaş olabilir.
Spring Boot mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Performans Karşılaştırmaları
Soru 7: Virtual Threads'ten ne kadar performans kazancı beklenebilir?
Karşılaştırmalar, tipik I/O ağırlıklı uygulamalarda kayda değer iyileşmeler gösterir. Kazançlar I/O süresi / CPU süresi oranına ve gerekli eşzamanlılık seviyesine bağlıdır.
// Endpoint for measuring performance
@RestController
@RequestMapping("/api/benchmark")
public class BenchmarkController {
private final ExternalApiClient apiClient;
// Simulation of a typical endpoint with external calls
@GetMapping("/user/{id}")
public ResponseEntity<UserData> getUser(@PathVariable Long id) {
// Three sequential I/O calls
UserInfo info = apiClient.fetchUserInfo(id); // ~50ms
List<Order> orders = apiClient.fetchOrders(id); // ~80ms
CreditScore score = apiClient.fetchCreditScore(id); // ~100ms
return ResponseEntity.ok(new UserData(info, orders, score));
}
}# Benchmark results - 10,000 concurrent requests
# Configuration: 8 cores, 16GB RAM, simulated latency 230ms/request
Platform Threads (pool 200):
- Throughput: 850 req/s
- P99 latency: 1250ms
- Heap memory: 2.1GB
Virtual Threads:
- Throughput: 4200 req/s
- P99 latency: 280ms
- Heap memory: 850MB
Gain: 5x throughput, 4.5x P99 latency reductionVirtual Threads ayrıca bellek kullanımını da düşürür; her thread Platform Thread'in yaklaşık 1MB'sı yerine yalnızca birkaç KB ayırır.
Soru 8: Virtual Threads ile bağlantı havuzları nasıl yapılandırılır?
Bağlantı havuzları (HikariCP, Lettuce) Virtual Threads ile birlikte yeni darboğaz haline gelir. 10 bağlantılı bir havuz, milyonlarca Virtual Thread olsa bile aynı anda 10 DB sorgusuyla sınırlıdır.
# application.yml
# HikariCP configuration optimized for Virtual Threads
spring:
datasource:
hikari:
# More connections since Virtual Threads allow more concurrency
maximum-pool-size: 50
minimum-idle: 10
# Shorter timeout due to more concurrent requests
connection-timeout: 5000
# Fast validation
validation-timeout: 3000
# Redis with Lettuce - already async-friendly
data:
redis:
lettuce:
pool:
max-active: 50
max-idle: 20// Pool monitoring to avoid contention
@Component
public class ConnectionPoolMonitor {
private final HikariDataSource dataSource;
@Scheduled(fixedRate = 10000)
public void logPoolStats() {
HikariPoolMXBean pool = dataSource.getHikariPoolMXBean();
log.info("Pool stats - Active: {}, Idle: {}, Waiting: {}",
pool.getActiveConnections(),
pool.getIdleConnections(),
pool.getThreadsAwaitingConnection());
// Alert if too many threads waiting
if (pool.getThreadsAwaitingConnection() > 100) {
log.warn("Connection pool contention detected!");
}
}
}Havuz boyutu Virtual Thread sayısına değil veritabanı kapasitesine bağlıdır. Standart bir PostgreSQL yaklaşık 100-200 aktif bağlantıyı destekler.
Soru 9: Virtual Threads etkisi Micrometer ile nasıl ölçülür?
Micrometer ve Spring Boot Actuator, Virtual Threads'in etkinliğini değerlendirmek için temel metrikler sağlar. Bu metrikler kazançları doğrular ve potansiyel sorunları açığa çıkarır.
// Custom metrics for Virtual Threads
@Component
public class VirtualThreadMetrics {
private final MeterRegistry registry;
private final AtomicLong activeVirtualThreads = new AtomicLong(0);
@PostConstruct
public void registerMetrics() {
// Active Virtual Threads counter
Gauge.builder("virtual.threads.active", activeVirtualThreads, AtomicLong::get)
.description("Number of active virtual threads")
.register(registry);
// JVM metrics for carriers
Gauge.builder("virtual.threads.carriers", this::getCarrierCount)
.description("Number of carrier threads")
.register(registry);
}
private double getCarrierCount() {
// Gets the ForkJoinPool carrier count
return ForkJoinPool.commonPool().getPoolSize();
}
// Interceptor to trace requests
public void trackVirtualThread(Runnable task) {
activeVirtualThreads.incrementAndGet();
try {
task.run();
} finally {
activeVirtualThreads.decrementAndGet();
}
}
}# application.yml
# Metrics exposure
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}Metriklerin analizi Virtual Threads / Carriers oranını ortaya koyar ve bağlantı havuzundaki çekişme dönemlerini saptar.
Geçiş ve Uyumluluk
Soru 10: Hangi kütüphaneler Virtual Threads ile uyumludur?
Uyumluluk, synchronized bloklarının ve yerel çağrıların kullanımına bağlıdır. Spring ekosistemi büyük ölçüde uyumludur, ancak bazı kütüphaneler belirli sürümler gerektirir.
// Checking dependency compatibility
@Configuration
public class CompatibilityCheck {
// Compatible libraries (recommended versions)
// - Spring Boot 3.2+ (native support)
// - HikariCP 5.1+ (ReentrantLock instead of synchronized)
// - Lettuce 6.3+ (non-blocking I/O)
// - Jackson 2.16+ (no synchronized)
// Libraries requiring attention
// - JDBC drivers: check version
// - Some legacy HTTP clients
@Bean
public CommandLineRunner checkCompatibility() {
return args -> {
// Log versions for audit
log.info("Java version: {}", System.getProperty("java.version"));
log.info("Virtual threads available: {}",
Thread.ofVirtual() != null);
// Support verification
if (Runtime.version().feature() < 21) {
throw new IllegalStateException(
"Java 21+ required for Virtual Threads");
}
};
}
}Mevcut çoğu modern framework ReentrantLock'a geçmiştir. Eski bağımlılıklar için -Djdk.tracePinnedThreads=short ile yapılan yük testi pinning sorunlarını ortaya çıkarır.
Soru 11: Virtual Threads'e nasıl aşamalı olarak geçilir?
Aşamalı geçiş, üretimi riske atmadan kazançları doğrulamayı ve sorunları tespit etmeyi sağlar. Önerilen strateji, Virtual Threads endpoint'lerini geleneksel olanlardan ayırır.
// Configuration for progressive migration
@Configuration
public class DualExecutorConfig {
// Traditional executor for legacy endpoints
@Bean("platformExecutor")
public ExecutorService platformExecutor() {
return Executors.newFixedThreadPool(200);
}
// Virtual Threads executor for new endpoints
@Bean("virtualExecutor")
public ExecutorService virtualExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}
@RestController
@RequestMapping("/api/v2")
public class MigratedController {
@Qualifier("virtualExecutor")
private final ExecutorService executor;
// Endpoint migrated to Virtual Threads
@GetMapping("/users/{id}")
public CompletableFuture<User> getUser(@PathVariable Long id) {
return CompletableFuture.supplyAsync(() -> {
// Business code unchanged
return userService.findById(id);
}, executor);
}
}# application.yml
# Feature flag for migration
features:
virtual-threads:
enabled: true
endpoints:
- /api/v2/**
- /api/reports/**
# Disable for quick rollback
# features.virtual-threads.enabled: falseTüm bağımlılıkları test etmeden Virtual Threads'i global olarak etkinleştirmemek gerekir. Pinning performansı önemli ölçüde düşürebilir.
Soru 12: Üretime almadan önce hangi testler yapılmalıdır?
Virtual Threads doğrulaması yük testleri, pinning testleri ve uyumluluk testleri gerektirir. Bu testlerin gerçekçi üretim koşullarını taklit etmesi şarttır.
// Load test with JUnit and Virtual Threads
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class VirtualThreadLoadTest {
@LocalServerPort
private int port;
@Test
void shouldHandleHighConcurrency() throws Exception {
int concurrentRequests = 5000;
CountDownLatch latch = new CountDownLatch(concurrentRequests);
AtomicInteger successCount = new AtomicInteger(0);
AtomicInteger errorCount = new AtomicInteger(0);
HttpClient client = HttpClient.newHttpClient();
// Launch 5000 simultaneous requests
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < concurrentRequests; i++) {
executor.submit(() -> {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:" + port + "/api/test"))
.build();
HttpResponse<String> response = client.send(
request, BodyHandlers.ofString());
if (response.statusCode() == 200) {
successCount.incrementAndGet();
} else {
errorCount.incrementAndGet();
}
} catch (Exception e) {
errorCount.incrementAndGet();
} finally {
latch.countDown();
}
});
}
}
latch.await(60, TimeUnit.SECONDS);
// Performance assertions
assertThat(successCount.get()).isGreaterThan(4900); // >98% success
assertThat(errorCount.get()).isLessThan(100);
}
}Testler çekişme senaryolarını (doymuş bağlantı havuzu), zaman aşımlarını ve dakikalarca süren sürdürülmüş yükü kapsamalıdır.
İleri Seviye Sorular
Soru 13: Virtual Threads ile Structured Concurrency nasıl etkileşir?
Structured Concurrency (JEP 453) eşzamanlı görevlerin aynı yaşam döngüsünü paylaşmasını sağlayarak Virtual Threads'i tamamlar. Bu yaklaşım hata yönetimini ve iptal işlemlerini kolaylaştırır.
// Combining Virtual Threads + Structured Concurrency
public class StructuredConcurrencyExample {
public UserDashboard fetchDashboard(Long userId) throws Exception {
// StructuredTaskScope ensures all tasks complete together
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// Three parallel tasks on Virtual Threads
Subtask<UserProfile> profileTask = scope.fork(() ->
userService.getProfile(userId));
Subtask<List<Notification>> notifTask = scope.fork(() ->
notificationService.getRecent(userId));
Subtask<AccountBalance> balanceTask = scope.fork(() ->
accountService.getBalance(userId));
// Wait for all tasks or fail on first error
scope.join();
scope.throwIfFailed();
// All tasks succeeded - safe aggregation
return new UserDashboard(
profileTask.get(),
notifTask.get(),
balanceTask.get()
);
}
// If one task fails, others are automatically cancelled
}
}Structured Concurrency thread sızıntılarını engeller ve hata ayıklamayı kolaylaştırır; çünkü çağrı yığını kodun mantıksal yapısını yansıtır.
Soru 14: Virtual Threads ile reaktif programlama arasındaki fark nedir?
Her iki yaklaşım da aynı sorunu (I/O verimliliği) çözer ancak farklı programlama modelleri kullanır. Virtual Threads klasik imperatif kod yazmaya izin verirken reaktif yaklaşım stream'ler şeklinde yeniden yazımı zorunlu kılar.
// Same logic: imperative vs reactive
@Service
public class ComparisonService {
// VIRTUAL THREADS APPROACH: classic imperative code
// Easy to read, debug, and maintain
public UserData getUserDataImperative(Long id) {
User user = userRepository.findById(id).orElseThrow();
List<Order> orders = orderRepository.findByUserId(id);
PaymentInfo payment = paymentService.getInfo(id);
return new UserData(user, orders, payment);
}
// REACTIVE APPROACH: streams and operators
// More complex but native backpressure
public Mono<UserData> getUserDataReactive(Long id) {
return userRepository.findById(id)
.zipWith(orderRepository.findByUserId(id).collectList())
.zipWith(paymentService.getInfo(id))
.map(tuple -> new UserData(
tuple.getT1().getT1(),
tuple.getT1().getT2(),
tuple.getT2()
));
}
}| Kriter | Virtual Threads | Reaktif | |--------|-----------------|---------| | Öğrenme eğrisi | Düşük | Yüksek | | Hata ayıklama | Klasik stack trace | Karmaşık | | Backpressure | Manuel | Yerel | | Ekosistem | Büyüyen | Olgun | | Eski kod geçişi | Basit | Yeniden yazma |
Yeni uygulamalar ve göçler için Virtual Threads önerilir. Reaktif programlama, gelişmiş backpressure gerektiren senaryolarda hâlâ değerlidir.
Soru 15: ThreadLocal Virtual Threads ile nasıl yönetilir?
ThreadLocal Virtual Threads ile çalışır, ancak her örnek için bellek tüketir. Scoped Values (JEP 446) bağlam paylaşımı için daha verimli bir alternatif sunar.
// Comparing context approaches
public class ThreadLocalVsScopedValue {
// CLASSIC APPROACH: ThreadLocal
// Works but expensive with millions of VT
private static final ThreadLocal<RequestContext> requestContext =
new ThreadLocal<>();
public void processWithThreadLocal(Request request) {
requestContext.set(new RequestContext(request.getTraceId()));
try {
// Context accessible everywhere in the thread
processRequest();
} finally {
requestContext.remove(); // Important to prevent leaks
}
}
// MODERN APPROACH: ScopedValue (Java 21+)
// More efficient, immutable, explicit scope
private static final ScopedValue<RequestContext> CONTEXT =
ScopedValue.newInstance();
public void processWithScopedValue(Request request) {
ScopedValue.where(CONTEXT, new RequestContext(request.getTraceId()))
.run(() -> {
// Context accessible within this scope
processRequest();
// No cleanup needed - automatic at scope end
});
}
private void processRequest() {
// Context access
String traceId = CONTEXT.isBound()
? CONTEXT.get().getTraceId()
: "unknown";
log.info("Processing with trace: {}", traceId);
}
}Yeni geliştirmeler için ScopedValues önerilir. ThreadLocal kullanan eski kod için aşamalı geçiş yapmak makuldür.
Spring Boot mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Sonuç
Virtual Threads, basit ve yüksek performanslı kod yazımına olanak tanıyarak Java backend geliştirmesini dönüştürür. Önemli noktalar:
Temeller:
- Etkinleştirme:
spring.threads.virtual.enabled=true - I/O ağırlıklı yükler için ideal (REST, BD, dosyalar)
- Yoğun CPU hesaplamaları için kaçınılmalı
Performans:
- Bağlantı havuzlarını doğru boyutlandırın (yeni darboğaz)
- Pinning'i
-Djdk.tracePinnedThreadsile izleyin synchronized'ıReentrantLock'a taşıyın
Geçiş:
- Endpoint grupları halinde aşamalı test edin
- Bağımlılıkların uyumluluğunu doğrulayın
- Paralel işlemler için Structured Concurrency'yi kullanın
Virtual Threads bilgisi, modern uygulamaların performans zorluklarını anlayan adayları öne çıkarır. Bu kavramlar artık Spring Boot teknik mülakatlarında vazgeçilmezdir.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Spring Modulith: Modüler Monolit Mimarisi Açıklaması
Java'da modüler monolitler oluşturmak için Spring Modulith öğrenin. Mimari, modüller, asenkron eventler ve Spring Boot 3 örnekleriyle test.

Spring Batch 5 Mülakat: Partitioning, Chunk ve Hata Toleransı
Spring Batch 5 mülakatlarında ustalaşın: partitioning, chunk işleme ve hata toleransı üzerine 15 temel soru, Java 21 kod örnekleriyle.

Testcontainers Spring Boot: sancısız entegrasyon testleri
Spring Boot 3.4 ile Testcontainers yapılandırması için eksiksiz rehber. Güvenilir ve tekrarlanabilir entegrasyon testleri için Docker konteynerlerinde PostgreSQL, Redis ve Kafka.