Skip to content

15. Spring IoC

15.1. Introduction

Spring çerçevesinin (http://www.springframework.org) yapılandırma ve entegrasyon olanaklarını keşfetmeyi ve aynı zamanda bağımlılık enjeksiyonu (Dependency Injection) olarak da bilinen IoC (Kontrolün Tersine Çevrilmesi) kavramını tanımlamayı ve kullanmayı amaçlıyoruz.

Az önce oluşturduğumuz 3-katmanlı uygulamayı ele alalım:

Kullanıcı isteklerine yanıt vermek için, [Application] denetleyicisi [service] katmanına başvurmalıdır. Örneğimizde bu katman, [DaoImpl] türünde bir örnektir. [Application] denetleyicisi, [service] katmanına ilişkin bir referansı [init] yönteminde almıştır (paragraf 14.8.3):

@SuppressWarnings("serial")
public class Application extends HttpServlet {
...

     // hizmet
    ServiceImpl service=null;
...
     // başlatma
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
...
         // [dao] katmanının örneklenmesi
        DaoImpl dao = new DaoImpl();
        dao.init();
         // katmanın örneklenmesi [service]
        service = new ServiceImpl();
        service.setDao(dao);
    }
  • 6. satır: [dao] katmanı, [DaoImpl] örneğinin açıkça oluşturulmasıyla örneklendirilmiştir
  • 9. satır: [service] katmanı, [ServiceImpl] örneğinin açıkça oluşturulmasıyla örneklendirilmiştir

[DaoImpl] ve [ServiceImpl] sınıflarının sırasıyla [IDao] ve [IService] arayüzlerini uyguladığını hatırlatalım. Gelecek sürümlerde, [IDao] arayüzü, veritabanında bulunan kişi listesini yöneten bir sınıf tarafından uygulanacaktır. Örnek olarak bu sınıfa [DaoBD] adını verelim. [dao] katmanının [DaoImpl] uygulamasını [DaoBD] uygulamasıyla değiştirmek, [web] katmanının yeniden derlenmesini gerektirecektir. Nitekim, yukarıdaki 6. satırda [dao] katmanını [DaoImpl] türüyle örneklendiren kod, artık bunu [DaoBD] türüyle örneklendirmelidir. Dolayısıyla, [web] katmanımız, [dao] katmanına bağımlıdır. Yukarıdaki 9. satır, bu katmanın aynı zamanda [service] katmanına da bağımlı olduğunu göstermektedir.

Spring IoC, katmanların birbirinden bağımsız olduğu 3 katmanlı bir uygulama oluşturmamızı sağlayacaktır; yani c.a.d'te bir değişiklik yapıldığında diğerlerini değiştirmek gerekmeyecektir. Bu, uygulamanın geliştirilmesinde büyük bir esneklik sağlar.

Önceki mimari şu şekilde gelişecektir:

[Spring IoC] ile, [Application] denetleyicisi, [service] katmanından ihtiyaç duyduğu referansı şu şekilde alacaktır:

  1. [init] yönteminde, [Spring IoC] katmanından [service] katmanına ilişkin bir referans vermesini isteyecektir
  2. [Spring IoC] katmanı daha sonra, hangi sınıfın örneklenmesi gerektiğini ve nasıl başlatılması gerektiğini belirten XML yapılandırma dosyasını kullanacaktır.
  3. [Spring IoC], oluşturulan [service] katmanının referansını [Application] denetleyicisine geri verir.

Bu çözümün avantajı, artık farklı katmanları örneklendiren sınıfların adlarının denetleyicinin [init] yönteminde sabit olarak kodlanmaması, bunun yerine sadece bir yapılandırma dosyasında yer almasıdır. Bir katmanın uygulamasını değiştirmek, bu yapılandırma dosyasında bir değişikliğe yol açacak, ancak denetleyicide herhangi bir değişiklik gerektirmeyecektir.

Şimdi örnekler yardımıyla [Spring IoC]’in sunduğu olanakları inceleyelim.

15.2. Uygulamalı Spring IoC

15.2.1. Spring

[Spring IoC], [http://www.springframework.org/] (Mayıs 2006) adresinde bulunan daha geniş bir projenin parçasıdır:

  • [1]: Spring, burada dépendances olarak adlandırılan çeşitli üçüncü taraf teknolojileri kullanır. Daha sonra üçüncü taraf araçların kütüphanelerini indirmek zorunda kalmamak için dépendances sürümünü indirmeniz gerekir.
  • [2]: İndirilen sıkıştırılmış dosyanın dizin yapısı
  • [3]: [Spring] ve c.a.d dağıtımları. Spring projesinin bağımlılıkları hariç .jar dosyaları. Spring'in [IoC] görünümü, [spring-core.jar, spring-beans.jar] arşivleri tarafından sağlanır.
  • [4,5]: Üçüncü taraf araçların arşivleri

15.2.2. Örneklerin Eclipse projeleri

Spring IoC'in kullanımını gösteren üç örnek oluşturacağız. Bunların tümü aşağıdaki Eclipse projesinde yer alacaktır:

Image

[springioc-exemples] projesi, kaynak dosyaların ve derlenmiş sınıfların proje klasörünün kök dizininde yer alacağı şekilde yapılandırılmıştır:

  • [1]: [Eclipse] proje klasörünün dizin yapısı
  • [2]: Spring yapılandırma dosyaları projenin kök dizininde, yani uygulamanın Classpath dizininde yer almaktadır
  • [3]: Örnek 1'deki sınıflar
  • [4]: Örnek 2'deki sınıflar
  • [5]: Örnek 3'ün sınıfları
  • [6]: [spring-core.jar, spring-beans.jar] projesine ait kütüphaneler, Spring dağıtımındaki [dist] klasöründe ve [commons-logging.jar] kütüphaneleri ise [lib/jakarta-commons] klasöründe bulunur. Bu üç arşiv, uygulamanın Classpath paketine dahil edilmiştir.

15.2.3. Örnek 1

Örnek 1'deki öğeler, projenin [springioc01] paketine yerleştirilmiştir:

Image

[Personne] sınıfı şu şekildedir:

package istia.st.springioc01;

public class Personne {

     // özellikler
    private String nom;
    private int age;

     // Kişi ekranı
    public String toString() {
        return "nom=[" + this.nom + "], age=[" + this.age + "]";
    }

     // başlatma-kapatma
    public void init() {
        System.out.println("init personne [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy personne [" + this.toString() + "]");
    }

     // alıcı-ayarlayıcılar
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

}

Sınıfta şunlar bulunmaktadır:

  • 6-7. satırlar: "nom" ve "age" adlı iki özel alan
  • 23-38. satırlar: bu iki alanın okuma (get) ve yazma (set) yöntemleri
  • 10-12. satırlar: toString nesnesinin değerini bir karakter dizisi biçiminde almak için kullanılan toString yöntemi
  • satır 15-21: nesnenin oluşturulmasında Spring tarafından çağrılacak bir init yöntemi ve nesnenin yok edilmesinde çağrılacak bir close yöntemi

Spring kullanarak [Personne] türündeki nesneleri oluşturmak için aşağıdaki [spring-config-01.xml] dosyasını kullanacağız:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="personne1" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="personne2" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
</beans>
  • 3. ve 12. satırlar: <beans> etiketi, Spring yapılandırma dosyalarının kök etiketidir. Bu etiketin içinde, <bean> etiketi oluşturulacak farklı nesneleri tanımlamak için kullanılır.
  • 4-7. satırlar: bir bean'in tanımı
  • 4. satır: Bean'in adı [personne1]'tir (id özniteliği) ve [istia.st.springioc01.Personne] sınıfının bir örneğidir (class özniteliği). Örnek oluşturulduktan sonra [init] yöntemi çağrılacaktır (init-method özniteliği) ve örnek yok edilmeden önce [close] yöntemi çağrılacaktır (destroy-method özniteliği).
  • 5. satır: Oluşturulan [Personne] örneğinin [nom] özelliğine (name özniteliği) verilecek değeri tanımlar. Bu başlatma işlemini gerçekleştirmek için Spring, [setNom] yöntemini kullanacaktır. Dolayısıyla bu yöntemin mevcut olması gerekir. Burada durum böyledir.
  • 6. satır: [age] özelliği için de durum aynıdır.
  • 8-11. satırlar: [personne2] adlı bir bean'in benzer şekilde tanımlanması

[Main] test sınıfı şu şekildedir:

package istia.st.springioc01;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
         // Spring yapılandırma dosyasının işlenmesi
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-01.xml"));
         // [personne1] bean'inin alınması
        Personne personne1 = (Personne) bf.getBean("personne1");
        System.out.println("personne1=" + personne1.toString());
         // bean'in alınması [personne2]
        Personne personne2 = (Personne) bf.getBean("personne2");
        System.out.println("personne2=" + personne2.toString());
         // [personne2] bean'inin yeniden alınması
        personne2 = (Personne) bf.getBean("personne2");
        System.out.println("personne2=" + personne2.toString());
         // tüm bean'ler siliniyor
        bf.destroySingletons();
    }
}

Açıklamalar:

  • 10. satır: [spring-config-01.xml] dosyasında tanımlanan bean'leri elde etmek için, [XmlBeanFactory] türünde bir nesne kullanıyoruz; bu nesne, XML dosyasında tanımlanan bean'leri örneklendirmemizi sağlar. [spring-config-01.xml] dosyası, uygulamanın [ClassPath] dosyası olan c.a.d içinde, Java sanal makinesinin uygulama tarafından referans verilen bir sınıfı ararken taradığı dizinlerden birinde yer alacaktır. [ClassPathResource] nesnesi, bir uygulamanın [ClassPath] dosyasında (burada [spring-config-01.xml] dosyası) bir kaynağı aramak için kullanılır. Elde edilen [bf] nesnesi (Bean Factory), bf.getBean("XX") komutu aracılığıyla "XX" adlı bir bean'in referansını almaya olanak tanır.
  • 12. satır: [spring-config-01.xml] dosyasında [personne1] adlı bean'e ait bir referans istenir.
  • 13. satır: İlgili [Personne] nesnesinin değeri görüntülenir.
  • 15-16. satırlar: [personne2] adlı bean için de aynı işlem yapılır.
  • 18-19. satırlar: [personne2] adlı bean yeniden istenir.
  • 21. satır: [bf] ve c.a.d adlı tüm bean'ler silinir. Bunlar, [spring-config-01.xml] dosyasından oluşturulmuştur.

