28 Nisan 2016 Perşembe

Anotasyon İşleme İle Kod Oluşturma


Java 5 sürümüyle hayatımıza giren anotasyonlar (annotations) bugün pek çok modern framework tarafından -konfigrasyon ağırlıkta olmak üzere- kullanılmakta; spring tarafında bean tanımlamalarında, bağımlılık enjektesinde, mvc routing'de, orm kütüphanelerinde tablo, kolon vs tanımlamalarında, AOP'de vs. Bu çözümlerde anotasyonlar genelde yansıma (reflection) ile çalışma zamanında kullanılıyor. Oysa anotasyonlar derleme zamanında da kullanılabiliyor. Bunu sağlayan yapı java derleyicisi(javac) ile entegre calisan Annotation Processor Tool (APT). APT ile cok kolay biçimde derleme zamanında kod üretimi (Code Generation) yapılabiliyor.

Derleme zamanında kod üretimi yaparak karşılaştığımız problemlere farklı bir bakış açısı getirebiliriz; en basitinden sürekli tekrar ettiğiniz bazı kod rutinlerini (boilerplate code vs) otomatize edebiliriz yine reflection yerine kullanarak performanstan kazanabiliriz (android orm kütüphanelerinin bazıları bu yontemi kullanıyor; bkz. DBFlow). Misal android tarafında pojo sınflarınızı parcelable yapmak icin Parcelable arayüzünü implemente eden rutin kodları yazmanız gerek oysa parceler adlı kütüphaneyi kullanarak bu işi, ilgili pojo sıfnıfını @Parcel ile işaretlemeye kadar indirgiyoruz. Parceler kütüphanesi APT'yi kullanıp derleme sırasında @Parcel ile işaretlenmiş sınıfları tarayıp bu sınıflar için gerekli sınıfları sizin için oluşturuyor.

İşleyici Oluşturma

Kendi anotasyon işleyecilerinizi oluşturmak icin AbstractProcessor sınıfından türeyen bir sınıf olusturmanız gerek. Java 6 (JSR 269) ile birlikte gelen AbstractProcessor sınıfı Java Annotation Processor API'in merkezinde yer alıyor ve yapısı kabaca aşağıdaki gibi:
package javax.annotation.processing;

import ...

public abstract class AbstractProcessor implements Processor {
 
 // adı uzerinde olusturma sırasında islem yapmak icin
 public synchronized void init(ProcessingEnvironment processingEnv) { }

 // desteklenen dil versiyonu(tavsiyem SourceVersion.latestSupported()), anndroid icin java6(SourceVersion.RELEASE_6) kullanmak gerek
 public SourceVersion getSupportedSourceVersion() { }

 // hangi anotasyonlar icin islem yapacagımızı burda belirtiyoruz
 public Set getSupportedAnnotationTypes() { }

 // en önemli metot; kod uretimini bu metot ile yapıyoruz
 public boolean process(Set annoations, RoundEnvironment env) { }

}
Alternatif olarak @SupportedSourceVersion ve @SupportedAnnotationTypes anotasyonları sayesinde yukarıdaki ilgili iki metodu ezmek zorunlu değil işleyici sınıfı bu anotasyonlar ile işaretlemeniz yeterli.

İşleyici Kaydı

İkinci ve son adım javac icin işleyici sınıfınızı kayıt (register) etmek. Bunun için işleyecinizi içeren bir *.jar dosyası oluşturmalısınız. Ayrıca bu paket src/main/resources/META-INF/services altında javax.annotation.processing.Processor adlı bir dosya barındırmalı ve içeriği de oluşturduğunuz işleyici sınıfın paket adı ve ismi olmalı: gturedi.library.CustomProcessor gibi. "Bir kayıt için bu kadar işçiliğe gerek mi var?" diyorsanız kolayı var: Google AutoService :]

Bu arada javac anotasyon işleyicileriniz icin ayrı bir jvm ayağa kaldırır yani işleyeci modülünde kullandığınız bağımlılıklar ana uygulamanıza eklenmez, bu nokta özellikle android gibi kaynakları sınırlı(65k method limit, apk size vs) ortamlar icin geliştirme yapıyorsanız önemli yani gözünüz kapalı guava kullanabilirsiniz! :] Tabii bağımlılıkları doğru ayarlamanız gerekli; işleyiciyi, anotasyonları ve uygulamayı barındıran modüller ayrık/farklı olmalı.

Kod Oluşturma Evresi

Eveeet geldik işimizi kolaylaştırmak için gerekli sınıfları oluşturacağımız kısma. AbstractProcessor sınfının process metodu ile tanımladığımız anotasyonlar için kod oluşturacağız. Java'da deyim yerinde ve tabiri caizse kodla kod oluşturmak icin üç kütüphane öne çıkıyor bunlar:

  1. Filer: en ilkel yöntem, text dosyası yazar gibi tek tek yazıyorsunuz
  2. Apache Veloctiy: template kullanarak deklaratif biçimde class generate ediyorsunuz
  3. Square JavaPoet: en güncel ve kolay yöntem, örnek projede de kullandım

Pojo sınıflarının tüm field'larını ekrana basmaya yarayan (ToStringProcessor) basit bir örneği github'a koydum. Anotasyon kullanan bazı popüler github projelerini altta listeledim, kaynak kodunu incelemenizde fayda var:


Derleme zamanında kod üretebiliyor olmak biz geliştiriciler için açılmış yeni bir pencere/bakış açısı. Bunu kullanıp nasıl çözümler üretebileceğiniz tamamen o lanet olası hayal gücünüze kalmış!

İyi çalışmalar!

2 yorum:

  1. Eline sağlık üstad, çok güzel olmuş. Yeni paylaşımlarını bekliyorum. Takipteyim..

    YanıtlaSil
    Yanıtlar
    1. sagol kardesim, bence sen de en yakın zamanda blog acıp android konusunda paylasımlar yapmalısın

      Sil