10 Ağustos 2017 Perşembe

Spring'te Dependency Injection

Merhaba arkadaşlar,

Bu yazımda Dependency Injection'ın Spring framework'te nasıl uygulandığını anlatmaya çalışacağım.

Bildiğiniz gibi dependency injection class'ların birbiriyle olan bağımlılıklarını azalmak için kullanılan bir yöntemdir. 

Örnek verecek olursak;

Elimizde "A" ve "B" class'ları olsun. "A" class'ında "B" nesnesi üretildiğini düşünelim. Şuan "A" class'ı "B" class'ına bağlı hale geldi. Bunların bağımlılıklarını azaltmamız gerekmektedir. Bu sebepten A class'ı içinde bir B nesnesini new anahtar kelimesiyle yaratmak yerine dışarıdan bunu vereceğiz.  


Dışarıdan verebilmemiz için setter veya constructor kullanmamız gerekmektedir. 

Peki bu constructor veya setter methodu içine B class'ı tipinde bir nesne mi alması gerekiyor? Tabikide hayır. 

Bu işlem şu şekilde olmalıdır.

A class'ı setter metodu veya constructor'a bir interface tipinde nesne istemeli. Bu interfce tipindeki nesnenin ismini Ib olarak tanımladık diyelim. 

Daha sonra B class'ı bu Ib interfacesini implement edip gerekli metodlarını override eder. Şuan B nesnesi Ib interfacesini impelemt ettiği için A class'ınının setter'ına veya constructor'ına parametre olarak geçilebilir. 

Peki A setter veya constructor Ib interface nesnesini parametre olarak isterken nasıl B class'ı tipinde bir nesneyi parametre olarak gönderebileceğiz?

Bu sorunun cevabı Java tarafından sağlanan POLIMORFIZM'dir. Anlayamayanlar java polimorfizm konusunu detaylı incelemelidir.

Eğer yapıyı bu şekle getirebilirsek bir C class'ı eğer Ib interfacesini implement edip, gerekli metodları override ederse, C class'ını da A setter metod veya constructor'a parametre olarak geçebiliriz. 

( Parametre olarak geçirilen kısım Ib interfacesine casting ediliyor diye düşünebiliriz.)

Biraz kafanız karışmış olmuş olabilir. Bu konuyu daha iyi anlamak için öncelikle Dependency Injection ( DI ) yazımı okumalıdır.


Spring DI

Spring framework'ünün en büyük özelliklerden birisi dependency injection desteği sağlamasıdır.

Spring'de iki tür injection vardır. Bunlar Setter Injection ve Constructor Injection.

Örnek üzerinden açıklamaya çalışa.


1.Setter Injection





import java.util.List;
 
public class SearchEngine1
{
    private Search searchAlgorithm;
     

     
    public Search getSearchAlgorithm() {
        return searchAlgorithm;
    }
 
    public void setSearchAlgorithm(Search searchAlgorithm) {
        this.searchAlgorithm = searchAlgorithm;
    }   
 
 
     public boolean search( List<Integer> list, int id )
    {
        return searchAlgorithm.find(list, id);
    }
     
}
import java.util.List;
 
public interface Search 
{
    public boolean find( List<Integer> list,  int id );
}

SeachEngine1 class'ını incelediğimzde  Search tipinde searchAlgorithm isminde bir değişkene sahip olduğunu görüyoruz. Bu nesnenin dependency injection mantığı gereği dışarıdan verilmesi gerekmektedir. Ayrıca esneklik sağlanması için Search tipi bir interface tipi olması gerekmektedir. Böylelikle Search interfacesini implement eden her class setSearchAlgorithm metoduna parametre olarak geçilebilir. ( polimorfizm ) ( sanki Search interfacesine cast ediliyormuş gibi düşünülebilir.)



import java.util.Collections;
import java.util.List;

public class BinarySearch implements Search 
{
    @Override
    public boolean find(List<Integer> list, int id) 
    {
        System.out.println("Binary Search");
         
        Collections.sort( list );       
        return Collections.binarySearch(list, id)>=0;
    }   
}

Kodu incelersek, BinarySearch class'ı Search class'ını implement etmiş ve find methodunu Override edip kendi search tipini bu metoda uygulamış.






import java.util.List;
 
public class SequentialSearch implements Search
{
    @Override
    public boolean find(List<Integer> list, int id) 
    {   
        for( Integer i : list )
        {
            if( i == id )
                return true;
        }
         
        System.out.println("Sequential Search");
         
        return false;
    }
         
}  
SequentialSearch class'ını incelersek, Search interfacesini implement etmiş ve find() metodunu Override etmiş. find() metoduna kendi search algoritmasını uygulamış.


BinarySearch ve SequentialSearch class'larını incelersek ikiside Search interfacesini implement etmiş. Yani  ikiside SearchEngine1 classında bulunan Search searchAlgorithm nesnesinin set edilmesi için kullanılan setSearchAlgorithm( ) metoduna parametre olarak verilebilir. Çünkü bu iki class'ta Search interfacesini implement edip ilgili metodu override etmiş durumdalar. 

Bu özellik bize SearchEngine1 class'ının setter metoduna verilen algoritma tipine göre search yeteneği kazandırmış oldu. Bu sayede esnelik kazandık. İleride başka bir search algoritması üretilip uygulanmak istenirse SearchEngine1 içinde hiçbir değişiklik yapılmadan uygulanabilir.