[Main] sınıfının çalıştırılması aşağıdaki sonuçları verir:

1
2
3
4
5
6
7
init personne [nom=[Simon], age=[40]]
personne1=nom=[Simon], age=[40]
init personne [nom=[Brigitte], age=[20]]
personne2=nom=[Brigitte], age=[20]
personne2=nom=[Brigitte], age=[20]
destroy personne [nom=[Simon], age=[40]]
destroy personne [nom=[Brigitte], age=[20]]

Açıklamalar:

  • 1. satır, [Main] dosyasının 12. satırının çalıştırılmasıyla elde edilmiştir.
Personne personne1 = (Personne) bf.getBean("personne1");

işlemi, [personne1] bean'inin oluşturulmasını zorunlu kılmıştır. [personne1] bean'inin tanımında [init-method="init"] yazılmış olduğu için, oluşturulan [Personne] nesnesinin [init] yöntemi çalıştırıldı. İlgili mesaj görüntülenir.

  • 2. satır: [Main]'in 13. satırı, oluşturulan [Personne] nesnesinin değerini görüntüledi.
  • 3-4. satırlar: Aynı durum, [personne2] adlı bean için de tekrarlanmaktadır.
  • satır 5: [Main]'in 18-19. satırlarındaki işlem
    personne2 = (Personne) bf.getBean("personne2");
    System.out.println("personne2=" + personne2.toString());

işlemi, [Personne] türünde yeni bir nesnenin oluşturulmasına yol açmamıştır. Eğer böyle olsaydı, [init] yönteminin çıktısı görüntülenirdi. Bu, singleton ilkesidir. Spring, varsayılan olarak, yapılandırma dosyasındaki bean'lerin yalnızca tek bir örneğini oluşturur. Bu, bir nesne referans hizmetidir. Henüz oluşturulmamış bir nesnenin referansı istenirse, Spring onu oluşturur ve bir referans döndürür. Nesne zaten oluşturulmuşsa, Spring sadece bir referans verir. Burada [personne2] zaten oluşturulmuş olduğundan, Spring sadece bir referans döndürür.

  • 6-7. satırlardaki çıktılar, [Main] dosyasının 21. satırından kaynaklanmaktadır; bu satır, [XmlBeanFactory bf] nesnesi tarafından referans verilen tüm bean'lerin, yani [personne1, personne2] bean'lerinin yok edilmesini talep etmektedir. Bu iki bean, [destroy-method="close"] özniteliğine sahip olduğu için, her iki bean'in [close] yöntemi yürütülür ve 6-7. satırların görüntülenmesine neden olur.

Artık Spring yapılandırmasının temellerini öğrendiğimize göre, açıklamalarımızı biraz daha hızlı yapabileceğiz.

15.2.4. Örnek 2

Örnek 2'deki öğeler, projenin [springioc02] paketinde yer almaktadır:

Image

[springioc02] paketi, önce [springioc01] paketinin kopyalanıp yapıştırılmasıyla elde edilir; ardından bu pakete [Voiture] sınıfı eklenir ve [Main] sınıfı yeni örneğe uyarlanır.

[Voiture] sınıfı şu şekildedir:

package istia.st.springioc02;

public class Voiture {
     // özellikler
    private String marque;
    private String type;
    private Personne propriétaire;

     // yapıcılar
    public Voiture() {
    }

    public Voiture(String marque, String type, Personne propriétaire) {
        setMarque(marque);
        setType(type);
        setPropriétaire(propriétaire);
    }

     // toString
    public String toString() {
        return "Voiture : marque=[" + this.marque + "] type=[" + this.type
                + "] propriétaire=[" + this.propriétaire + "]";
    }

     // alıcı-ayarlayıcılar
    public String getMarque() {
        return marque;
    }

    public void setMarque(String marque) {
        this.marque = marque;
    }

    public Personne getPropriétaire() {
        return propriétaire;
    }

    public void setPropriétaire(Personne propriétaire) {
        this.propriétaire = propriétaire;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

     // başlatma-kapatma
    public void init() {
        System.out.println("init voiture [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy voiture [" + this.toString() + "]");
    }
}

Sınıfta şunlar bulunur:

  • 5-7. satırlar: tür, marka ve sahip olmak üzere üç özel alan. Bu alanlar, 26-48. satırlardaki bean'lerin get ve set gibi genel yöntemleriyle başlatılabilir ve okunabilir. Ayrıca, 13-17. satırlarda tanımlanan Voiture(String, String, Personne) yapıcı kullanılarak da başlatılabilirler. Sınıf ayrıca JavaBean standardına uymak için argüman içermeyen bir yapıcıya sahiptir.
  • 20-23. satırlar: toString nesnesinin değerini bir karakter dizesi biçiminde almak için kullanılan bir [Voiture] yöntemi
  • 51-57. satırlar: nesnenin oluşturulmasından hemen sonra Spring tarafından çağrılacak bir init yöntemi ve nesnenin yok edilmesinde çağrılacak bir close yöntemi

[Voiture] türünde nesneler oluşturmak için aşağıdaki Spring [spring-config-02.xml] dosyasını kullanacağız:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="personne1" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="personne2" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
    <bean id="voiture1" class="istia.st.springioc02.Voiture" init-method="init" destroy-method="close">
        <constructor-arg index="0" value="Peugeot" />
        <constructor-arg index="1" value="307" />
        <constructor-arg index="2">
            <ref local="personne2" />
        </constructor-arg>
    </bean>
</beans>

Bu dosya, [spring-config-01.xml]'te tanımlanan bean'lere, "voiture1" anahtarına sahip [Voiture] türünde bir bean ekler (12-17. satırlar). Bu bean'i başlatmak için şunu yazabilirdik:

1
2
3
4
5
6
7
    <bean id="voiture1" class="istia.st.springioc.domain.Voiture" init-method="init" destroy-method="close">
        <property name="marque" value="Peugeot"/>
        <property name="type" value="307"/>
        <property name="propriétaire">
            <ref local="personne2"/>
        </property>
</bean>

Örnek 1'de daha önce sunulan bu yöntemi seçmek yerine, burada sınıfın Voiture(String, String, Personne) oluşturucusunu kullanmayı tercih ettik.

