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;

Dispatcher servlet diagram

  • 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.

spring-framework-module-dependencies

 

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

http500

<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

project explorer

Run Application

Projemizi deploy edelim , su linke gittigimizde ;

http://localhost:8080/injavawetrust.springmvc/hello

run application

Eger ilgili URL ‘e karsilik olarak bir Controller tanimlanmadiysa bu durumda HTTP 404 hatasi aliriz.

http 404 status

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

hello spring mvc wolrd

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

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *