Web uygulamalarında nesnelerinizin mutlaka Spring managed bean olması gerekmez. Herhangi bir biçimde ServletContext’e erişebiliyorsanız, Spring ApplicationContext’e de ulaşıp, istediğiniz Spring managed bean’ı kullanabilirsiniz. Ancak yine de Spring’in sağladığı başta dependency injection olmak üzere pek çok diğer kolaylıktan faydalanmak için uygulamalarınızdaki nesneleri mümkün olduğunca Spring’in yönetimine devretmek en akıllıca iş olacaktır.
Ancak web uygulamalarında zaman zaman öyle nesneler karşımıza çıkmaktadır ki, bunların Spring tarafından oluşturulması ve dolayısı ile yönetilmesi söz konusu olamamaktadır. Örneğin, web.xml içerisinde tanımlarını yaptığımız Filter, HttpServlet ve HttpSessionListener nesnelerimiz bunlardan birkaçıdır. Bu nesnelerin çoğu zaman Spring yönetimindeki diğer nesnelere erişip, bunları kullanmaları gerekmektedir. Elbette Spring’in WebApplicationContextUtils sınıfı yardımı ile istediğimiz Spring bean’ını alıp kullanabiliriz, ancak tanımladığımız bu Filter, HttpServlet ve HttpSessionListener nesnelerini de Spring’in yönetimine dahil edebilirsek hem Spring’in DI imkanlarından faydalanmış olacağız, hem de bu nesnelerin entegrasyon birim testlerini yapmamız çok daha kolay olacak.
Spring Application Framework içerisinde doğrudan bu tür nesneleri Spring managed bean yapmak için yapılar söz konusu olmasa da her biri için utility sınıfları Spring topluluğu içerisinde geliştirilmiştir ve yaygın biçimde kullanılmaktadır. Örneğin, Acegi Security Framework bu ihtiyacı çok önceden tespit edip FilterToBeanProxy isimli yardımcı bir sınıfı API’sine dahil etmiştir. Bu sınıf vasıtası ile web.xml’deki tanımlı Filter’a gelen çağrılar ilgili Spring managed bean’a yönlendirilmektedir. Örneğin;
<filter> <filter-name>BypassAcegiAuthenticationFilter</filter-name> <filter-class> org.acegisecurity.util.FilterToBeanProxy </filter-class> <init-param> <param-name>targetBean</param-name> <param-value>bypassAcegiAuthenticationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>BypassAcegiAuthenticationFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
BypassAcegiAuthenticationFilter tanımı FilterToBeanProxy sınıfından oluşturulmuş bir Filter’dır. FilterToBeanProxy nesneleri kendilerine gelen çağrıları targetBean ile belirtilmiş spring managed bean’a yönlendirmektedir.
<bean id="bypassAcegiAuthenticationFilter" class="com.ems.base.web.filters.BypassAcegiAuthenticationFilter"> <property name="bypassEnabled"> <value>true</value> </property> <property name="username"> <value>ksevindik</value> </property> <property name="roles"> <value>ROLE_EDITOR</value> </property> </bean>
Yukarıda da görüldüğü gibi bypassAcegiAuthenticationFilter normal bir POJO olarak Spring ApplicationContext içerisinde tanımlanmıştır.
Diğer bir utility sınıf ise ServletToBeanProxy’dir. Acegi Security Framework’ün FilterToBeanProxy yardımcı sınıfından esinlenerek geliştirilen bu sınıf da ana Spring dağıtımında bulunmamaktadır. İlk olarak http://forum.springframework.org/showthread.php?t=11994 forum yazışmalarında bahsi geçmiştir. Elimizdeki herhangi bir HttpServlet nesnesini Spring managed yaparak bu nesnenin DI kabiliyetlerinden yararlanması hedeflenmiştir. Örneğin;
<servlet> <servlet-name>ReportGeneratorServlet</servlet-name> <servlet-class>com.ems.base.web.servlets.ServletToBeanProxy</servlet-class> <init-param> <param-name>targetBean</param-name> <param-value>reportGeneratorServlet</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ReportGeneratorServlet</servlet-name> <url-pattern>/reportGeneratorServlet</url-pattern> </servlet-mapping>
ReportGeneratorServlet tanımında sınıf olarak ServletToBeanProxy kullanılmıştır. /reportGeneratorServlet URL’ine gelen request’ler ServletToBeanProxy tarafından targetBean ile belirtilmiş Spring managed bean’a yönlendirilmektedir. Aşağıda da reportGeneratorServlet Spring managed bean’ın ApplicationContext içerisindeki tanımını görmekteyiz. Bu örnekten de rahatlıkla anlaşılacağı gibi ihtiyaç duyulan bütün harici nesneler DI vasıtası ile herhangi bir ilave efor sarf etmeden ilgili nesneye sağlanmaktadır.
<bean id="reportGeneratorServlet" class="com.ems.base.web.servlets.ReportGeneratorServlet"> <property name="reportExportManager"> <ref local="reportExportManager" /> </property> <property name="reportPrintManager"> <ref local="reportPrintManager" /> </property> <property name="reportOutputTransformChain"> <ref local="reportOutputTransformChain" /> </property> <property name="reportDefinitionMap"> <ref bean="reportDefinitionMap" /> </property> <property name="defaultReportGenerator"> <ref local="defaultSqlBasedReportGenerator" /> </property> </bean>
Son olarak WebApplicationContextSessionListener yardımcı sınıfından bahsetmek istiyorum. Eğer web.xml içerisinde tanımladığımız HttpSessionListener nesnelerini Spring managed yapmak istersek bu sınıftan faydalanabiliriz. http://opensource.atlassian.com/projects/spring/browse/SPR-437 de bahsi geçtiği gibi bu sınıf Interface21 ekibinden Alef Arendsen tarafından kodlanmış ve sandbox’a konmuştur. Hali hazırda Servlet API delegation model’in Spring 2.5’de yeniden gözden geçilmesi nedeni ile Spring core’a eklenmemiştir. Ancak yine de şu an için ihtiyaç duyanlar kendi kaynak kodlarına ekleyerek bu sınıfı rahatlıkla kullanabilir.
WebApplicationContextSessionListener sessionCreated ve sessionDestroyed çağrılarını HttpSessionListener arayüzünü implement etmiş Spring managed bean’lara yönlendirmektedir. web.xml içerisinde aşağıdaki tanımı yapmak yeterlidir.
<listener> <listener-class>org.springframework.web.servlet.support.WebApplicationContextSessionListener</listener-class> </listener>
Bu sayede diğer bütün HttpSessionListener nesnelerimizi Spring ApplicationContext içerisinde tanımlanabilir. Web uygulaması çalışırken yeni bir oturum oluşturulduğu vakit, WebApplicationContextSessionListener nesnesi ApplicationContext içindeki bütün HttpSessionListener nesnelerinin sessionCreated metodunu çağırır. Yine aynı şekilde oturum sonlandırıldığı vakit de sessionDestroyed metodu çağırılır.
Sonuç olarak kurumsal web uygulamalarımızdaki nesneleri ne kadar POJO’lar olarak tasarlayıp, Spring tarafından yönetilebilir hale getirebilirsek, sistemimiz de o kadar rahat geliştirilebilir, entegrasyon testleri daha kolay ve kapsamlı olarak yazılabilir hale gelecektir. Herkese bol Spring’li günler…