  • 12. satır: Bean'in adı, sınıfı, örneklendirilmesinden sonra çalıştırılacak yöntem ve silinmesinden sonra çalıştırılacak yöntemin tanımlanması.
  • 13. satır: [Voiture(String, String, Personne)] yapıcı işlevinin 1. parametresinin değeri.
  • 14. satır: [Voiture(String, String, Personne)] yapıcı işlevinin 2. parametresinin değeri.
  • 15-17. satırlar: [Voiture(String, String, Personne)] yapıcı işlevinin üçüncü parametresinin değeri. Bu parametre, [Personne] türündedir. Bu parametreye, aynı dosyada tanımlanan [personne2] bean'inin referansı (ref etiketi) değer olarak verilir (local özniteliği).

Testlerimiz için aşağıdaki [Main] sınıfını kullanacağız:

package istia.st.springioc02;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
         // Spring yapılandırma dosyasının kullanılması
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-02.xml"));
         // bean'in alınması [voiture1]
        Voiture Voiture1 = (Voiture) bf.getBean("voiture1");
        System.out.println("Voiture1=" + Voiture1.toString());
         // bean'leri silme
        bf.destroySingletons();
    }
}

[main] yöntemi, [voiture1] bean'inin referansını ister (12. satır) ve bunu görüntüler (13. satır). Sonuçlar şöyledir:

1
2
3
4
5
init personne [nom=[Brigitte], age=[20]]
init voiture [Voiture : marque=[Peugeot] type=[307] propriétaire=[nom=[Brigitte], age=[20]]]
Voiture1=Voiture : marque=[Peugeot] type=[307] propriétaire=[nom=[Brigitte], age=[20]]
destroy voiture [Voiture : marque=[Peugeot] type=[307] propriétaire=[nom=[Brigitte], age=[20]]]
destroy personne [nom=[Brigitte], age=[20]]

Açıklamalar:

  1. [main] yöntemi, [voiture1] bean'ine bir referans ister (12. satır). Spring, [voiture1] bean'inin oluşturulmasına başlar çünkü bu bean henüz oluşturulmamıştır (singleton). [voiture1] bean'i, [personne2] bean'ine referans verdiği için, bu son bean da sırayla oluşturulur. [personne2] bean'i oluşturulmuştur. Sonuçların 1. satırında, [init] yöntemi yürütülür. Ardından [voiture1] bean'i örneklenir. Sonuçların 2. satırında, [init] yöntemi yürütülür.
  2. Sonuçların 3. satırı, [main]'in 13. satırından gelir: [voiture1] bean'inin değeri görüntülenir.
  3. [main]'in 15. satırı, mevcut tüm bean'lerin yok edilmesini talep eder; bu da sonuçların 4. ve 5. satırlarının görüntülenmesine neden olur.

15.2.5. Örnek 3

Örnek 3'teki öğeler, projenin [springioc03] paketine yerleştirilmiştir:

Image

[springioc03] paketi, önce [springioc01] paketinin kopyalanıp yapıştırılmasıyla elde edilir, ardından bu pakete [GroupePersonnes] sınıfı eklenir, [Voiture] sınıfı silinir ve [Main] sınıfı yeni örneğe uyarlanır.

[GroupePersonnes] sınıfı şu şekildedir:

package istia.st.springioc03;

import java.util.Map;

public class GroupePersonnes {

     // özellikler
    private Personne[] membres;
    private Map groupesDeTravail;

     // getter - setter
    public Personne[] getMembres() {
        return membres;
    }

    public void setMembres(Personne[] membres) {
        this.membres = membres;
    }

    public Map getGroupesDeTravail() {
        return groupesDeTravail;
    }

    public void setGroupesDeTravail(Map groupesDeTravail) {
        this.groupesDeTravail = groupesDeTravail;
    }

     // görüntüleme
    public String toString() {
        String liste = "membres : ";
        for (int i = 0; i < this.membres.length; i++) {
            liste += "[" + this.membres[i].toString() + "]";
        }
        return liste + ", groupes de travail = "
                + this.groupesDeTravail.toString();
    }

