@Async in Spring

Ömer Çelik
3 min readJan 15, 2020

--

Bu makalede, Spring’ in async işlemlerimiz için sağladığı desteklerden biri olan @Async annotationunu işleyeceğiz.

Basitçe söylemek gerekirse, bir bean’in methoduna @Async annotationu eklemek onun main thread’den farklı olarak ayrı bir threadde çalıştırılmasını sağlar.
Yani call edilen methodun tamamlanmasını call eden kısım beklemez. Kod böylece async olarak çalışmış olur.

@Async annotationu ile Async bir method yaratmak çok kolay.
2 Adımda(3. Adım opsiyonel) nasıl yapabileceğinizi gösterip, örneklere geçelim.

  1. Enable Async Support
    Asynchronous process’leri, Java configuration’ı ile enable etmeliyiz(@EnableAsync). @EnableAsync annotationu @Async annotationuna sahip methodları arayacak ve bu methodları background thread pool’larında çalışacaktır.

2. @Async annotationu Async yapmak istediğimiz method üzerine eklemek.

3. İsteğe bağlı olarak Executor’ın ayarlarını özelleştirebilirsiniz.

Görüldüğü gibi bu kadar basit işlemimiz.

ÖNEMLİ BİR NOT : @Async annotationu kullanırken dikkat edilmesi gereken 2 kural vardır.
1. Method public olmalıdır.
2. Aynı class içerisinden @Async bir methodu çağırırsanız çağrılan method asynchronous olarak çalışmaz.

Bunun sebebi biraz kafa karıştırıcı. Aslında Spring’in bu annotation’lu olan destek verdiği şeylerin tasarımından dolayı.(@Transactional, @Async,..)
Kısaca açıklayacağım. Ancak detaylı olarak en son örnekte ele alacağım. Kafanıza oturmazsa burayı geçin.
Kısaca anlatacak olursam : Methodun proxied olabilmesi için public olması gerekiyor. Ve aynı class üzerinde çalışmama sebebi ise proxy üzerinden değil de direct olarak methodun çağrılmasından dolayı çalışmaz.

Artık notu geçerek konuya girmek istiyorum. Öncelikli olarak synchronous bir method yazıp daha sonra nasıl @Async ile asynchronous hale getiririm bunu incelicez.

Bir restimiz olsun. (PersonRestController)

Ve bu rest aşağıdaki methodu çağırıyor.(PersonService)

Output aşağıdaki gibi olur :

//     Output
// getSyncPersonsSize Method Giris : 14/01/2020 00:01:54
// printAsync Method Giris : 14/01/2020 00:01:54
// printAsync Method Cikis : 14/01/2020 00:01:59
// getSyncPersonsSize Method Cikis : 14/01/2020 00:01:59
// Ve return edilen person nesnesi 01.59 'da yapılır yani en son

Yani printSync methodu tamamlanmadan bir sonraki satıra geçilmez.

Şimdi bunu nasıl asynchronous hale getiririz inceleyelim.

Aşağıdaki gibi bir restimiz olsun. (PersonRestConroller)

Ve bu restin çağırdığı method aşağıdadır.(PersonService)

//    OUTPUT :
// getAsyncPerson Method Giris : 14/01/2020 00:04:55
// getAsyncPerson Method Cikis : 14/01/2020 00:04:55
// Ve return edilen person nesnesi burada yapılır. 00:04:55
// printAsync Method Giris : 14/01/2020 00:04:55
// printAsync Method Cikis : 14/01/2020 00:05:00

Böylece methodumuzu async hale getirmiş olduk. printAsync methodunun tamamlanmasını beklemeden kod asynchronous bir biçimde çalışmıştır.

Tüm örneklerimi github’a koydum. Hatta buradaki örneklerden daha fazlası var.
https://github.com/omercelikceng/spring-async-operation-example

NOT : Şimdi ise başta bahsetmiş olduğum 2 kuralı açıklayacağım. @Async veya @Transaction gibi annotationlu işlemler için önemli bir not..

Örneğin;

PersonService sınıfı içerisinde A ve B methodlarını düşünelim.
A methodu içerisinden B(async) methodunu çağırırsak async olmaz.
Çünkü spring’in proxy patterni sadece bean üzerinden bir methodu asynchronous hale getirilebilir.
Yani bean kullanarak methodu çağrımalıyız. Aynı şey transactional içinde
geçerlidir.

Async method için başka bir service oluşturmanız ve bunu servicenizden
call etmeniz gerekir.

Spring common annotationları kullanarak oluşturduğumuz her service ve component için bir proxy oluşturur.
Yalnızca bu proxy’ler @async methodu gibi annotationlu methodların
istenen davranışı yapmasını sağlar. Bu sebeple @Async sadece proxy üzerinden çalışabilir . Proxy’de bean üzerinden çalıştığı için original
naked class üzerinden asynchronous hale getiremeyiz.

Örnek:

PersonController.java

//    OUTPUT  
// getAsyncPerson Method Giris : 14/01/2020 00:09:28
// getAsyncPerson Method Cikis : 14/01/2020 00:09:28
// printComplexAsync Method Giris : 14/01/2020 00:09:28
// printComplexAsync2 Method Giris : 14/01/2020 00:09:28
// printComplexAsync2 Method Cikis : 14/01/2020 00:09:33
// printComplexAsync Method Cikis : 14/01/2020 00:09:33

PersonController sınıfının getComplexAsyncPerson methodundan personService bean’inden printComplexAsync methodu çağrılıyor. Bir bean üzerinden bu Async method çağrıldığı için method async olarak çalışmaktadır. Ve printComplexAsync methodunun işleminin bitmesi beklenmemektedir.

Ancak printComplexAsync methodu içerisinden doğrudan(bean üzerinden değil) printComplexAsync2 methodu çağrılıyor. Ve bu method @Async annotationuna sahip olmasına rağmen async olarak çalışmıyor.

Kodlarımın yer aldığı github hesabım:

https://github.com/omercelikceng/spring-async-operation-example

--

--