Spring MVC – 09 – Beginning WebStore Implementation
Merhaba Arkadaslar
Bu bolumde basit bir WebStore uygulamasini gerceklestirecegiz.
Ilerleyen bolumlerde bu WebStore uzerinde gelistirmeler yapacagiz.
Burada su annotation’lari tekrar inceleyip daha iyi anlayalim.
- @PathVariable
- @RequestParam
- @MatrixVariable
URI template patterns
Dusunelim , elimizde cesitli Urunler/Product olacak ve bu urunleri kategoriye gore listelemek istersek ne yapmamiz gerekli.
Ornegin laptop ve tablet gibi kategorilerimiz oldugunu dusunelim ;
http://localhost:8080/injavawetrust.springmvc/products/laptop http://localhost:8080/injavawetrust.springmvc/products/tablet
Bu sekilde bir cok kategori olabilir , her bir URL’e karsilik bir @RequestMapping annotation tanimlayip metot kullanmamiz mumkun olmayacaktir.
Iste bu noktada bize yardimci olarak @PathVariable annotation’ini kullanabiliriz.
@RequestMapping("/category/{productCategory}") public String listProductsByCategory(@PathVariable("productCategory") String productCategory, Model model) { model.addAttribute("products", productService.getProductsByCategory(productCategory)); return "08.webstore.view/productList"; }
Kodu inceleyecek olursak ;
- @RequestMapping annotation’ininda path bilgisini suslu parantezler (curly brace) arasinda yaziyoruz {productCategory}. Boylece bunun bir URI template variable oldugunu belirtiyoruz.
- @PathVariable annotation’ini , @PathVariable(“productCategory”) String productCategory seklinde kullanabiliriz.
- @PathVariable , URI template variable’i okumak icin kullanilir.
- @RequestMapping ‘te tanimladigimiz URI template variable ismi ile (productCategory) , @PathVariable annotation’da kullandigimiz isim ayni olmalidir.
- Eger @PathVariable annotation’da bir isim kullanilmazsa bu durumda variable ismiyle ayni isim olur. Yani su kodlar ayni anlama gelmektedir ;
@PathVariable String productCategory
@PathVariable(“productCategory”) String productCategory
Request Parameters
Simdi de GET request parametreleri Spring MVC de nasil kullanabilecegimizi inceleyelim. Dusunelim ki , Urun/Product detayini gormek istiyoruz. Bunun icin primary key olarak dusunebilecegimiz product id bilgisinden yararlanabiliriz. Bu bilgiyi Urun/Product icin URL sonuna request parameter olarak ekledigimizi dusunursek istedigimiz seyi elde edebiliriz. Bu noktada karsimiza @RequestParam annotation’ini cikmaktadir.
@RequestMapping("/product") public String getProductById(@RequestParam(required = false, name = "id") String productId, Model model) { model.addAttribute("product", productService.getProductById(productId)); return "08.webstore.view/productDetail"; }
Kodu inceleyecek olursak ;
- Oncelikle @RequestMapping annotation’ini metodumuzun basina ekliyoruz. Boylece ilgili request’ler icin bu metodu kullanabiliriz.
- Daha sonrasinda @RequestParam annotation’ini ayni @PathVariable annotation’inda oldugu gibi kullaniyoruz.
@RequestParam(“id”) String productId - Boylelikle Request Parameter’lari @RequestParam annotation yardimiyla ilgili degiskenlere baglamis/iliskilendirmis/bind oluruz.
Matrix Variable
Son olarak inceleyecegimiz ozellik Matrix Variable olacaktir. Dusunelim , ornegin brand ve category degerlerine gore ( bi birden fazla brand ya da category secmek isteriz) Urun/Product listelemek istersek bu durumda bu filtreleme yapisini nasil yapabiliriz.
Iste bu noktada Spring MVC @MatrixVariable annotation’i bizim icin cozum sunacaktir.
Burada basit olarak brand’e gore filtreleme yapisini entegre edelim.
@RequestMapping("/brand/{brands}") public String listProductsByBrands(@MatrixVariable(pathVar = "brands") List<String> brands, Model model) { model.addAttribute("products", productService.getProductsByBrands(brands)); return "08.webstore.view/productList"; }
Kodu inceleyecek olursak ;
- @PathVariable annotation taniminda oldugu gibi Matrix Variable icin de curly brace kullaniyoruz. {brands}
- @MatrixVariable(pathVar = “brands”) List<String> brands seklinde metot deklarasyonunda @MatrixVariable annotation’ini kullaniyoruz.
Application
Simdi de uygulamiza daha yakindan goz atalim;
Domain
Bu ornegimizde Product isminde bir sinif kullaniyoruz.
Product.java
package _08.webstore.domain; public class Product { private String productId; private String name; private double unitPrice; private String description; private String manufacturer; private String category; private long unitsInStock; //getters and setters //constructors ... }
Data Access Object Layer (DAO)
DAO katmaninda su metotlar yer alacaktir ;
tum urunleri getirdigimiz (getAllProducts)
productId degerine gore ilgili urunu getirdigimiz (getProductById)
category degerine gore tum urunleri getirdigimiz (getProductsByCategory)
brand’e gore filtreleme yaptigimiz (getProductsByBrands)
ProductRepositoryDAO.java
package _08.webstore.dao; import java.util.List; import _08.webstore.domain.Product; public interface ProductRepositoryDAO { public List<Product> getAllProducts(); public Product getProductById(String productID); public List<Product> getProductsByCategory(String category); public List<Product> getProductsByBrands(List<String> brands); }
ProductRepositoryDAOImpl.java
package _08.webstore.dao; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import org.springframework.stereotype.Repository; import _08.webstore.domain.Product; @Repository public class ProductRepositoryDAOImpl implements ProductRepositoryDAO { private List<Product> listOfProducts = new ArrayList<Product>(); public ProductRepositoryDAOImpl() { Product iphone = new Product("P1001", "iPhone 5s", 549.99); iphone.setDescription("Apple iPhone 5s smartphone with 4.00-inch 640x1136 display and 8-megapixel rear camera"); iphone.setCategory("SmartPhone"); iphone.setManufacturer("Apple"); iphone.setUnitsInStock(1000); //more products listOfProducts.add(laptopDell); //addd listOfProducts.add(laptopAppleMacbook); } @Override public List<Product> getAllProducts() { return listOfProducts; } @Override public Product getProductById(String productId) { // Old way // Product foundProductById = null; // for (Product product : listOfProducts) { // if (product.getProductId().equals(productId)) { // foundProductById = product; // break; // } // } // return foundProductById; Predicate<Product> predicate = (Product p) -> p.getProductId().equals(productId); Product foundProduct = listOfProducts.stream().filter(predicate).findAny().orElse(null); return foundProduct; } @Override public List<Product> getProductsByCategory(String category) { // Old way // List<Product> productsByCategory = new ArrayList<Product>(); // // for (Product product : listOfProducts) { // if (category.equalsIgnoreCase(product.getCategory())) { // productsByCategory.add(product); // } // } // // return productsByCategory; Predicate<Product> predicate = (Product product) -> product.getCategory().equalsIgnoreCase(category); List<Product> filtered = listOfProducts.stream().filter(predicate).collect(Collectors.toList()); return filtered; } @Override public List<Product> getProductsByBrands(List<String> brands) { // Old way // List<Product> productsByBrand = new ArrayList<Product>(); // for (String brandName : brands) { // for (Product product : listOfProducts) { // if (brandName.equalsIgnoreCase(product.getManufacturer())) { // productsByBrand.add(product); // } // } // } // return productsByBrand; Predicate<Product> predicate = (Product product) -> brands.contains(product.getManufacturer().toLowerCase()); List<Product> filtered = listOfProducts.stream().filter(predicate).collect(Collectors.toList()); return filtered; } }
Service Layer
Controller’lardan direkt olarak DAO katmanina erisim olmamalidir. Bunun yerine Controller siniflari Service Layer’a erismelidir.
Servive Layer da DAO Layer’a erismelidir. Bu konuda bilgileri onceki yazilarda vermistim.
ProductServiceImpl.java
package _08.webstore.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import _08.webstore.dao.ProductRepositoryDAO; import _08.webstore.domain.Product; @Service public class ProductServiceImpl implements ProductService { @Autowired private ProductRepositoryDAO productRepository; @Override public List<Product> getAllProducts() { return productRepository.getAllProducts(); } @Override public Product getProductById(String productID) { return productRepository.getProductById(productID); } @Override public List<Product> getProductsByCategory(String category) { return productRepository.getProductsByCategory(category); } @Override public List<Product> getProductsByBrands(List<String> brands) { return productRepository.getProductsByBrands(brands); } }
Web Layer / Controller
ProductController sinifimizi parca parca incelemistik
ProductController.java
package _08.webstore.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.MatrixVariable; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import _08.webstore.service.ProductService; @Controller @RequestMapping("/products") public class ProductController { @Autowired private ProductService productService; @RequestMapping public String list(Model model) { model.addAttribute("products", productService.getAllProducts()); return "08.webstore.view/productList"; } @RequestMapping("/all") public ModelAndView listAllProducts() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("products", productService.getAllProducts()); modelAndView.setViewName("08.webstore.view/productList"); return modelAndView; } @RequestMapping("/category/{productCategory}") public String listProductsByCategory(@PathVariable("productCategory") String productCategory, Model model) { model.addAttribute("products", productService.getProductsByCategory(productCategory)); return "08.webstore.view/productList"; } @RequestMapping("/product") public String getProductById(@RequestParam(required = false, name = "id") String productId, Model model) { model.addAttribute("product", productService.getProductById(productId)); return "08.webstore.view/productDetail"; } @RequestMapping("/brand/{brands}") public String listProductsByBrands(@MatrixVariable(pathVar = "brands") List<String> brands, Model model) { model.addAttribute("products", productService.getProductsByBrands(brands)); return "08.webstore.view/productList"; } }
Maven Dependency
JSP sayfalarimizda JSTL taglarini kullanacagiz ilgili dependency tanimini ekleyelim ;
pom.xml
<properties> .... <jstl.version>1.2</jstl.version> ... </properties> .... <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> ....
View
Bu uygulamada Bootstrap kullanacagiz. JSP dosyalarinda yer alan kodlarinda dikkat etmemiz gereken bir kac noktayi inceleyecek olursak ;
JSTL taglarini kullanabilmek icin oncelikle taglib directive tanimini yapiyoruz.
Daha sonrasida linklerde kullanabilecegimiz <c:set> tagiyla attribute’ler ekliyoruz.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:set var="productUrlAll" value="${pageContext.request.contextPath}/products/all"/> <c:set var="productUrlCategory" value="${pageContext.request.contextPath}/products/category"/> <c:set var="matrixUrl" value="${pageContext.request.contextPath}/products/brand"/>
Projemizde css ve js dosyalari WebContent/resources altinda yer alacak. Ilgili dizinlerdeki dosyalari gosteriyoruz. Bununla birlikte ek bir konfigurasyon yapmamiz gerekli. Yazinin ilerleyen bolumunde bahsedecegim.
<link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/resources/css/sidebar.css" />" /> <link rel="stylesheet" type="text/css" media="screen" href="<c:url value="/resources/css/bootstrap.min.css" />" /> <script src="<c:url value="/resources/js/jquery.min.js" />"></script>
Matrix Variable’lar icin link’i tekrar duzenlemek icin basit bir Javascript function yazalim.
function prepareMatrix(){ var selectedBrands=""; $('input:checkbox[name=brand]').each(function() { if($(this).is(':checked')) selectedBrands=selectedBrands+$(this).val()+","; }); var url= '${matrixUrl}/brands='+selectedBrands; $("#search").attr("href", url); } </script>
XML Configuration
Son olarak XML dosyamizi inceleyelim ;
- Oncelikle Matrix Variable ozelligini aktif etmek icin <mvc:annotation-driven> tagini kullaniyoruz.
- <mvc:annotation-driven> tagini kullanabilmek ek olarak xlms ve xsi:schemaLocation eklememiz gereklidir.
- css ve js dosyalarini JSP dosyamizda kullanabilmek icin <mvc:resources> annotation ile ilgili location bilgisini veriyoruz.
08.webstore.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" xmlns:mvc="http://www.springframework.org/schema/mvc" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd "> <!-- enabled matrix variable --> <mvc:annotation-driven enable-matrix-variables="true"/> <mvc:resources mapping="/resources/**" location="resources/"/> <context:component-scan base-package="_08.webstore" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean id= "messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="messages"/> </bean> </beans>
Son olarak web.xml dosyamiza contextConfigLocation olarak 08.webstore.xml bilgisini verelim.
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 /WEB-INF/02.00.appContext.xml --> /WEB-INF/08.webstore.xml <!-- --> </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> ...
Run Application
Ornegimizi calistiralim ;
http://localhost:8080/injavawetrust.springmvc/products/all
Laptops , Tablets , Smart Phone linklerinden birine tikladigimizda ilgili category’de yer alan urunler listelenecektir.
Bunun icin @PathVariable annotation’dan yararlandik.
<li class=""> <a href="${productUrlAll}">All Products</a> </li> <li> <a href="${productUrlCategory}/laptop">Laptops</a> </li> <li class=""> <a href="${productUrlCategory}/tablet">Tablets</a> </li> <li class=""> <a href="${productUrlCategory}/smartPhone">Smart Phones</a> </li>
Bir diger test olarak Details linkine tikladigimizda ilgili Urun/Product’a ait detail sayfasi gelecektir.
Bunun icin @RequestParam annotation da yararlandik.
<a href=" <c:url value="/products/product?id=${product.productId}" /> " class="btn btn-primary"> <span class="glyphicon-info-sign glyphicon" /></span> Details </a>
Bir diger test olarak Side Barda yer alan ilgili checboxlar secildiginde filtreleme yapacaktir. Bunun icin @MatrixVariable annotationdan yararlaniyoruz.
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