     // başlatma-kapatma
    public void init() {
        System.out.println("init GroupePersonnes [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy GroupePersonnes [" + this.toString() + "]");
    }
}

Bu sınıfın iki özel üyesi şunlardır:

  1. satır: üyeler: gruba üye kişilerin bulunduğu bir dizi

  2. satır: groupesDeTravail: bir kişiyi bir çalışma grubuna atayan bir sözlük

Burada, [GroupePersonnes] sınıfının argüman içermeyen bir oluşturucu tanımlamadığına dikkat edilmelidir. Herhangi bir oluşturucu bulunmadığında, argüman içermeyen ve hiçbir işlem yapmayan bir "varsayılan" oluşturucu olduğu hatırlatılmalıdır.

Burada, Spring'in dizi veya sözlük türünde alanlara sahip nesneler gibi karmaşık nesneleri nasıl başlatabileceğini göstermeye çalışıyoruz. Örnek 3'teki [spring-config-03.xml] bean dosyası şöyledir:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="personne1" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="personne2" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
        <property name="nom" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
    <bean id="groupe1" class="istia.st.springioc03.GroupePersonnes" init-method="init" destroy-method="close">
        <property name="membres">
            <list>
                <ref local="personne1" />
                <ref local="personne2" />
            </list>
        </property>
        <property name="groupesDeTravail">
            <map>
                <entry key="Brigitte" value="Marketing" />
                <entry key="Simon" value="Ressources humaines" />
            </map>
        </property>
    </bean>
</beans>
  • 14-17. satırlar: <list> etiketi, dizi türünde olan veya List arayüzünü uygulayan bir alanı farklı değerlerle başlatmaya olanak tanır.
  • 20-23. satırlar: <map> etiketi, Map arayüzünü uygulayan bir alan için de aynı işlemi gerçekleştirmeye olanak tanır.

Testlerimiz için aşağıdaki [Main] sınıfını kullanacağız:

package istia.st.springioc03;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
         // Spring yapılandırma dosyasının kullanılması
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-03.xml"));
         // [groupe1] bean'inin alınması
        GroupePersonnes groupe1 = (GroupePersonnes) bf.getBean("groupe1");
        System.out.println("groupe1=" + groupe1.toString());
         // bean'leri siliyoruz
        bf.destroySingletons();
    }
}
  • 12-13. satırlar: Spring'den [groupe1] bean'ine ilişkin bir referans istenir ve bu bean'in değeri görüntülenir.

Elde edilen sonuçlar şunlardır:

1
2
3
4
5
6
7
init personne [nom=[Simon], age=[40]]
init personne [nom=[Brigitte], age=[20]]
init GroupePersonnes [membres : [nom=[Simon], age=[40]][nom=[Brigitte], age=[20]], groupes de travail = {Brigitte=Marketing, Simon=Ressources humaines}]
groupe1=membres : [nom=[Simon], age=[40]][nom=[Brigitte], age=[20]], groupes de travail = {Brigitte=Marketing, Simon=Ressources humaines}
destroy GroupePersonnes [membres : [nom=[Simon], age=[40]][nom=[Brigitte], age=[20]], groupes de travail = {Brigitte=Marketing, Simon=Ressources humaines}]
destroy personne [nom=[Simon], age=[40]]
destroy personne [nom=[Brigitte], age=[20]]

Açıklamalar:

  • [Main]'in 12. satırında, [groupe1] bean'ine bir referans isteniyor. Spring, bu bean'in oluşturulmasına başlar. [groupe1] bean'i, [personne1] ve [personne2] bean'lerine referans verdiği için, sonuçların 1. ve 2. satırlarında bu iki bean oluşturulur. Ardından [groupe1] bean'i örneklenir ve [init] yöntemi yürütülür (sonuçların 3. satırı).
  • [Main]'in 13. satırı, sonuçların 4. satırını görüntüler.
  • [Main]'in 15. satırı, sonuçların 5-7. satırlarını görüntüler.

15.3. Spring ile n katmanlı bir uygulamanın yapılandırılması

Aşağıdaki yapıya sahip bir 3-katmanlı uygulamayı ele alalım:

kullanıcı

Veriler

İş Katmanı [metier]

Veri erişim katmanı [dao]

Kullanıcı arayüz katmanı [ui]