Şimdi sıra geldi Spring'te bunu uygulamaya. Spring'te injection özelliği xml üzerinden yapılmaktadır.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.com/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                      http://www.springframework.org/schema/context
                      http://www.springframework.org/schema/context/spring-context-3.2.xsd">
                            
    <bean id="binarySearch" class="com.serdarkocerr.spring.search.BinarySearch" />
    <bean id="sequentialSearch" class="com.serdarkocerr.spring.search.SequentialSearch" />
     
    <bean id="searchEngineV1" class="com.serdarkocerr.spring.search.SearchEngine1" >
        <property name="searchAlgorithm" ref="binarySearch" /> <!-- Setter Injection -->
    </bean> 
 
</beans> 
Yukarıdaki xml konfigürasyon dosyasını inceleyelim. Bütün class'lar bean olarak tanılmış. 

SearchEngine1 class'ı içerisinde deklare edilen 

private Search searchAlgorithm 

nesnesine bir atama yapılmalıdır. Yoksa nullPointerException hatası olur. Bu kısımda Spring'in bize sunduğu injection özelliği devreye girmektedir. Injection xml dosyasından direk yapılabilmektedir. Bu sayede bir nesne üretip setter fonksiyonuna gidip bir bir nesne üretip ardından  nesneyi parametre olarak vermiyoruz. Hiç böyle kodlamalara girmeden Xml üzerinden rahat bir şekilde yapıyoruz. 

searchAlgorithm  interface nesnesine referans verilirken interface nesnesi gerekmektedir. Bean olarak tanıtılmış olan binarySearch ve sequentialSearch class'ları bu interfaceyi implement ettiğinden referans olarak verilebilir. Bu kısım bize Dependency Injection'ı sağlar.  Çünkü referans olarak bu interfaceyi implement etmiş her nesneyi verebiliriz. Bize  esneklik sağlar. İleride üretilecek başka search algoritmaları için searchEngine1 içinde değişiklik yapılmadan  uygulanabilirlik sağlanmış olur.


Setter injection da ilgili interface nesnesi için setter metodu yazılması gerekiyor. Bu setter metounun beklediği nesnenin ismi (Search searchAlgorithm)  ile referans olarak verilen isim name="searchAlgorithm" aynı olmalıdır. Yani hangi isme karşılık referans olarak bu bean veriliyorun cevabı gibi düşünülebilir.

Sonuç olarak setter fonksiyonuna verilen parametrenin ismi ise bean'de verilen referansın ismi aynı olmalıdır.


2.Constructor Injection

Bu injection tipinde constructorlar üzerinden dependency injection uygulanır.



import java.util.List;
 
public class SearchEngine2
{
    private Search searchAlgorithm;
     
    public SearchEngine2( Search searchAlgorithm )
    {
        this.searchAlgorithm = searchAlgorithm;
    }
     
    public boolean search( List<Integer> list, int id )
    {
        return searchAlgorithm.find(list, id);
    }
} 



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.com/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                      http://www.springframework.org/schema/context
                      http://www.springframework.org/schema/context/spring-context-3.2.xsd">
                            
    <bean id="binarySearch" class="com.serdarkocerr.spring.search.BinarySearch" />
    <bean id="sequentialSearch" class="com.serdarkocerr.spring.search.SequentialSearch" />
     
     
    <bean id="searchEngineV2" class="com.serdarkocerr.spring.search.SearchEngine2" >
          <constructor-arg name="searchAlgorithm" ref="sequentialSearch" />
    </bean>
 
</beans> 

SearchEngine2 class'ına dikkat edilirse constructor içine bir Search interface tipinde nesne alıyor. 

.xml dosyasını incelersek SearchEngine2 beanı tanımlanırken constructor üzerinden bir referans verilmiş. Bu referans  bean id olarak verilir. sequentialSearch bean id'yi temsil eden SequentialSearch class'ı ise Search interfacesini implement etmişti yani referans olarak verilebilir. Başka bir search algoritması da eğer Search interfacesini implement ettiyse constructor'a  parametre olarak geçilebilir. Bu sayede büyük bir esneklik kazanmış oluyoruz. Dependency Injection bu şekilde sağlanmış oluyor.




Örneklerimizde hem Setter hem de Constructor Injection özelliklerini kullanarak Dependency Injection mimarisini başarı bir şekilde uygulamış olduk. Spring bize injection bakımından çok fazla kodlama yapmamız konusunda yardımcı oluyor.

Aşağıdaki constructor'ı incelersek

Search tipinde bir interface constructor'a parametre olarak verilmeli.
İsmine dikkat edersek  nesnenin ismi searchAlgorithm


    public SearchEngine2( Search searchAlgorithm )
    {
        this.searchAlgorithm = searchAlgorithm;
    }
     
Daha sonra aşağıdaki bean tanımlamasını incelersek,

constructor 'a bir referans geçilmeli. Bu referans geçilecek nesnenin ismi searchAlgorithm olduğunu görmekteyiz.


    <bean id="searchEngineV2" class="com.serdarkocerr.spring.search.SearchEngine2" >
          <constructor-arg name="searchAlgorithm" ref="sequentialSearch" />
    </bean>

Yani sonuç olarak constructor'a tanıtılan parametre isimleri hep aynı olmalıdır.





Kaynaklar:

http://www.kazimsoylu.com/java/spring-framework-ile-dependency-injection.html




Hiç yorum yok:

Yorum Gönder