Professional Documents
Culture Documents
Spring MVC Uygulama Catisi OSUNERCAN WwwJavaDiliCom
Spring MVC Uygulama Catisi OSUNERCAN WwwJavaDiliCom
Ömer SUNERCAN
omersunercan.cs@gmail.com
2006, Ankara
İçindekiler
Giriş......................................................................................................................................................3
Belge İçeriği Hakkında....................................................................................................................3
Spring MVC..........................................................................................................................................3
Geliştirme Ortamının Hazırlanması.................................................................................................3
Kurulum......................................................................................................................................3
Uygulama Kılavuz Yapısının Oluşturulması...............................................................................4
Başlangıç (DispatcherServlet).........................................................................................................4
İlk Spring MVC Örneği...................................................................................................................5
Uygulamaya Başlıyoruz.................................................................................................................11
Ana Sayfa.......................................................................................................................................13
Form Kullanımı..............................................................................................................................15
Araç Girişi......................................................................................................................................17
Doğrulama (Validation).................................................................................................................23
Durdurucular (Interceptors)...........................................................................................................28
Sonuç..................................................................................................................................................31
Terimler Sözlüğü................................................................................................................................32
Kaynaklar............................................................................................................................................32
Giriş
Spring, son dönemde J2EE uygulaması geliştirme alanında popülerlik kazanan ve yaygın olarak kullanılmaya başlanılan
bir uygulama geliştirme çatısıdır (application framework). Uygulama geliştirmeyi ve denetimi zorlaştıran ağır (heavy
weight) çözümlere alternatif olarak hafif (lightweight) bir yapı sunarken, aynı zamanda esnek ve modüler bir şekilde bir
çok özelliği içerisinde barındırması Spring'e gösterilen ilgide önemli rol oynar.
Spring, birbirinden bağımsız ve ihtiyaca göre kullanımına karar verilebilecek bir çok farklı özellik içerir. Bu özellikler
çatıyı oluşturan yedi farklı birim tarafından sağlanır. Bu belge bunlardan Web MVC (Model View Controller / Model
Görüntüm Denetim) biriminin kullanımını örneklemek amacı ile hazırlanmış olduğu için bu birimlerin tek tek ele
alınmasına yer verilmeyecektir.
Spring temel olarak, bileşenleri XML yapılandırma kütükleri aracılığıyla bütünleştirmeye dayalı bir yapı sunar. Bu
yaklaşım esas olarak Denetim Çevrimi (Inversion of Control), diğer bir adıyla Bağımlılık İletimi (Dependency Injection),
tasarım örüntüsünü kullanır. Buna göre, bileşenler arasındaki bağımlılıklar bileşenlerin kendileri yerine Spring
tarafından ele alınır.
Spring bileşenleri bir araya getirmek ve biçimlendirmek için Java çekirdeklerini (JavaBean) kullanır. Sınıfları kodlanan
bu çekirdeklere dair isimlendirmelerin yapılması, gerekli ilk değer atamaları ve diğer çekirdeklerle aralarındaki
bağımlılıkların tanımlanması XML yapılandırma kütüklerinin içerisinde yapılır. Spring, bu kütükleri okur ve buradaki
tanımları kullanarak çekirdek olgularını gerekli yapılandırmaları gerçekleştirerek oluşturur. Bu işleyiş sayesinde
uygulamadaki bileşenlerin yapılandırılması ve kullanımı kodun içine girmeye gerek kalmadan, yapılandırma kütükleri
aracılığıyla son derece esnek ve kolay bir biçimde gerçekleştirilir. İleride ele alacağımız Web MVC birimi örneklerinde
bunu çok daha somut şekilde görme ve inceleme olanağı bulacağız.
Belge İçeriği Hakkında...
Bu belgede yoğun bir şekilde bilgiye yer vermek yerine, Spring MVC ile başlangıç düzeyinde basit bir uygulamanın
geliştirilmesi örneklenmeye çalışılacaktır. Özellikle başlangıç aşamasında kavranılmakta zorlanılabilen kavramlara ve
geliştirme sürecinde ortaya çıkabilen temel sorunlara yönelik çözümlere yer verilecektir.
Bu belgede anlatılanların iyi kavranılabilmesi için temel J2EE bilgisine (JSP/Servlet, JSTL, JSP EL) yeteri kadar sahip
olunması gerekmektedir. Spring MVC çatısının kullanımı tamamen giriş düzeyinde ele alınmış olup, bazı noktalarda
atıflar bulunsa da okuyucunun Struts vb. bir çatıyı daha önce kullanmış veya biliyor olması gereksinimi yoktur.
Belge içinde verilecek olan kodların tamamı çalıştırılarak denenmiş kodlar olmalarının yanısıra, okuyucuya kolaylık
olması amacıyla, tekrar tekrar verilen kaynak (java) ve yapılandırma (xml) kütüklerinin içieriklerinin sadece
güncellenen kısımları değil, her her seferinde tamamı verilecektir.
Son olarak, MVC'nin Spring'in bir alt bölümü olduğundan hareketle metnin içerisinde bir çok yerde Spring MVC yerine
sadece Spring ifadesine yer verilecektir, bunun bir kavram kargaşasına yer açmaması için hatırda tutulmasında fayda
vardır.
Spring MVC
Bu bölümde Spring MVC çatısının kullanımını baştan sona adım adım ilerleyerek geliştireceğimiz bir uygulama ile
örnekleyeceğiz.
Geliştirme Ortamının Hazırlanması
Kurulum
Örnek uygulamamızı gerçekleştirmeye uygun bir çalışma ortamı hazırlayarak başlayacağız.
Spring MVC ile bir J2EE uygulaması geliştirmeye başlamak için öncelikli ihtiyaçlarımız bir Java SDK'sı ve bir Servlet
Kozası (Servlet Container) 'dır. Örnek uygulamamız jdk1.5.0 Java SDK'sı ve koza olarak da Apache Tomcat 5.5.9
üzerinde çalışacaktır.
Kullanılabilecek değişik araçlardan bağımsız olması için derleme, yükleme vb. amacıyla herhangi bir araç
kullanmayacağız. Siz isteğinize bağlı olarak Ant gibi bir araç veya seçtiğiniz bir IDE kullanarak bu işlemleri alışagelmiş
olduğunuz bir şekilde gerçekleştirebilirsiniz. Bu nedenle bu işlemlere dair ayrıntılara yer verilmeyecektir.
Yukarıdaki kısım herhangi bir J2EE uygulaması için gerekli olan ortama ilişkin ihtiyaçlardı. Spring MVC'yi
kullanabilmek için www.springframework.org/download.html adresinden Spring çatısını indirmeniz gerekmektedir.
Burada karşınıza iki seçenek çıkacaktır:
springframework<sürüm_no>.zip
springframework<sürüm_no>withdependencies.zip
İlk seçenek sadece Spring çatısını içerirken, ikinci seçenekle birlikte J2EE uygulaması geliştirmek için gereken diğer bir
çok arşiv de gelir. Uygulamamızda springframework1.2.5withdependencies.zip kurulumu kullanılacak.
Spring çatısını indirdikten sonra istediğiniz bir yere açtığınızda kurulum tamamlanmıştır. Uygulamamızı geliştirme
aşamasında gerektikçe buradan Spring MVC çatısına ilişkin arşivleri uygulamamızın içine dahil edeceğiz.
Uygulama Kılavuz Yapısının Oluşturulması
Uygulamamızı geliştirmeye gerekli kılavuzları oluşturarak başlayabiliriz. Uygulamamızın adı springornek olsun, bu
durumda uygulamamız için kök kılavuzun adı da springornek olmalıdır. Öncelikle bir J2EE uygulaması için standart
olan WEBINF kılavuzunu ve web.xml kütüğünü oluşturmamız gereklidir.
<web-app version="2.4">
</web-app>
Başlangıç (DispatcherServlet)
Spring MVC, Spring çatısı içerisinde MVC (Model View Controller / Model Görünüm Denetim) mimarisinin
gerçekleştirildiği birimdir. Bilindiği gibi MVC bir uygulamada kullanıcıya sunulan görüntü (view), veri (model) ve
kullanıcıdan gelen taleplere karşılık olarak veriler üzerinde işlemleri gerçekleştiren denetim (controller) bölümlerini
birbirinden ayırmayı öngören bir yaklaşımdır. Bu yaklaşımın J2EE uygulamalarındaki esaslarından birisi Ön Denetimci
Servlet (Front Controller Servlet)'in gelen tüm HTTP isteklerini (request) karşılaması ve bu isteği tanımlanan eşlemelere
(mapping) göre değerlendirip yanıtı (response) üretecek olan işleyicilere (handler) yönlendirmesidir.
Spring MVC'de Ön Denetimci (Front Controller) Servlet olarak DispatcherServlet hizmet verir. Spring MVC'de
işleyicilere karşılık olarak da Controller arayüzünü (interface) uygulayan (implement) sınıflardır.
Artık Spring MVC'yi uygulamamızın içine dahil etmenin zamanı geldi. Bunun için öncelikle Spring kurulumunuzun
içindeki spring.jar (springframework1.2.5/dist/spring.jar) arşiv kütüğünü WEBINF/lib kılavuzunun içine kopyalayın.
Spring kütüphanesini uygulamaya dahil ettiğimize göre artık Spring'e özel işlemler yapmaya başlayabiliriz. Yukarıda
Spring'de Ön Denetimci olarak DispatcherServlet Servlet'inin işlev gördüğünden söz etmiştik. Bu nedenle ilk
adım olarak uygulamaya gelen tüm istekleri bu Servlet'e yönlendirmeli ve böylece uygulamanın denetimini tamamen
Spring'e bırakmalıyız. Bunu gerçekleştirebilmek için web.xml kütüğüne bir Servlet tanımı eklememiz gerekiyor (Kod
2.2).
Görüldüğü üzere uygulamamızın adı olan springornek adı ile bir Servlet tanımı ekliyoruz ve Servlet sınıfı olarak da
DispatcherServlet'i belirtiyoruz. Ayrıca htm ile sonlanan tüm url'lerin bu Servlet'e yönlendirilmesi gerektiğini
belirtiyoruz. Buna göre bu uygulamaya gelen (<sunucu>/springornek ile başlayan) ve htm ile sona eren url'ler Spring'in
DispatcherServlet'i tarafından değerlendirilecek ve buna isteğe buna göre cevap verilecektir.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4">
<servlet>
<servlet-name>springornek</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springornek</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
<beans>
</beans>
İlk Spring MVC Örneği
Spring MVC kullanarak hazırlayacağımız bir sayfa için temel olarak iki kütüğe ihtiyacımız vardır. Bir görünüm (view)
ve bir denetimci (controller). Spring MVC birden fazla görünüm teknolojisine destek vermektedir (JSP/JSTL, Tiles,
Velocity, FreeMaker vs.). Bizim uygulamamızdaki tüm görünümler JSP (ve JSTL) ile oluşturulacaktır. Bu nedenle
görünüm kelimesinin geçtiği noktalarda bir JSP sayfasından bahsediyor olacağız.
Denetimciler (controller) daha önce söz edildiği üzere bir HTTP isteğini (request) değerlendiren ve buna göre bir yanıt
(response) üretip, bu sonuca uygun bir sayfaya yönlendirme işini yapan sınıflardır. Spring MVC, isteklerin türüne göre
en uygun ve en kolay kodlamayı sağlayabilmek için farklı denetim sınıfları içermektedir. Yani örneğin sadece metin ve
bir takım bağlantılardan (link) oluşan bir sayfa için farklı, bir submit olayı sonucu üretilen ve form verisi taşıyan bir istek
için farklı ve daha uygun bir sınıftan türetilen denetimciler kullanılabilir.
Denetimci sınıfların en üst düzeydeki atası Controller arayüzüdür. Struts kullanmış olanlar için bunun Action'a
karşılık geldiğini söyleyebiliriz.
RequestUtils
org.springframework.web.bind.RequestUtils sınıfı, HttpServletRequest bir nesnesinin
parametrelerine parametrenin türünü belirterek erişim sağlayan static yöntemler içerir. Parametrelere
HttpServletRequest nesnesinin üzerinden doğrudan erişmek yerine bu sınıfı kullanmak tavsiye edilen bir
yöntemdir. Örnekler:
String str = RequestUtils.getStringParameter(request, "string_param");
int i = RequestUtils.getIntParameter(request, "int_param", 0);
Integer i = RequestUtils.getIntParameter(request, "integer_param");
Şu ana kadar açıkladıklarımıza dayanarak bir isteğin hangi aşamalardan geçerek bir yanıta dönüştüğünü sıralarsak, istek
ilk önce DispatcherServlet tarafından ele alınır, bu Servlet ileride açıklayacağımız eşleme (mapping)
yapılandırmasını kullanarak isteği bir denetimciye yönlendirir. Denetimci isteğe dair yapılması gereken işlemleri
gerçekleştirdikten sonra bir ModelAndView nesnesi döndürür. Bu nesne hangi hangi sayfanın gösterileceği bilgisinin
yanı sıra sunulacak veriye de sahiptir ve bunlardan üretilen bir yanıt (response) kullanıcıya döndürülür.
Denetimciler ve diğer ayrıntılara girmeden önce artık “Merhaba Dünya!” mesajını gösteren bir ana sayfa yapmaya
geçmeliyiz.
Öncelikle sitenin girişi niteliğide olan bir index.jsp hazırlamalıyız. Bir J2EE web uygulamasında sitenin açılış sayfası
(ana sayfası) web.xml kütüğüne welcomefile etiketi içerisnde verilir ve burada verilen sayfanın açılışta görüntülenmesi
için kullanıcıların erişimine açık fiziksel bir kütüğe işaret ediyor olması gerekir. Bu nedenle index.jsp sayfasını WEB
INF/jsp kılavuzuna değil doğrudan uygulamanın kök kılavuzuna koymalıyız (springornek/index.jsp).
<web-app version="2.4">
<servlet>
<servlet-name>springornek</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springornek</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>Merhaba Dünya</title>
</head>
<body>
<h1>Merhaba Dünya!</h1>
</body>
</html>
<beans>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
package denetim;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
<beans>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>springornek</title>
</head>
<body>
<h1>Hoşgeldiniz!</h1>
Örnek Spring MVC uygulaması.
</body>
</html>
<beans>
<bean id="urlEsleme"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index.htm">basitDenetimci</prop>
</props>
</property>
</bean>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
Resim 2.1 Başlangıç Sayfası
ModelAndView nesnesinin veri (model) kısmı bir harita mantığı ile çalışır. BasitDenetimci'yi biraz
değiştirerek görünüme bilgi göndermesini sağlayalım (Kod 2.13).
ModelAndView'e iki biçimde veri ekleyebildiğimizi görüyoruz. Bunlardan ilki ModelAndView nesnesinin
yapılandırıcısına görünümün ardından anahtar kelime (bir String) ve veriyi (bir Object) parametre olarak vermektir.
İkincisi ise benzer şekilde addObject yöntemini kullanmaktır. Bu yöntem kullanılarak ModelAndView nesnesine
istenildiği kadar veri eklenilebilir.
package denetim;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>springornek</title>
</head>
<body>
<h1>Hoşgeldiniz!</h1>
Örnek Spring MVC uygulaması.<br><br><br>
<em><strong>Veriler:</strong></em> <br><br>
Mesaj: <em>${mesaj}</em> <br>
Bir sayı: <em>${birSayi}</em> <br>
Tarih: <em>${tarih}</em>
</body>
</html>
Resim 2.2 Denetmicinin Gönderdiği Veriye Erişim
Uygulamaya Başlıyoruz
Artık Spring MVC kullanarak basit web uygulaması geliştirmek için temel altyapımız hazır durumda olduğuna göre
kendi uygulamamızı inşa etmeye başlayabiliriz. Bu esnada Spring'in form içeren sayfalarda form verilerinin kontrolünü
kolaylaştıran, kullanıcı tarafından girilen verileri doğrulama (validation) işlemini uygulama kodundan ayıran ve
yalınlaştıran, istek denetimciye ulaşmadan önce ve sonra yapılması gereken işlemlerin gerçekleştirilmesine olanak veren
bir yapı (interceptors) sağlayan çeşitli işlevlerini inceleyeceğiz. Bunlar gibi bir çok işleve değinecek olmamıza rağmen
takip etmeyi kolaylaştırmak ve karışıklığa sebep olmamak için olabildiğince az sayıda sayfaya yer vereceğiz.
Uygulamamız, bir otoparka giriş yapan araçlara otopark gişesinde boş olan bir park yerinin verilmesi (sıradaki yer veya
seçilen boş bir yer) ve park yeri verilen aracın plakasının ve park yerinin kaydedilmesi işlemlerini içerecektir. Önce
sayfaları hazırlarken kullanacağımız, araçlara ve otoparka dair verileri tutan ve bunlara erişim sağlayan iş mantığı
sınıflarını kodlayarak başlayalım (Kod 2.15, 2.16) ve bu sınıfları ismantigi adlı bir paketin içerisinde toplayalım.
package ismantigi;
//plaka alanları
int ilKodu;
String harfKodu;
int aracNo;
package ismantigi;
static {
for(int i = 0; i < ARAC_SAYISI; ++i)
araclar[i] = null;
}
araclar[parkYeri] = arac;
if(ilkBosYer == parkYeri) {
int gecici = ilkBosYer;
do {
ilkBosYer = (ilkBosYer + 1) % ARAC_SAYISI;
if(ilkBosYer == gecici) { //Boş yer yok
ilkBosYer = -1;
break;
}
} while(araclar[ilkBosYer] != null);//dolu Mu?
}
}
public static boolean doluMu(int parkYeri) {
return araclar[parkYeri] != null;
}
Ana Sayfa
Uygulamamız için ilk olarak bir ana sayfa yapalım. Bunu gerçekleştirmek için başlangıç amaçlı yazdığımız denetimci
sınıf ve JSP sayfasına benzer şekilde yeni bir denetimci ve bir JSP sayfası ekleyebiliriz. Ana sayfada hangi park yerinde
hangi aracın bulunduğunu listeleyebiliriz ve yeni araç girişinin yapabileceği sayfaya yönlendiren bir bağlantıya yer
vermemiz gerekir. Amacımızı gerçekleştirebilmemiz için JSP sayfasının araçlar ve park yerleri bilgisine ihtiyacı vardır,
bu nedenle denetimci sınıfın döndürdüğü ModelAndView nesnesinin içine bu bilgiyi eklemesi gerekir (Kod 2.17).
package denetim;
import ismantigi.Otopark;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
<beans>
<bean id="basitDenetimci" class="denetim.BasitDenetimci"/>
<bean id="anasayfaDenetimci" class="denetim.AnasayfaDenetimci"/>
<bean id="urlEsleme"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index.htm">anasayfaDenetimci</prop>
<prop key="/basit.htm">basitDenetimci</prop>
</props>
</property>
</bean>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>springornek</title>
</head>
<body>
Resim 2.3 Ana Sayfa
Form Kullanımı
Daha önce Spring MVC'nin farklı ihtiyaçlara cevap verebilecek nitelikte farklı denetimci sınıf seçenekleri sunduğundan
bahsetmiştik ve özellikle formlar içeren sayfalarda form verilerinin denetimi, değerlendirilmesi ve kullanılması
konusunda kolaylıklar getiren form denetimci sınıfları vurgulamıştık. Uygulamamızda bunlardan çok kullanışlı olan ve
tüm ihtiyaçlarımıza cevap veren org.springframework.web.servlet.mvc.SimpleFormController
sınıfını kullanacağız. Örnekler üzerinde inceleyince daha iyi anlaşılacabilecek olmasına rağmen biraz daha derli toplu
biçimde faydalı olabileceği için aşağıda bu sınıfın kullanımı için önemli olan bazı noktalar hakkında özet bilgi
verilmektedir.
SimpleFormController sınıfı formView ve successView adlı iki nitelik içerir. Bunlara genellikle XML
yapılandırma kütüğündeki denetimci tanımının içerisinde ilk değer ataması yapılır. Bu niteliklerden ilki denetimci
tarafından denetlenecek olan formu içeren sayfaya ilişkin görünümün adıdır. Yani denetimciye yönelik bir istek geldiği
zaman doğrudan, kullanılacak olan formu içeren bu görünüm görüntülenir . İkinci nitelik ise form işlemleri başarılı bir
şekilde sonuçlanırsa yönlendirilecek olan görünümü belirlemek üzere öngörülmüştür. Bu görünüm formView niteliği
örneğinde olduğu gibi otomatik olarak görüntülenmez, ancak geliştirici isterse bu nitelikle belirlenen görünüme
yönlendirme yapar (ModelAndView ile kullanarak). Aslında successView niteliğinin kullanımının tercih edilmesinin
nedeni değer atamasın XML içerisinde yapılabilmesi ve böylece gidilecek görünümün adının kodun içine gömülmesine
gerek kalmamasıdır.
SimpleFormController'ın XML içinde ilklendirilebilen çok önemli iki niteliği daha vardır; commandName ve
commandClass. Bunları belirtmeden önce Spring MVC'deki komut (command) sınıflarının açıklanması gerekiyor.
Spring MVC, bir formda kullanıcının girdiği verilerin doğrudan bir Java nesnesinin (komut nesnesi) nitelikleri ile
ilişkilendirilmesine (bağlama / bind) sağladığı bir takım etiketler (tag) aracılığıyla izin verir ve bu sayede ilgili form
alanının içeriği nesnenin niteliğinin değeri olarak kaydedilir. Komut nesneleri adı verilen bu nesnelerin kullanımı, form
verilerinin istek (request) nesnesinden çıkartılıp başka bir Java nesnesine kaydedilmesi işini geliştiricinin
yükümlülüğünden tamamen çıkartan çok pratik bir işlevdir. Ayrıca bu nesneler çeşitli amaçlara yönelik (örneğin verileri
doğrulama (validation)) işlevlerde de Spring'in sağladığı yöntemler sayesinde doğrudan kullanılabilirler ve bu yapı
geliştiriciye çok önemli kolaylıklar sağlar. Sözünü ettiğimiz commandName ve commandClass nitelikleri de form
tarafından kullanılacak olan komut (command) nesnesinin adı ve sınıfıdır. Bu niteliklere genellikle yapılandırma
kütüğünde ilk değer atamaları yapılır. Eğer commandName niteliğine burada yer verilmezse komut nesnesinin adı
varsayılan olarak command olur ve nesneye görünümün içerisinden isimle erişim sağlanır. Komut nesnesinin sınıfını
belirten commandClass niteliği yapılandırma kütüğünde belirlenmişse ihtiyaç olduğunda otomatik olarak bu sınıftan
boş bir nesne oluşturulur ve kullanılır, buna alternatif bir çözüm ise denetimci sınıf içerisinde daha sonra açıklanacak
olan formBackingObject yöntemininin yeniden yazılmasıdır(override). Komut nesnesi ile ilgili olarak
vurgulanması gereken diğer önemli bir nokta değerinin null olmasına izin verilmemesidir.
SimpleFormController sınıfı isteğin (request) değerlendirilip yönlendirilmesi sürecinin farklı noktalarında
otomatik olarak çağrılan ve genellikle bu sınıftan türetilen denetimci sınıflar tarafından yeniden yazılan (override)
yöntemler içerir. Bunlardan önemli olan bazılarını kısaca özetleyelim:
• Object formBackingObject(HttpServletRequest);
Formun komut nesnesinin yapılandırma kütüğünde commandClass niteliği ile belirlenmesi durumunda bu
sınıfın varsayılan yapılandırıcısı (default constructor) kullanılarak bir olgusunun oluşturularak formda
kullanıldığını söylemiştik. Bu nitelik belirtilmiş olsun veya olmasın denetimci sınıfın içinde
formBackingObject yönteminin yeniden yazılması durumunda bu yöntemin döndürdüğü nesne komut
nesnesi olarak kullanılır. Bu sayede, komut nesnesine bir takım ilk değerler vermek suretiyle (örneğin veri
tabanından gerekli veriler çekilerek) bu verilerin formların ilişkilendirildikleri (bağlandıkları) alanlarında
varsayılan olarak görüntülenmesi sağlanabilir. Burada dikkat edilmesi gereken, yöntemin geriye null değer
döndürmemesi şartıdır, aksi durumda bir çalışma zamanı hatası ortaya çıkar. Anlaşılacağı üzere bu daha form
görünümü görüntülenmeden önce çağrılan bir yöntemdir.
• Map referenceData(HttpServletRequest);
Bu yöntem komut nesnesinin içinde taşınması gereksiz olan verilerin görünüme iletilmesi amacıyla kullanılır.
Yani formla ilgisiz olan ama görünüm tarafından (görüntülenmek amacıyla) kullanılacak olan verilerin
iletilmesini sağlar. Doğrudan Controller'dan türetilen denetimcilerin handleRequest yönteminde
ModelAndView nesnesine verileri eklemesine benzetilebilir. Yöntemin döndürdüğü Map nesnesindeki tüm
veriler yine bu nesnedeki anahtar sözcükleri kullanılarak görünüme (formView) geçirilirler. Yine anlaşılacağı
üzere form görünümü görüntülenmeden önce çağrılan bir yöntemdir.
• ModelAndView
onSubmit(HttpServletRequest,HttpServletResponse,Object,BindException);
Bu yöntem yeniden yazılması olmazsa olmazlardan biridir ve formdan bir submit isteği geldiğinde yapılması
gerekenleri gerçekleştirildiği ve işlem sonucunda gidilmesi gereken sayfaya yönlendirmenin yine döndürülen
ModelAndView nesnesi sayesinde yapıldığı yöntemdir. Form işlemlerinin tamamen sonuçlandığı yöntem
olduğu söylenilebilir.
Bir form denetimci tarafından formun ele alınmasına ilişkin akışla ve sağlanan diğer yöntemlerle ilgili detaylı
açıklamaları Spring kurulumunuzun docs/api kılavuzunun altında bulunan javadoc belgelerinde bulabilirsiniz. Bunun
yanısıra, bu belgeleri geliştirme sırasında önemli bir başvuru kaynağı olarak kullanabilirsiniz.
Araç Girişi
Yeni bir aracın otoparka girişinin gerçekleştirildiği sayfa aracın plakasına ilişkin üç alanın girilmesine ve uygun bir park
yerinin seçilmesine olanak veren bir form içermelidir. İşlem gerçekleştirildikten sonra, verilerde bir hata yoksa, listenin
son durumunu görmek üzere tekrar ana sayfaya dönülmesini öngörüyoruz. Bir form denetimcisi yazmadan önce komut
sınıfını yazalım. Bu arada komut sınıfları için komut adlı bir paket oluşturduğumuza dikkat edin (Kod 2.20).
Form üzerinde araca dair bilgilerle (örneğimizde sadece plaka alanları) aracın otoparkta hangi park yerine park
edildiğine dair bilgiler girileceği için komut sınıfımızda (AracGirisi) bir Arac nesnesi ve park yeri için bir tam sayı
bulunuyor. Tabi bu sınıfın komut sınıfı olarak kullanılabilmesi için niteliklerine okuma ve yazma izni veren JavaBean
tanımına uygun erişim yöntemleri (set/get) gerçekleştirilmelidir.
package komut;
import ismantigi.Arac;
Arac arac;
int parkYeri;
package denetim;
import ismantigi.Otopark;
import komut.AracGirisi;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;
RedirectView
Kod 2.21'de görünümün doğrudan görünüm adı ile belirtilmesi yerine görünüm adı ile oluşturulan bir
RedirectView nesnesinin kullanıldığını görüyoruz. İlk kullanımda (görünüm adı kullanımı) isteğin (request)
kontrolü aynen başka bir noktaya yönlendirilir veya devredilir (forward). Bu durumda eğer post yöntemini kullanan
bir form bu şekilde yönlendirilmişse kullanıcı sayfayı her yenilediğinde aynı istek tekrar işletilir. Daha açık bir
ifade ile kullanıcı ekranında artık form değil örneğin işlem sonucunu yansıtan bir sayfa vardır, ancak kullanıcı
sayfayı yenilediği zaman üretilen istek form tarafından oluşturulan isteğin aynısıdır ve bu genellikle istenmeyen bir
şekilde bir kez gerçekleştirilmesi gereken bir işlemin tekrar edilmesine neden olur. Kritik bir uygulamada bu
önemli sakıncaları olabilir. Yeniden yönlendirme (redirect) yaklaşımının kullanılması ile yönlendirilen sayfaya
gidilirken belirtilen url'ye tamamen yeni bir istek oluşturularak yönlendirme gerçekleşir. Yani form tarafından
üretilen istekten tamamen bağımsız yeni bir istek oluşturularak yeni sayfaya gidilir. Bu kullanımda dikkat edilmesi
gereken nokta yönlendirme bir görünüm adı ile değil görünüme ilişkin adres ile yapılır ve successView
niteliğinin değerinin bu göz önüne alınarak belirlenmesi gerekir.
Sonuç olarak, yapılandırma kütüğünde AracGirisFormDenetimci için eklenilen tanımlamaları Kod 2.22'de
görebilirsiniz. İlk olarak aracGirisFormDenetimci adıyla tanımladığımız denetimciyi url eşleme kısmında
aracgiris.htm adresi ile eşleştirildiğini vurgulayalım (ana sayfanın en altında bulunan bağlantıyı hatırlayın!). Bu
denetimci tanımının içinde komut nesnesinin adının aracGiris olduğu ve bunun AracGirisi türünde olduğu
görülüyor. Ayrıca formun aracgiris.jsp içerisinde bulunduğu, dolayısıyla bu denetimciye yönelik istekler geldiğinde bu
sayfanın görüntülenmesi talebi belirtiliyor. Son olarak successView niteliği ile form işlemleri başarılı bir şekilde
tamamlandığında yeniden yönlendirmenin (redirect) yapılacağı adresin bu XML kütüğü içerisinde belirlendiğini
görüyoruz.
<beans>
<bean id="aracGirisFormDenetimci"
class="denetim.AracGirisFormDenetimci">
<property name="sessionForm"><value>true</value></property>
<property name="commandName"><value>aracGirisi</value></property>
<property name="commandClass">
<value>komut.AracGirisi</value>
</property>
<property name="formView"><value>aracgiris</value></property>
<property name="successView"><value>index.htm</value></property>
</bean>
<bean id="urlEsleme"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index.htm">anasayfaDenetimci</prop>
<prop key="/basit.htm">basitDenetimci</prop>
<prop key="/aracgiris.htm">aracGirisFormDenetimci</prop>
</props>
</property>
</bean>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>springornek - Araç Girişi</title>
</head>
<body>
<h2>Spring MVC - Otopark Uygulaması</h2>
<h3>Araç Girişi</h3>
<form method="post">
<table cellspacing="10">
<tr>
<td>Araç Plakası:</td>
<td>
<spring:bind path="aracGirisi.arac.ilKodu">
<input type="text" style="width:30;"
name="${status.expression}" value="${status.value}">
</spring:bind>
<spring:bind path="aracGirisi.arac.harfKodu">
<input type="text" style="width:40;"
name="${status.expression}" value="${status.value}">
</spring:bind>
<spring:bind path="aracGirisi.arac.aracNo">
<input type="text" style="width:40;"
name="${status.expression}" value="${status.value}">
</spring:bind>
</td>
</tr>
<tr>
<td>Park Yeri:</td>
<td>
<spring:bind path="aracGirisi.parkYeri">
<select name="${status.expression}">
<c:set var="i" value="0"/>
<c:forEach items="${araclar}" var="arac">
<option value="${i}" "${i == status.value ? "selected" : ""}"
<c:if test="${arac != null}">
style="background-color:GREY;"
</c:if>
>${i + 1}</option>
<c:set var="i" value="${i + 1}"/>
</c:forEach>
</select>
</spring:bind>
</td>
</tr>
<tr>
<td colspan="2" align="right">
<input type="submit" value="Kaydet">
</td>
</tr>
</table>
</form>
<a href="<c:url value="index.htm"/>">Ana Sayfa</a>
</body>
</html>
Resim 2.4 Araç Girişi Sayfası
Doğrulama (Validation)
Lütfen plakanın il kodu veya araç alanına bir tam sayı olmayan bir değer girerek veya boş bırakarak araç girişi yapmayı
deneyin. İşlemin tamamlanamadığını ana sayfa yerine araç girişi sayfasında kaldığınızı görerek anlamış olmalısınız. Bu
Spring tarafından otomatik olarak yapılan, beklenilen veri türü (tamsayı) ile girdinin veri türünün uyuşmaması
nedeniyle işlemin gerçekleştirilmesine izin vermeyen veri doğrulaması (validation) nedeniyle olmuştur.
Yukarıda açıklanan işlev aslında geliştiriciyi bir çok zahmetten kurtarmaktadır. Çünkü form verilerinin türlerinin
kontrol edilmesi, dönüştürme sırasında fırlatılan aykırı durumların ele alınması ve kullanıcıya bu hatanın sonucu ile geri
dönülmesi gibi hacim kaplayan bir çok kodlama işlemlerinin otomatikleşmesi sağlanıyor.
Ancak biraz düşününce bunların yeterli olmadığı açıktır. Bir uygulamada iş mantığı veya veri tutarlılığı gibi bir çok
konuda farklı denetimler gerekebilir, ayrıca bu denetimler sonucu fark edilen hataların giderilmesi veya tekrar
edilmemesi için kullanıcıya mutlaka geri bildirimde bulunulması gerekmektedir. Tabi Spring'in hata denetimi desteği de
bu kadarla kalmamakta, sözü edilen iki konuyla ilgili etkili çözümler sunulmaktadır.
Spring'in MVC kısmından bağımsız olan org.springframework.validation paketi veri doğrulama işleminde
kullanılır. Bu paketin içindeki Validator arayüzünden türetilen ve parametre olarak aldığı komut nesnesi üzerinde
doğrulama işlemlerini yerine getirebildiği validate yöntemini gerçekleştiren sınıfların nesneleri bir form
denetimcisinin validator niteliği olarak kullanılabilir. Tahmin ettiğiniz gibi bu tanımlamalar doğrulama yapan sınıf
yazıldıktan sonra yapılandırma kütüğü üzerinde gerçekleştirilir.
Kendi uygulamamızı göz önüne aldığımızda tür denetiminin dışında mantıksal denetimler yapmamız gerektiği açıktır.
Bunları şu şekilde listeleyebiliriz:
• Plakanın il kodu kesimi 1 – 81 (Ocak 2006 itibarı ile) arasında bir tamsayı olmalıdır.
• Plakanın araç numarası kesimi 1 – 9999 aralığında bir tamsayı olmalıdır.
• Plakanın harf kodu kesimi ise 1 – 3 adet (büyük) harften ibaret olmalıdır.
• Dolu bir park yerine başka bir araç park edilemez.
Uygulamamızda veri doğrulama amaçlı sınıflarımızı koyabileceğimiz dogrulama paketini oluşturduktan sonra Kod
2.25'te verilen ve bu kontrolleri gerçekleştiren Validator türevi sınıfı buraya kaydedebilirsiniz.
package dogrulama;
import ismantigi.Otopark;
import komut.AracGirisi;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
if(Otopark.doluMu(aracGirisi.getParkYeri()))
errors.rejectValue("parkYeri", "parkYeriDolu");
if(aracGirisi.getArac().getIlKodu() < 1 ||
aracGirisi.getArac().getIlKodu() > 81)
errors.rejectValue("arac.ilKodu", "gecersizIlKodu");
if(aracGirisi.getArac().getHarfKodu().length() > 3)
errors.rejectValue("arac.harfKodu", "harfKoduUzunlukHatasi");
if(!aracGirisi.getArac().getHarfKodu().matches("[A-Z]*"))
errors.rejectValue("arac.harfKodu","harfKoduKarakterHatasi");
if(aracGirisi.getArac().getAracNo() < 1 ||
aracGirisi.getArac().getAracNo() > 9999)
errors.rejectValue("arac.aracNo", "gecersizAracNo")
}
}
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>mesajlar</value></property>
</bean>
</beans>
typeMismatch=Geçersiz veri!
typeMismatch.ilKodu=\u0130l kodu bir tamsay\u0131 olmal\u0131!
typeMismatch.aracNo=Araç no bir tamsay\u0131 olmal\u0131!
parkYeriDolu=Seçti\u011Finiz park yeri dolu!
gecersizIlKodu=\u0130l kodu 1-81 aras\u0131nda olmal\u0131!
harfKoduUzunlukHatasi=Harf kodu en fazla 3 karakter içerebilir!
harfKoduKarakterHatasi=Harf kodu yaln\u0131z A..Z aras\u0131ndaki karakterleri
içerebilir!
gecersizAracNo=Araç no 1-9999 aras\u0131nda olmal\u0131!
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-9">
<title>springornek - Araç Girişi</title>
</head>
<body>
<h2>Spring MVC - Otopark Uygulaması</h2>
<h3>Araç Girişi</h3>
<form method="post">
<table cellspacing="10">
<tr>
<td valign="top">Araç Plakası:</td>
<td>
<spring:bind path="aracGirisi.arac.ilKodu">
<input type="text" style="width:30;"
name="${status.expression}" value="${status.value}">
</spring:bind>
<spring:bind path="aracGirisi.arac.harfKodu">
<input type="text" style="width:40;"
name="${status.expression}" value="${status.value}">
</spring:bind>
<spring:bind path="aracGirisi.arac.aracNo">
<input type="text" style="width:40;"
name="${status.expression}" value="${status.value}">
</spring:bind>
<font color="red" size="2px">
<spring:bind path="aracGirisi.arac.*">
<c:forEach items="${status.errorMessages}" var="error">
<br>${error}
</c:forEach>
</spring:bind>
</font>
</td>
</tr>
<tr>
<td valign="top">Park Yeri:</td>
<td>
<spring:bind path="aracGirisi.parkYeri">
<select name="${status.expression}">
<c:set var="i" value="0"/>
<c:forEach items="${araclar}" var="arac">
<option value="${i}" "${i == status.value ? "selected" : ""}"
<c:if test="${arac != null}">
style="background-color:GREY;"
</c:if>
>${i + 1}</option>
<c:set var="i" value="${i + 1}"/>
</c:forEach>
</select>
<font color="red" size="2px">
<c:forEach items="${status.errorMessages}" var="error">
<br>${error}
</c:forEach>
</font>
</spring:bind>
</td>
</tr>
<tr>
<td colspan="2" align="right">
<input type="submit" value="Kaydet">
</td>
</tr>
</table>
</form>
<spring:hasBindErrors name="aracGirisi">
<font color="blue"><h5>
Lütfen hatalı girişleri düzeltiniz!
</h5></font>
</spring:hasBindErrors>
</body>
</html>
Durdurucular (Interceptors)
Durdurucu kavramı Spring'in işleyici eşleme (handler mapping) mekanizması ile birlikte düşünülen bir işlevdir. Bir
isteği uygun denetimciye yönlendiren işleyici eşleme nesnesinin (handler mapping) içerisinde
org.springframework.web.servlet.HandlerInterceptor arayüzünü gerçekleştiren durdurucu
sınıflarının nesneleri bulunabilir. Bu arayüz preHandle, postHandle ve afterCompletion yöntemlerini
sağlar. Bunlardan preHandle uygun denetimci çalıştırılmadan önce, postHandle denetimci çalıştırıldıktan sonra,
ama görünüme yönlendirilmeden önce, afterCompletion ise istek tamamen gerçekleştirildiğinde çağrılır.
Uygulamamızda durdurucuları örneklemek için otoparkta yeni araç girişi için boş yer kalmadığı zaman araç giriş
sayfasına gelen isteklerin değerlendirilmeden ana sayfaya yönlendirilmesi senaryosunu ön görüyoruz. Bu durumda
durdurucunun işlemleri preHandle yönteminin içinde gerçekleştirmesi gerektiği ortaya çıkıyor. Çünkü bir denetimci
(aracGirisFormDenetimci) çalıştırılmadan önce onun çalıştırılıp çalıştırılamayacağınca karar verilmesi söz
konusu. Ancak burada ortaya çıkan bir problem daha var; tüm denetimciler aynı işleyici eşleme nesnesi tarafından ele
alındığı için tüm denetimcilere gelen istekler durdurucu tarafından park yeri kalıp kalmadığı sorgusuyla karşı karşıya
kalacaklardır. Bunu çözebilmek için içerisinde sadece aracGirisDenetimci'sinin eşlemesinin ve durdurucu
tanımının yer aldığı yeni bir işleyici eşleme tanımına ihtiyacımız olacaktır. Kod 2.29'da durdurucu sınıfın içeriğine yer
verilmiştir.
package denetim;
import ismantigi.Otopark;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class AracGirisDurdurucu implements HandlerInterceptor {
if(Otopark.doluMu()) {
response.sendRedirect(hataSayfasi);
return false;
}
return true;
}
<beans>
<bean id="urlEsleme"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index.htm">anasayfaDenetimci</prop>
<prop key="/basit.htm">basitDenetimci</prop>
</props>
</property>
</bean>
<bean id="aracGirisUrlEsleme"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="aracGirisDurdurucu"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/aracgiris.htm">aracGirisFormDenetimci</prop>
</props>
</property>
</bean>
<bean id="gorunumCozumleyici"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>mesajlar</value></property>
</bean>
</beans>
Kaynaklar
http://www.springframework.org/docs/reference/
http://www.ociweb.com/
http://www.theserverside.com/articles/article.tss?l=SpringFramework
http://www.devx.com/Java/Article/22134/0/page/1
http://www.128.ibm.com/developerworks/web/library/waspring3/