Burada, bu tür bir mimariyi oluşturmak için Spring'in sunduğu avantajları göstermeyi amaçlıyoruz.

  • Java arayüzlerinin kullanılması sayesinde bu üç katman birbirinden bağımsız hale getirilecektir
  • Üç katmanın entegrasyonu Spring tarafından gerçekleştirilecektir

Eclipse ortamındaki uygulamanın yapısı şu şekilde olabilir:

  • [1]: [dao] katmanı:
    • [IDao]: katman arayüzü
    • [Dao1, Dao2]: Bu arayüzün iki uygulaması
  • [2]: katman [metier]:
    • [IMetier]: katman arayüzü
    • [Metier1, Metier2]: bu arayüzün iki uygulaması
  • [3]: katman [ui]:
    • [IUi]: katman arayüzü
    • [Ui1, Ui2]: bu arayüzün iki uygulaması
  • [4]: Uygulamanın Spring yapılandırma dosyaları. Uygulamayı iki şekilde yapılandıracağız.
  • [5]: Uygulama için gerekli kütüphaneler. Bunlar, önceki örneklerde kullanılan kütüphanelerdir.
  • [6]: Test paketi. [Main1], [spring-config-01.xml] yapılandırmasını; [Main2] ise [spring-config-02.xml] yapılandırmasını kullanacaktır.

Bu örneğin amacı, diğer katmanlar üzerinde hiçbir etki yaratmadan uygulamanın bir veya daha fazla katmanının uygulamasını değiştirebileceğimizi göstermektir. Her şey Spring yapılandırma dosyasında gerçekleşir.


[dao] katmanı


[dao] katmanı, aşağıdaki [IDao] arayüzünü uygular:

1
2
3
4
5
package istia.st.springioc.troistier.dao;

public interface IDao {
    public int doSomethingInDaoLayer(int a, int b);
}

[Dao1] uygulaması şu şekilde olacaktır:

1
2
3
4
5
6
7
8
package istia.st.springioc.troistier.dao;

public class Dao1 implements IDao {

    public int doSomethingInDaoLayer(int a, int b) {
        return a+b;
    }
}

[Dao2] uygulaması şu şekilde olacaktır:

1
2
3
4
5
6
7
8
package istia.st.springioc.troistier.dao;

public class Dao2 implements IDao {

    public int doSomethingInDaoLayer(int a, int b) {
        return a-b;
    }
}

[métier] katmanı


[métier] katmanı, aşağıdaki [IMetier] arayüzünü uygular:

1
2
3
4
5
package istia.st.springioc.troistier.metier;

public interface IMetier {
      public int doSomethingInBusinessLayer(int a, int b);
}

[Metier1] uygulaması şu şekilde olacaktır:

package istia.st.springioc.troistier.metier;

import istia.st.springioc.troistier.dao.IDao;

public class Metier1 implements IMetier {

     // katman [dao]
    private IDao dao = null;

    public IDao getDao() {
        return dao;
    }

    public void setDao(IDao dao) {
        this.dao = dao;
    }

    public int doSomethingInBusinessLayer(int a, int b) {
        a++;
        b++;
        return dao.doSomethingInDaoLayer(a, b);
    }

}

[Metier2] uygulaması şu şekilde olacaktır:

package istia.st.springioc.troistier.metier;

import istia.st.springioc.troistier.dao.IDao;

public class Metier2 implements IMetier {

     // [dao] katmanı
    private IDao dao = null;

    public IDao getDao() {
        return dao;
    }

    public void setDao(IDao dao) {
        this.dao = dao;
    }

    public int doSomethingInBusinessLayer(int a, int b) {
        a--;
        b--;
        return dao.doSomethingInDaoLayer(a, b);
    }

}

[ui] katmanı


[ui] katmanı, aşağıdaki [IUi] arayüzünü uygular:

1
2
3
4
5
package istia.st.springioc.troistier.ui;

public interface IUi {
    public int doSomethingInUiLayer(int a, int b);
}

[Ui1] uygulaması şu şekilde olacaktır:

package istia.st.springioc.troistier.ui;

import istia.st.springioc.troistier.metier.IMetier;

public class Ui1 implements IUi {

