Skip to content

rahmanusta/java-service-spi-example

Repository files navigation

Java ServiceLoader Nedir? SPI (Service Provider Interface) Nasıl Yazılır?

Java programlama dilinde yazılan servisler, farklı servis sağlayıcıları tarafından uygulanabilmektedir. Servisler genel olarak arayüzler veya soyut sınıflar ile yazılırken, servis sağlayıcıları ise bu arayüz servislerini uygulayan uygulayıcı sınıflardır.

Java programlama dilinin 6. versiyondan beri bulunan java.util.ServiceLoader sınıfı ise, uygulamalarınızda yer alan farklı servis sağlayıcılarına erişerek, kullanmanıza imkan sağlamaktadır.

Örneğin bir servis yazarının RandomServiceProvider adında bize bir arayüz sunduğunu varsayalım;

public interface RandomServiceProvider {

    public Integer random(int start, int end);

}
Note

RandomServiceProvider sınıfının bulunduğu projenin dizin yapısı aşağıdaki gibidir.

random-service-spi projesi

random-service-spi pom.xml random-service-spi.iml src #main #java com #kodcu spi RandomServiceProvider.java #resources

Bu servisin görevi adından da anlaşılacağı üzere verilen iki tam sayı arasında rastegele bir sayı üretmesidir. Servis sağlayıcı sınıfları genel olarak arayüz veya soyut sınıf türünden olmaktadır.

Spesifikasyon belirleyicileri, farklı uygulayıcılar için yukarıdakine benzer arayüzleri API uygulayıcılarına açmaktadır. Java standartlarının geliştirim sürecine bakıldığında, buna benzer bir süreç sürdürülmektedir. API uygulayıcıları ise bu arayüzlere dönük kendi implementasyonlarını geliştirmektedir.

Fakat iş sadece arayüzü tanımlamak değildir, bunun yanında servis sağlayıcı kütüphanenin sisteminize standart bir yolla entegre edilebilmesi gerekmektedir. java.util.ServiceLoader sınıfı bu noktada, servis uygulayıcılarına standart arayüz servislerine eklenti olarak eklenme imkanı sunmaktadır. Örneğin şimdi, RandomServiceProvider arayüzünün kullanıcı talebine karşı farklı sağlayıcıları sunabiliyor olmasını sağlayalım. Bunun için bu servise iki statik metod daha ekleyeceğiz.

Note
Java 8 ile birlikte arayüzlere static metodlar tanımlanabilmektedir. Java 8 öncesini kullanıyorsanız interface yerine abstract sınıfları da kullanabilirsiniz.
public interface RandomServiceProvider {

    public Integer random(int start, int end);

    public static RandomServiceProvider getProvider(String providerName) {
        return ...;
    }

    public static RandomServiceProvider getDefaultProvider() {
        return ...;
    }

}

RandomServiceProvider sınıfına getProvider ve getDefaultProvider adlarında iki statik metod ekledik. Bunlardan getDefaultProvider() çağrıldığında bu servisin varsayılan imlpementasyonunun kullanıcıya döndürülmesini istiyoruz. getProvider(..) olanla ise metoda sunulan sağlayıcı ismiyle X bir sağlayıcının döndürülmesini istiyoruz. İşte bu sağlayıcı sınıflar türünden uygulayıcı nesnelerin sistemde aranıp sunulması için ServiceLoader sınıfından faydalanacağız. Fakat bu metodların içerisini ServiceLoader ile yapılandırmadan evvel bir varsayılan sağlayıcı nesnesi oluşturma yoluna gidelim.

public class StandardRandomProvider implements RandomServiceProvider {

    @Override
    public Integer random(int start, int end) {
        Random random = new Random();
        int randomInt = random.nextInt((end - start + 1)) + start;
        return randomInt;
    }

}

Görüldüğü üzere RandomServiceProvider sağlayıcı arayüzünü uygulayan bir StandardRandomProvider sınıfı oluşturduk. Bu sınıf içerisindeki random() metodu, Random sınıfı üzerinden verilen aralıkta rastgele bir tamsayı üretmektedir.

ServiceLoader sınıfının, bu sağlayıcıya erişip yükleyebilmesi için, sağlayıcının belirli bir formda Java projesine kayıt ettirilmesi gerekmektedir. Bunun için StandardRandomProvider sınıfının yer aldığı projede META-INF/services dizini altında RandomServiceProvider sınıfının tam adıyla bir dosya oluşturulmalı ve bu dosyanın içine RandomServiceProvider sınıfının tam adı yazılmalıdır.

standard-random-service projesi

standard-random-service pom.xml src #main java #com #kodcu StandardRandomProvider.java #resources META-INF #services #com.kodcu.spi.RandomServiceProvider

com.kodcu.spi.RandomServiceProvider dosyasını içi ise aşağıdaki gibidir.

com.kodcu.spi.RandomServiceProvider dosyasının içi
com.kodcu.StandardRandomProvider

Bu şekilde artık StandardRandomProvider servisini ServiceLoader sistemine göre yapılandırmış oluyoruz. Artık ServiceLoader, sağlayıcı sınıfı arayıp bulabilir ve sisteme yükleyebilir.

