Spring MVC – 02 – Hello Spring MVC World
Merhaba arkadaslar,
Bu bolumde Spring MVC konusuna giris yapacagiz. Spring MVC , Model 2 MVC Design Pattern’ini uygulamaktadir , dolayisiyla bir Front Controller’a sahiptir.
Spring MVC’nin Front Controller’i DispatcherServlet’tir. DispatcherServlet IS-A Servlet ‘tir.
Onceki bolumde Front Controller’un gorevinden bahsetmistik , Front Controller gelen istegi ilk karsilayan ve istegi ilgili Controller’a yonlendirme islemi yapan bir Servlet’tir.
DispatcherServlet Request Processing Workflow
Workflow u adim adim inceleyecek olursak;
- Istek(request) Front Controller (DispatcherServlet) tarafindan karsilanir.
- DispatcherServlet , HandlerMapping’e basvurararak ilgili Controller bilgisine ulasir. Varsayilan olarak BeanNameUrlHandlerMapping handler olarak kullanilir.
- Controller bilgisi DispatcherServlet’e doner.
- Istek Controller tarafindan karsilanir. (handle request)
- Model olusturulur/hazirlanir ,view ismi de kullanilarak geriye org.springframework.web.servlet.ModelAndView objesi DispatcherServlet ‘e doner.
- View adi kullanilarak ViewResolver tarafindan geriye org.springframework.web.servlet.View objesi doner.
- org.springframework.web.servlet.View , model i render eder.
- Response FrontController a doner.
- Response/cevap istemciye/browser’a doner.
Simple Spring MVC Example
Bu bolumde minimum kod ve konfigurasyon ile en basit sekli ile bir ornek uygulama yapacagiz. Oncelikle yeni bir Dynamic Web Project olusturalim ve bu projeyi Maven projesine convert edelim.
Gerekli Jar’lar ? Maven , dependency ?
spring-webmvc module’u icin dependency tanimi eklememiz yeterli olacaktir. Bununla birlikte
Projemizi calistirabilmek icin Apache Tomcat gibi bir Servlet Container yeterli olacaktir.
Hatirlayacagimiz gibi Spring Framework bunyesinde bir cok module/jar yer almaktadir , bu module’lerin birbiriyle baglantisi su sekildedir. Dolayisiyla biz webmvc module’unu ekledigimizde Maven ilgili/gerekli jarlari da projeye ekleyecektir.
Biz sadece spring-webmvc dependency ekliyoruz diger jar’lar da otomatik olarak eklenecektir , Maven dunyasinda otomatik olarak eklenen bu dependency’lere transitive dependencies adi verilir.
Spring Framework ,ek olarak commons-logging jar’ina ihtiyac duymaktadir.
Projemiz Maven tabanli oldugu icin ilgili tum dependency/jar’lar projeye eklenecektir.
pom.xml
..... <properties> <!-- updated 01.05.2017 --> <spring.version>4.3.8.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> </dependencies> ....
Deployment Descriptor & DispatcherServlet
Istekleri karsilayabilmek icin oncelikle Front Controller olarak calisan DispatcherServlet’in deklarasyonunu ve mapping islemini yapmamiz gereklidir. Bunu yapabilmek icin web.xml dosyamizi kullanabiliriz. Servlet 3.0 ile birlikte gelen ozellik ile Java kodu ile de yapabiliriz. Simdilik web.xml de bu islemi yapalim.
web.xml
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/01.appContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
Burada dikkat edecek olursak param-name contextConfigLocation olan bir init parameter tanimladik.
DispatcherServlet , controller siniflarimizin ve view dosyalarimizin(jsp) projemizde hangi dizinde oldugunu bilmelidir. Bunun icin varsayilan olarak WEB-INF dizini altinda ismi [servletname]-servlet.xml olacak sekilde bir dosya aranir. Bu durumda dispatcher-servlet.xml adinda bir dosya olmalidir. Bu varsayilan ozelligi override etmek icin contextConfigLocation init parametresini kullanabiliriz.
Eger XML konfigurasyon dosyasi ilgili dizinde yer almazsa bu durumda su hataya neden olacaktir ;
HTTP Status 500 - Servlet.init() for servlet dispatcher threw exception java.io.FileNotFoundException
<url-pattern> olarak / ifadesi uygulamanin default servlet’ini belirtir. DispatcherServlet icin basit olarak bu sekilde <url-pattern> tanimlamamiz yeterli olacaktir.
URL pattern olarak “/” ifadesi , tanimladigimiz bu servlet’in uygulamamiz icin default servlet oldugunu ifade eder. Boylelikle gelen tum istekler DispatcherServlet tarafindan karsilanacaktir.
Tum istekler(request) ilk olarak DispatcherServlet tarafindan karsilanacaktir. Daha sonrasinda DispatcherServlet bu istegin hangi Controller tarafindan yapilacagina karar verir ve istegi bu Controller’a dispatch eder.
Controller , Service , Model
Spring 4.x te bir sinifi Controller olarak tanimlayabilmek icni @Controller annotation’i kullanmak yeterli olacaktir.
Spring 3.0 dan once Spring’e ozel bazi siniflari extends etmemiz gerekliydi.
Ornegin AbstractController. Spring 3.0 te @Controller annotation’i kullanabiliyoruz.
Spring 3.0 la birlikte bazi XXXController siniflari deprecated olmaya basladi ve bunlar artik Spring 4.x te desteklenmemektedir.
Spring 3.0 icin org.springframework.web.servlet.mvc paketinde olup Spring 4.2 de olmayan siniflari gorebiliriz;
spring-webmvc-3.0.0.RELEASE.jar
org.springframework.web.servlet.mvc AbstractCommandController.class AbstractController.class AbstractFormController.class AbstractUrlViewController.class AbstractWizardFormController.class BaseCommandController.class CancellableFormController.class Controller.class HttpRequestHandlerAdapter.class LastModified.class package-info.class ParameterizableViewController.class ServletForwardingController.class ServletWrappingController.class SimpleControllerHandlerAdapter.class SimpleFormController.class UrlFilenameViewController.class WebContentInterceptor.class
spring-web-4.2.4.RELEASE.jar
org.springframework.web.servlet.mvc AbstractController.class AbstractUrlViewController.class Controller.class HttpRequestHandlerAdapter.class LastModified.class ParameterizableViewController.class ServletForwardingController.class ServletWrappingController.class SimpleControllerHandlerAdapter.class UrlFilenameViewController.class WebContentInterceptor.class
Orneklerimizde genel olarak @Controller annotation’i kullanmayi tercih edecegiz. Controller sinifina bir istek geldigince request’i karsilayabilmek icin uygun bir handler method gerekecektir. Bu handler method’lari tanimlamak icin @RequestMapping annotation’i kullaniriz. Simdi basit sekilde bir ornek yapalim , ilerleyen bolumlerde tekrar detayli olarak inceleyecegiz.
Handler method tamamlandiktan sonra donus degeri view implementation ismi olarak donmez yani hello.jsp , report.pdf yerine logical view olarak doner ; hello seklinde.
ViewResolver , logical view’in gercekte hangi view’e karsilik geldigini cozumler.
HelloController.java
package _01.mvc.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import _01.mvc.service.HelloService; @Controller public class HelloController { private HelloService helloService; public void setHelloService(HelloService helloService) { this.helloService = helloService; } @RequestMapping(value = "/hello", method = RequestMethod.GET) public String sayHelloSpringMVC(ModelMap model) { String message = helloService.getMessage(); model.addAttribute("message", message); return "01.hello.springmvc/hello"; } }
HelloController sinifimizda @Controller annotation kullaniyoruz. Handler methodumuzda path olarak /hello degerini veriyoruz. HTTP GET ile geldigimizde bu method tetiklenecektir. ModelMap tipindeki degiskene addAttribute metodu ile bir attribute ekliyoruz. Bu attribute bilgisine JSP sayfamizdan ulasacagiz.
model.addAttribute metodu request.setAttribute metodunun yaptigi isi yapar.
Yani request scope’a ilgili attribute objesini ekleyecektir.
Burada return ifadesine dikkat edecek olursak ,hello.jsp dosyamiz WebContent/WEB-INF/jsp/01.hello.springmvc dizini altinda yer alacaktir.
return "01.hello.springmvc/hello";
HelloService.java
package _01.mvc.service; public interface HelloService { public String getMessage(); }
HelloServiceImpl.java
package _01.mvc.service; import org.springframework.stereotype.Service; import _01.mvc.domain.Message; @Service public class HelloServiceImpl implements HelloService { @Override public String getMessage() { Message message = new Message(); String greetingMessage = message.getGreetingMessage(); return greetingMessage; } }
Message.java
package _01.mvc.domain; public class Message { private String greetingMessage = "welcome to www.injavawetrust.com spring mvc tutorial!"; public String getGreetingMessage() { return greetingMessage; } public void setGreetingMessage(String greetingMessage) { this.greetingMessage = greetingMessage; } }
More XML Configuration
Simdi de bean tanimlarimizi 01.appContext.xml de tanimlayalim.
<?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"> <bean class="_01.mvc.controller.HelloController" <property name="helloService" ref="helloServiceId"/> </bean> <bean class="_01.mvc.service.HelloServiceImpl" id="helloServiceId" /> </beans>
bean’leri tek tek tanimlamak yerine <context:component-scan base-package=”….” /> tag’ini kullanabiliriz.
Auto-Scanning ozelligi aktif hale gelecektir.Boylelikle @Controller @Service gibi annotation’li siniflar aranip bulunacaktir. Bununla birlikte @Autowired annotation’i kullandigimizda Inject islemi gerceklesecektir. Bu konuyu Spring Core yazilarimda inceledim.
.... <!-- <bean class="_01.mvc.controller.HelloController"> <property name="helloService" ref="helloServiceId"/> </bean> <bean class="_01.mvc.service.HelloServiceImpl" id="helloServiceId" /> --> <!-- We can use auto-scanning via context:component-scan tag. However we don't have to define bean with XML configuration --> <!--Auto-scanning ozelligini aktif ettigimizde bean tanimlamasini xml dosyamizda yapmamiza gerek kalmaz.@Controller ve @Service annotation i yeterli olacaktir. --> <context:component-scan base-package="_01.mvc.controller , _01.mvc.service" />
HelloController.java
package _01.mvc.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import _01.mvc.service.HelloService; @Controller public class HelloController { @Autowired private HelloService helloService; // we are using context:component-scan. // we don't need setter method for @Autowired. // setter injection'ini kendimiz yapmak yerine , context:component-scan // tag'ini kullaniyoruz. // @Autowired icin setter metoduna ihtiyacimiz yoktur. // public void setHelloService(HelloService helloService) { // this.helloService = helloService; // } @RequestMapping(value = "/hello", method = RequestMethod.GET) public String sayHelloSpringMVC(ModelMap model) { String message = helloService.getMessage(); model.addAttribute("message", message); return "01.hello.springmvc.view/hello"; } }
ViewResolver Configuration
01.appContext.xml dosyamizda ViewResolver olarak InternalResourceViewResolver sinifini kullanabiliriz. JSP dosyalarimiz WEB-INF/jsp klasoru altinda yer alacak , tabi bu dizini kendimiz belirledik . WEB-INF klasoru altinda yer alan dosyalara direkt erisim yoktur.
Bu nedenle gercek uygulamalarda JSP dosyalari WebContent altinda degil mutlaka WEB-INF klasoru altinda yer alir. Boylelikle direkt erisime karsi security/guvenlik saglanmis olur.
WEB-INF altina koydugumuz JSP dosyalarini Spring bizim icin cozumleyecektir ve render edecektir.
View Resolver, istege(request) karsilik olarak gosterilecek olan view’in belirlenmesinde kullanilir. Spring MVC bir cok view resolver implementation destekler. Burada InternalResourceViewResolver sinifini kullanabiliriz.
prefix property degeri ile projedeki JSP dosyalarimizin bulundugu dizini belirtiyoruz.
suffix property degeri ile projemizde view olarak JSP dosyalarini kullanacagimiz icin .jsp bilgisini belirtiyoruz.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
sayHelloSpringMVC metotunda donus degeri olarak “01.hello.springmvc/hello” literalini belirttik. prefix ve suffix bilgisi eklendiginde
WEB-INF/01.hello.springmvc/hello.jsp dosyasinin view olarak kullanilacagi anlamina gelecektir.
JSP files
JSP dosyamizin adi “hello.jsp” olacak. Controller sinifimizda donus degeri olarak “hello” logical view degerini kullanmistik.
hello.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Hello Spring MVC!</title> </head> <body> <h2>${message}</h2> </body> </html>
JSP dosyamizda basit bir Expression ifadesi yer almaktadir. ${message} ifadesi , Controller sinifimizda addAttribute metodu ile ekledigimiz attribute degeridir.
Project Explorer
Run Application
Projemizi deploy edelim , su linke gittigimizde ;
http://localhost:8080/injavawetrust.springmvc/hello
Eger ilgili URL ‘e karsilik olarak bir Controller tanimlanmadiysa bu durumda HTTP 404 hatasi aliriz.
Eclipse’te Console’a bakacak olursak ;
WARNING: No mapping found for HTTP request with URI [/injavawetrust.springmvc/nocontroller] in DispatcherServlet with name 'dispatcher'
Bir diger nokta olarak DispatcherServlet icin <url-pattern> ekleyelim.
/myproject/* , boylece url adresi myproject/XXXX olan tum istekler DispatcherServlet tarafindan yakalanacaktir.
<servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> <url-pattern>/myproject/*</url-pattern> </servlet-mapping>
http://localhost:8080/injavawetrust.springmvc/myproject/hello
Github kaynak kodlar / source folder
Injavawetrust-springmvc-tutorial
Yazimi burada sonlandiriyorum.
Herkese Bol Javali Gunler dilerim.
Be an oracle man , import java.*;
Levent Erguder
OCP, Java SE 6 Programmer
OCE, Java EE 6 Web Component Developer
Leave a Reply