     // katman [business]
    private IMetier metier = null;

    public IMetier getMetier() {
        return metier;
    }

    public void setMetier(IMetier business) {
        this.metier = business;
    }

    public int doSomethingInUiLayer(int a, int b) {
        a++;
        b++;
        return metier.doSomethingInBusinessLayer(a, b);
    }

}

[Ui2] uygulaması şu şekilde olacaktır:

package istia.st.springioc.troistier.ui;

import istia.st.springioc.troistier.metier.IMetier;

public class Ui2 implements IUi {

     // katman [business]
    private IMetier metier = null;

    public IMetier getMetier() {
        return metier;
    }

    public void setMetier(IMetier business) {
        this.metier = business;
    }

    public int doSomethingInUiLayer(int a, int b) {
        a--;
        b--;
        return metier.doSomethingInBusinessLayer(a, b);
    }

}

Spring yapılandırma dosyaları


İlki [spring-config-01.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- dao sınıfı -->
    <bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
    <!-- meslek sınıfı -->
    <bean id="metier"     class="istia.st.springioc.troistier.metier.Metier1">
        <property name="dao">
            <ref local="dao" />
        </property>
    </bean>
    <!-- sınıf UI -->
    <bean id="ui" class="istia.st.springioc.troistier.ui.Ui1">
        <property name="metier">
            <ref local="metier" />
        </property>
    </bean>
</beans>

İkinci [spring-config-02.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- DAO sınıfı -->
    <bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
    <!-- iş sınıfı -->
    <bean id="metier"
        class="istia.st.springioc.troistier.metier.Metier2">
        <property name="dao">
            <ref local="dao" />
        </property>
    </bean>
    <!-- UI sınıfı -->
    <bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
        <property name="metier">
            <ref local="metier" />
        </property>
    </bean>
</beans>

Test programları


[Main1] programı şöyledir:

package istia.st.springioc.troistier.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

import istia.st.springioc.troistier.ui.IUi;

public class Main1 {

    public static void main(String[] args) {
         // IUi arayüzünün bir uygulaması alınır
        IUi ui = (IUi) (new XmlBeanFactory(new ClassPathResource("spring-config-01.xml"))).getBean("ui");
         // sınıfı kullanıyoruz
        int a = 10, b = 20;
        int res = ui.doSomethingInUiLayer(a, b);
         // sonuç görüntülenir
        System.out.println("ui(" + a + "," + b + ")=" + res);
    }

}

[Main1] programı, [spring-config-01.xml] yapılandırma dosyasını ve dolayısıyla katmanların [Ui1, Metier1, Dao1] uygulamalarını kullanır. Eclipse konsolunda elde edilen sonuçlar:

ui(10,20)=34

[Main2] programı şöyledir:

package istia.st.springioc.troistier.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

import istia.st.springioc.troistier.ui.IUi;

public class Main2 {

    public static void main(String[] args) {
         // IUi arayüzünün bir uygulamasını alıyoruz
        IUi ui = (IUi) (new XmlBeanFactory(new ClassPathResource("spring-config-02.xml"))).getBean("ui");
         // sınıfı kullanılır
        int a = 10, b = 20;
        int res = ui.doSomethingInUiLayer(a, b);
         // sonuç görüntülenir
        System.out.println("ui(" + a + "," + b + ")=" + res);
    }

}

[Main2] programı, [spring-config-02.xml] yapılandırma dosyasını ve dolayısıyla katmanların [Ui2, Metier2, Dao2] uygulamalarını kullanır. Eclipse konsolunda elde edilen sonuçlar:

ui(10,20)=-10

15.4. Conclusion

Oluşturduğumuz uygulama, geliştirme açısından büyük bir esnekliğe sahiptir. Basit bir yapılandırma ile bir katmanın uygulamasını değiştirebiliyoruz. Diğer katmanların kodu ise değişmeden kalır. Bu, Spring'in iki temel direğinden biri olan IoC kavramı sayesinde sağlanmaktadır. Diğer temel unsur ise, daha önce ele almadığımız AOP (Aspect Oriented Programming) kavramıdır. Bu kavram, yine yapılandırma yoluyla, sınıf yöntemine kodunu değiştirmeden “davranış” eklemeye olanak tanır.