Şimdi, RandomServiceProvider arayüz içini uygulayıcılarını sunar şekilde yapılandırmaya devam edelim.

public interface RandomServiceProvider {

public Integer random(int start, int end);

public static RandomServiceProvider getDefaultProvider() {
    return getProvider("com.kodcu.StandardRandomProvider"); (1)
}

public static RandomServiceProvider getProvider(String providerName) {
    ServiceLoader<RandomServiceProvider> serviceLoader = ServiceLoader.load(RandomServiceProvider.class); // (2)

    for (RandomServiceProvider provider : serviceLoader) { // (3)
        String className = provider.getClass().getName(); // (4)
        if (providerName.equals(className)) // (5)
            return provider;
    }

    throw new RuntimeException(providerName + " provider is not found!"); // (6)
}

}
  1. com.kodcu.StandardRandomProvider tam isimli varsayılan sağlayıcı sınıfını CLASSPATH 'den yükler ve döndürür.

  2. Yükleyeceği sağlayıcı arayüz türünden bir ServiceLoader nesnesi üretilir.

  3. CLASSPATH içinde bulunan tüm RandomServiceProvider servis sınıfları türünden nesneler turlanır.

  4. Bulunan RandomServiceProvider türünden sınıfın tam adı elde edilir.

  5. Eğer bulunan sınıfın tam adı metoda düşen isimle aynı ise ilgili nesne metotdan döndürülür.

  6. Eğer CLASSPATH içinde hiçbir servis uygulayıcısı bulunmadıysa bir istisna mesajı ile geliştirici bilgilendirilir.

Şimdi varsayılan StandardRandomProvider sağlayıcısına alternatif yeni bir sağlayıcı oluşturalım. İsmi ThreadedRandomProvider olsun.

public class ThreadedRandomProvider implements RandomServiceProvider {

    @Override
    public Integer random(int start, int end) {
        int randomInt = ThreadLocalRandom.current().nextInt(start,end);
        return randomInt;
    }
}

ThreadedRandomProvider sınıfı rastgele sayı üretme işini Random sınıfı yerine ThreadLocalRandom sınıfı üzerinden yapıyor. Tabiki bu servis sınıfını yine kendi projesinde META-INF/services altında yapılandırmalıyız.

threaded-random-service projesi

threaded-random-service pom.xml src #main java #com #kodcu ThreadedRandomProvider.java #resources META-INF #services #com.kodcu.spi.RandomServiceProvider

com.kodcu.spi.RandomServiceProvider dosyasını içi ise aşağıdaki gibidir.

com.kodcu.spi.RandomServiceProvider dosyasının içi
com.kodcu.ThreadedRandomProvider

Böylece RandomServiceProvider servisi StandardRandomProvider gibi ThreadedRandomProvider sınıfını da kullanıcılarına sunabilecektir.

Şimdi bu üç ayrı Java projeyesini random-service-spi , standard-random-service , threaded-random-service çalıştıran random-service-app adında bir proje oluşturalım. Bu proje random-service-spi servisi üzerinden iki servis sağlayıcısını yükleyecek ve random() metodlarını koşturacaktır.

public class App {

    public static void main(String[] args) {

        RandomServiceProvider defaultProvider = RandomServiceProvider.getDefaultProvider(); // (1)

        System.out.println(defaultProvider.random(1,1000));  // (2)

        RandomServiceProvider threadedProvider = RandomServiceProvider.getProvider("com.kodcu.ThreadedRandomProvider"); // (3)

        System.out.println(threadedProvider.random(1,1000)); // (4)
    }

}
  1. RandomServiceProvider.defaultProvider() metodu üzerinden varsayılan servis sağlayıcısını com.kodcu.StandardRandomProvider yükler.

  2. StandardRandomProvider 'ın random() metodu ile rastgele bir sayı üretip çıktılar.

  3. RandomServiceProvider.getProvider() metodu üzerinden com.kodcu.ThreadedRandomProvider servis sağlayıcısını yükler.

  4. ThreadedRandomProvider 'ın random() metodu ile rastgele bir sayı üretip çıktılar.

random-service-app projesinin dosya sistemindeki görünümü aşağıdaki gibidir.

random-service-app projesi

random-service-app pom.xml src #main java #com #kodcu spi App.java #resources

Böylece standart bir servise, ekler halinde ayrı uygulayıcılar tanıtmış olduk. ServiceLoader API ise biz geliştiricilere standart bir yol sunmuş oluyor. Bu yaklaşımın birçok popüler Java Framework’lerinde de kullanıyor olduğunu söylemeliyim.

Aşağıda servis ve iki uygulayıcısını ve bunları tüketen App sınıfını UML diagram halinde görebilirsiniz.

interface RandomServiceProvider { public Integer random(int start, int end); public static RandomServiceProvider getProvider(String providerName) { } public static RandomServiceProvider getDefaultProvider() { } }

class ThreadedRandomProvider implements RandomServiceProvider { @Override public Integer random(int start, int end) { } }

class StandardRandomProvider implements RandomServiceProvider { @Override public Integer random(int start, int end) { } }

class App { public static void main(String[] args) { } }

Kaynak kodlara buradaki bağlantıdan erişebilirsiniz.

Tekrar görüşmek dileğiyle

About

Java SPI (Service Provider Example)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages