Spring Framework Dersleri 3 - Setter And Constructor Injection
Merhabalar.
Önceki yazıda IoC (Inversion of Control) ve DI (Dependency Injection) konularını öğrenmiştik. Bu yazıda Spring Framework içinde bağımlılık ihtiyacının çözümü için kullanılan yöntemlerden olan Setter Ve Constructor Injection konularını öğreneceğiz.
Setter Injection
Spring’e Giriş yazısında yaptığımız Hello World uygulaması aslında Setter Injection’a bir örnekti ama burada konuya bir kez daha çalışacağız.
src/main/resources altındaki beans.xml dosyamız şöyle idi:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="provider"
class="com.ilkaygunel.springtutorials.helloworld.MessageProviderImplementation" />
<bean id="renderer"
class="com.ilkaygunel.springtutorials.helloworld.MessageRendererImplementation"
p:messageProvider-ref="provider" />
</beans>
Biz bu XML dosyasında renderer id’li MessageRendererImplementation sınıfının messageProvider alanı için provider id’li MessageProviderImplementation sınıfını referans göstermiştik. Yani biz bağımlılığa ihtiyaç duyup main kodu içinden getBean() metodu ile renderer id’li MessageRendererImplementation sınıfından bir nesne elde ettiğimizde Spring bizim için messageProvider alanını MessageRendererImplementation sınıfı içindeki setMessageProvider() metodu ile dolduracak.
public void setMessageProvider(MessageProvider provider) {
this.messageProvider = provider;
}
Yukarıdaki XML kodumuzda p tagi yerine bean taginin içinde provider tagini de kullanabilirdik. O zaman kodumuz şöyle olurdu:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="provider"
class="com.ilkaygunel.springtutorials.helloworld.MessageProviderImplementation" />
<bean id="renderer"
class="com.ilkaygunel.springtutorials.helloworld.MessageRendererImplementation">
<property name="messageProvider" ref="provider"></property>
</bean>
</beans>
Önceki yazıda context:component-scan tag’inden bahsetmiştim. XML içine koyulacak bu tag sizin Spring uygulamanızda notasyon kullanmak sureti ile bağımlılıklarınızı çözümlemenizi sağlar. Setter Injection’ı da dilerseniz yukarıdaki gibi XML vasıtası ile değil de notasyon vasıtası ile kullanabilirsiniz. Bunun için öncelikle XML dosyasına context:component-scan tag’i eklenmeli. XML dosyasını şöyle güncelleyebiliriz:
app-context-annotation.xml
<?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.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.ilkaygunel.springtutorials.setterconstructorinjection" />
</beans>
Az önce de belirttiğim gibi context:component-scan tag’i Spring’in notasyonlarının kullanıma açılması için gereklidir. Bu tag’in base-package özelliğine verilen değer hangi paket altındaki sınıflar için bu izni açayım manasındadır. Biz de com.ilkaygunel.springtutorials.setterconstructorinjection paketi altı için bu notasyon kullanımını aç dedik.
Şimdi MessageProviderImplementation ve MessageRendererImplementation sınıfları üzerinde notasyon güncellemesi yapacağız. Kodlarımızı şöyle güncelleyeceğiz:
MessageProviderImplementation.java
MessageProviderImplementation sınıfı üzerinde sadece sınıfı @Component notasyonu ile işaretleme işlemini yapıyoruz. Bu notasyon Spring’in DI container’ına bu sınıfı kayıt eder ve Spring içindeki birçok notasyonun türetildiği notasyondur.
MessageRendererImplementation.java
MessageProviderImplementation sınıfını işaretlediğimiz gibi MessageRendererImplementation sınıfını da @Component notasyonu ile işaretleyip Spring’e kayıt ettiriyoruz. Bu kayıt sırasında @Component notasyonunda renderer parametresini vererek renderer id bilgisi ile kayıt et diyoruz. Sınıf içerisinde dikaktinizi setMessageProvider() metoduna çekmek istiyorum. Bu metot @Autowired notasyonu ile işaretlenmiş. Bu şu anlama geliyor ki Spring’in ApplicationContext’i initialize olduğunda Spring bu bağımlılığı enjekte edecek.
package com.ilkaygunel.springtutorials.setterconstructorinjection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("renderer")
public class MessageRendererImplementation implements MessageRenderer {
private MessageProvider messageProvider;
@Override
public void render() {
if (messageProvider == null) {
throw new RuntimeException(
"You must set the property messageProvider of class:"
+ MessageRendererImplementation.class.getName());
}
System.out.println(messageProvider.getMessage());
}
@Override
@Autowired
public void setMessageProvider(MessageProvider provider) {
this.messageProvider = provider;
}
@Override
public MessageProvider getMessageProvider() {
return messageProvider;
}
}
HelloWorldWithDI.java
main() metodumuzun yer aldığı sınıfımız içerisinde konfigürasyon dosyamız olan app-context-annotation.xml’i ilk dersimizde yaptığımız gibi yüklüyoruz. Akabinde MessageRendererImplementation sınıfı tipinde nesneyi classPathXmlApplicationContext nesnesinin getBean() metodu vasıtası ile elde ediyoruz. Dikkat etmemiz gereken nokta renderer bean’i XML içinde kayıtlı değil. Notasyon vasıtası ile Spring’in DI mekanizmasına kayıtlı ve oluştururken de @Component notasyonune verdiğimiz parametre ile kullanıyoruz.
package com.ilkaygunel.springtutorials.setterconstructorinjection;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloWorldWithDI {
public static void main(String[] args) {
try (ClassPathXmlApplicationContext classPathXmlApplicationContext
= new ClassPathXmlApplicationContext("app-context-annotation.xml")) {
MessageRenderer mr = classPathXmlApplicationContext.getBean("renderer",
MessageRendererImplementation.class);
mr.render();
} catch (Exception e) {
System.err.println("Bir Hata Meydana Geldi! Hata:" + e);
}
}
}
Konsol Çıktısı Konsol çıktısı aynı şekilde çalıştı.
Nov 06, 2016 4:51:45 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2e817b38:
startup date [Sun Nov 06 16:51:45 EET 2016]; root of context hierarchy
Nov 06, 2016 4:51:45 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context-annotation.xml]
Hello Spring World :)
Nov 06, 2016 4:51:46 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@2e817b38:
startup date [Sun Nov 06 16:51:45 EET 2016]; root of context hierarchy
Constructor Injection
MessageProviderImplementation sınıfı içerisinde String’i biz elle yazmıştık ve döndürmüştük. Constructor Injection örneğimizde de bu String’in constructor vasıtası ile enjeksiyonunu göreceğiz.
Öncelikle MessageProviderImplementation sınıfı şu şekilde güncelleyelim:
package com.ilkaygunel.springtutorials.setterconstructorinjection;
import org.springframework.stereotype.Component;
@Component
public class MessageProviderImplementation implements MessageProvider{
private String message;
public MessageProviderImplementation(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}
Kodda gördüğünüz gibi bu sınıf artık bir yerlerden yapılandırıcısına message parameteresinin gelmesi sureti ile bir nesne üretebilecektir. Şimdi biz de bu message bağımlılığını XML dosyasından gidererek işlemi çözelim.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="provider"
class="com.ilkaygunel.springtutorials.setterconstructorinjection.MessageProviderImplementation" >
<constructor-arg value="Constructor'a Yollanan Mesaj!" />
</bean>
<bean id="renderer"
class="com.ilkaygunel.springtutorials.setterconstructorinjection.MessageRendererImplementation">
<property name="messageProvider" ref="provider"></property>
</bean>
</beans>
MessageProviderImplementation sınıfını yine aynı şekilde kaydediyoruz. bean tag’inin içinde constructor-arg notasyonunu kullandığımız zaman ilgili sınıfın yapılandırıcısına value özelliğinde atanmış değer gönderilecek.
Java sınıflarımızda ise herhangi bir notasyon vs. olmayacak ve saf hallerinde duracaklar.
Uygulamayı çalıştırdığımızda konsol çıktımız şu şekilde olacak:
Nov 06, 2016 5:16:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2e817b38:
startup date [Sun Nov 06 17:16:09 EET 2016]; root of context hierarchy
Nov 06, 2016 5:16:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [constructor-injection.xml]
Constructor'a Yollanan Mesaj!
Nov 06, 2016 5:16:09 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@2e817b38:
startup date [Sun Nov 06 17:16:09 EET 2016]; root of context hierarchy
Hatırlayacağımız gibi XML içinde p tag’ini kullanmıştık. Constructor Injection konusunda da c tag’ini kullanabiliriz. Eğer onu kullanmak istersek XML dosyamızı şöyle güncellememiz gerekir:
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="provider"
class="com.ilkaygunel.springtutorials.setterconstructorinjection.MessageProviderImplementation"
c:message="Constructor'a Yollanan Mesaj!"/>
<bean id="renderer"
class="com.ilkaygunel.springtutorials.setterconstructorinjection.MessageRendererImplementation">
<property name="messageProvider" ref="provider"></property>
</bean>
</beans>
Peki bu Constructor Injection meselesini notasyonlara yıkmak istersek ne yapmamız gerekecek? Hemen inceleyelim.
Öncelikle MessageRendererImplementation ve MessageProviderImplementation sınıfını az önceki @Component’li ve @Autowired’lı haline geri getirelim. Akabinde MessageProviderImplementation sınıfını şu şekilde güncelleyelim:
package com.ilkaygunel.springtutorials.setterconstructorinjection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MessageProviderImplementation implements MessageProvider{
private String message;
@Autowired
public MessageProviderImplementation(@Value("Constructor'a Yollanan Mesaj!") String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}
Dikkat edeceğimiz gibi MessageProviderImplementation sınıfın yapılandırıcısı @Autowired notasyonu ile işaretli. Yani Spring DI çalıştığından bu sınıftaki message nesnesi yapılandırıcıdaki parametreyi geçirecek. Uygulamayı çalıştırırken de app-context-annotation.xml yüklememiz durumunda başarılı bir şekilde çalışacak.
İstenmesi durumunda yine @Autowired kullanılarak constructor’a veri XML’den geçirilebilir. Bunun için de XML içinde yine c tag’i
tanımlı olmak üzere bir bean tanımlanması gerekir:
@Autowired
public MessageProviderImplementation(String message) {
this.message = message;
}
Uygulamayı çalıştırdığımızda Spring DI XML’den bu String’i constructor’a geçirecek.
Bu örnek için projede ne kadar yapılandırıcısı @Autowired ile işaretlenmiş ve String parametre alan sınıf varsa hepsi Constructor’a Yollanan Mesaj! String’ini yüklenirler. Örneğimiz basitize olması açısından bu şekilde oldu. Gerçek projelerimizde olay tabii ki daha uniqe olacaktır.
Bu yazıda anlatacaklarım bu kadar arkadaşlar. Gelecek yazıda görüşene kadar sağlıcakla kalın.
Selam ve Sevgilerimle