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):
- 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:
- [init] yönteminde, [Spring IoC] katmanından [service] katmanına ilişkin bir referans vermesini isteyecektir
- [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.
- [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:

[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:
![]()
[Personne] sınıfı şu şekildedir:
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:
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:
Açıklamalar:
- 1. satır, [Main] dosyasının 12. satırının çalıştırılmasıyla elde edilmiştir.
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:

[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:
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:
Ö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:
[main] yöntemi, [voiture1] bean'inin referansını ister (12. satır) ve bunu görüntüler (13. satır). Sonuçlar şöyledir:
Açıklamalar:
- [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.
- Sonuçların 3. satırı, [main]'in 13. satırından gelir: [voiture1] bean'inin değeri görüntülenir.
- [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:

[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:
Bu sınıfın iki özel üyesi şunlardır:
-
satır: üyeler: gruba üye kişilerin bulunduğu bir dizi
-
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:
- 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:
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:
[Dao1] uygulaması şu şekilde olacaktır:
[Dao2] uygulaması şu şekilde olacaktır:
[métier] katmanı
[métier] katmanı, aşağıdaki [IMetier] arayüzünü uygular:
[Metier1] uygulaması şu şekilde olacaktır:
[Metier2] uygulaması şu şekilde olacaktır:
[ui] katmanı
[ui] katmanı, aşağıdaki [IUi] arayüzünü uygular:
[Ui1] uygulaması şu şekilde olacaktır:
[Ui2] uygulaması şu şekilde olacaktır:
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:
[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:
[Main2] programı şöyledir:
[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:
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.






