Spring MVC Beginner’s Guide Beginner's Amuthan G
User Manual:
Open the PDF directly: View PDF .
Page Count: 545 [warning: Documents this large are best viewed by clicking the View PDF Link!]
- Spring MVC Beginner's Guide
- Time for action – whitelisting
- Credits
- About the Author
- About the Reviewers
- www.PacktPub.com
- Support files, eBooks, discount offers, and more
- Why subscribe?
- Free access for Packt account holders
- Preface
- What this book covers
- What you need for this book
- Who this book is for
- Conventions
- Time for action – heading
- What just happened?
- Pop quiz – heading
- Have a go hero – heading
- Reader feedback
- Customer support
- Downloading the example code
- Errata
- Piracy
- Questions
- 1. Configuring a Spring Development Environment
- Setting up Java
- Time for action – installing JDK
- Time for action – setting up environment variables
- Configuring a build tool
- Time for action – installing the Maven build tool
- Installing a web server
- Time for action – installing the Tomcat web server
- Configuring a development environment
- Time for action – installing Spring Tool Suite
- Time for action – configuring Tomcat on STS
- What just happened?
- Time for action – configuring Maven on STS
- Creating our first Spring MVC project
- Time for action – creating a Spring MVC project in STS
- What just happened?
- Spring MVC dependencies
- Time for action – adding Spring jars to the project
- What just happened?
- Time for action – adding Java version properties in pom.xml
- A jump-start to MVC
- Time for action – adding a welcome page
- What just happened?
- The dispatcher servlet
- Time for action – configuring the dispatcher servlet
- What just happened?
- Deploying our project
- Time for action – running the project
- Summary
- 2. Spring MVC Architecture – Architecting Your Web Store
- The dispatcher servlet
- Time for action – examining request mapping
- What just happened?
- Pop quiz – request mapping
- The web application context
- Time for action – understanding the web application context
- What just happened?
- Pop quiz – the web application context
- The web application context configuration
- Pop quiz – web application context configuration
- View resolvers
- Time for action – understanding InternalResourceViewResolver
- What just happened?
- Model View Controller
- An overview of the Spring MVC request flow
- The web application architecture
- The domain layer
- Time for action – creating a domain object
- What just happened?
- The persistence layer
- Time for action – creating a repository object
- What just happened?
- The service layer
- Time for action – creating a service object
- What just happened?
- Have a go hero – accessing the product domain object via a service
- An overview of the web application architecture
- Have a go hero – listing all our customers
- Summary
- 3. Control Your Store with Controllers
- Defining a controller
- Time for action – adding class-level request mapping
- What just happened?
- Pop quiz – class-level request mapping
- The role of a controller in Spring MVC
- Handler mapping
- Using URI template patterns
- Time for action – showing products based on category
- What just happened?
- Pop quiz – request path variable
- Using matrix variables
- Time for action – showing the products based on filter
- What just happened?
- Understanding request parameters
- Time for action – adding the product details page
- What just happened?
- Pop quiz – the request parameter
- Time for action – implementing a master detail view
- What just happened?
- Have a go hero – adding multiple filters to list products
- Summary
- 4. Working with Spring Tag Libraries
- Serving and processing forms
- Time for action – serving and processing forms
- What just happened?
- Customizing data binding
- Time for action – whitelisting form fields
- What just happened?
- Externalizing text messages
- Time for action – externalizing messages
- What just happened?
- Using Spring Security tags
- Time for action – adding a login page
- What just happened?
- Summary
- 5. Working with View Resolver
- Resolving views
- The redirect view
- Time for action – examining RedirectView
- What just happened?
- Pop quiz – redirect view
- Serving static resources
- Time for action – serving static resources
- What just happened?
- Pop quiz – static view
- Time for action – adding images to the product detail page
- What just happened?
- The multipart request in action
- Time for action – adding images to the product page
- What just happened?
- Have a go hero – uploading product user manuals to the server
- Using ContentNegotiatingViewResolver
- Time for action – configuring ContentNegotiatingViewResolver
- What just happened?
- Working with the handler exception resolver
- Time for action – adding the response status exception
- What just happened?
- Time for action – adding an exception handler
- What just happened?
- Summary
- 6. Intercept Your Store with Interceptor
- Working with interceptors
- Time for action – configuring an interceptor
- What just happened?
- Pop quiz – interceptor
- Internationalization (i18n)
- Time for action – adding internationalization
- What just happened?
- Have a go hero – fully internationalize the product detail page
- Audit logging
- Time for action – adding the data audit interceptor
- What just happened?
- Conditional redirecting
- Time for action – intercepting offer page requests
- What just happened?
- Summary
- 7. Validate Your Products with a Validator
- Bean validation
- Time for action – adding bean validation support
- What just happened?
- Have a go hero – adding more validation in the add products page
- Custom validation with JSR-303 / bean validation
- Time for action – adding custom validation support
- What just happened?
- Have a go hero – adding custom validation to a category
- Spring validation
- Time for action – adding Spring validation
- What just happened?
- Time for action – combining Spring and bean validations
- What just happened?
- Have a go hero – adding Spring validation to the product image
- Summary
- 8. Give REST to Your Application with Ajax
- Introducing REST
- Time for action – implementing RESTful web services
- What just happened?
- Time for action – consuming REST web services
- What just happened?
- Handling a web service in Ajax
- Time for action – consuming REST web services via Ajax
- What just happened?
- Summary
- 9. Apache Tiles and Spring Web Flow in Action
- Working with Spring Web Flow
- Time for action – implementing the order-processing service
- What just happened?
- Time for action – implementing the checkout flow
- What just happened?
- Understanding the flow definition
- Understanding the checkout flow
- Pop quiz – web flow
- Time for action – creating views for every view state
- What just happened?
- Have a go hero – adding a decision state
- Enhancing reusability through Apache Tiles
- Time for action – creating views for every view state
- What just happened?
- Pop quiz – Apache Tiles
- Summary
- 10. Testing Your Application
- Unit testing
- Time for action – unit-testing domain objects
- What just happened?
- Have a go hero – adding tests for cart
- Integration testing with the Spring Test Context framework
- Time for action – testing the product validator
- What just happened?
- Time for action – testing the product controller
- What just happened?
- Time for action – testing REST controllers
- What just happened?
- Have a go hero – adding tests for the remaining REST methods
- Summary
- A. Using the Gradle Build Tool
- Installing Gradle
- The Gradle build script for your project
- Understanding the Gradle script
- B. Pop Quiz Answers
- Chapter 2, Spring MVC Architecture – Architecting Your Web Store
- Pop quiz – request mapping
- Pop quiz – the web application context
- Pop quiz – web application context configuration
- Chapter 3, Control Your Store with Controllers
- Pop quiz – class-level request mapping
- Pop quiz – request path variable
- Pop quiz – the request parameter
- Chapter 5, Working with View Resolver
- Pop quiz – redirect view
- Pop quiz – static view
- Chapter 6, Intercept Your Store with Interceptor
- Pop quiz – interceptor
- Chapter 9, Apache Tiles and Spring Web Flow in Action
- Pop quiz – web flow
- Pop quiz – Apache Tiles
- Index
SpringMVCBeginner’sGuide
TableofContents
SpringMVCBeginner’sGuide
Credits
AbouttheAuthor
AbouttheReviewers
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmore
Whysubscribe?
FreeaccessforPacktaccountholders
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Conventions
Timeforaction–heading
Whatjusthappened?
Popquiz–heading
Haveagohero–heading
Readerfeedback
Customersupport
Downloadingtheexamplecode
Errata
Piracy
Questions
1.ConfiguringaSpringDevelopmentEnvironment
SettingupJava
Timeforaction–installingJDK
Timeforaction–settingupenvironmentvariables
Configuringabuildtool
Timeforaction–installingtheMavenbuildtool
Installingawebserver
Timeforaction–installingtheTomcatwebserver
Configuringadevelopmentenvironment
Timeforaction–installingSpringToolSuite
Timeforaction–configuringTomcatonSTS
Whatjusthappened?
Timeforaction–configuringMavenonSTS
CreatingourfirstSpringMVCproject
Timeforaction–creatingaSpringMVCprojectinSTS
Whatjusthappened?
SpringMVCdependencies
Timeforaction–addingSpringjarstotheproject
Whatjusthappened?
Timeforaction–addingJavaversionpropertiesinpom.xml
Ajump-starttoMVC
Timeforaction–addingawelcomepage
Whatjusthappened?
Thedispatcherservlet
Timeforaction–configuringthedispatcherservlet
Whatjusthappened?
Deployingourproject
Timeforaction–runningtheproject
Summary
2.SpringMVCArchitecture–ArchitectingYourWebStore
Thedispatcherservlet
Timeforaction–examiningrequestmapping
Whatjusthappened?
Popquiz–requestmapping
Thewebapplicationcontext
Timeforaction–understandingthewebapplicationcontext
Whatjusthappened?
Popquiz–thewebapplicationcontext
Thewebapplicationcontextconfiguration
Popquiz–webapplicationcontextconfiguration
Viewresolvers
Timeforaction–understandingInternalResourceViewResolver
Whatjusthappened?
ModelViewController
AnoverviewoftheSpringMVCrequestflow
Thewebapplicationarchitecture
Thedomainlayer
Timeforaction–creatingadomainobject
Whatjusthappened?
Thepersistencelayer
Timeforaction–creatingarepositoryobject
Whatjusthappened?
Theservicelayer
Timeforaction–creatingaserviceobject
Whatjusthappened?
Haveagohero–accessingtheproductdomainobjectviaaservice
Anoverviewofthewebapplicationarchitecture
Haveagohero–listingallourcustomers
Summary
3.ControlYourStorewithControllers
Definingacontroller
Timeforaction–addingclass-levelrequestmapping
Whatjusthappened?
Popquiz–class-levelrequestmapping
TheroleofacontrollerinSpringMVC
Handlermapping
UsingURItemplatepatterns
Timeforaction–showingproductsbasedoncategory
Whatjusthappened?
Popquiz–requestpathvariable
Usingmatrixvariables
Timeforaction–showingtheproductsbasedonfilter
Whatjusthappened?
Understandingrequestparameters
Timeforaction–addingtheproductdetailspage
Whatjusthappened?
Popquiz–therequestparameter
Timeforaction–implementingamasterdetailview
Whatjusthappened?
Haveagohero–addingmultiplefilterstolistproducts
Summary
4.WorkingwithSpringTagLibraries
Servingandprocessingforms
Timeforaction–servingandprocessingforms
Whatjusthappened?
Customizingdatabinding
Timeforaction–whitelistingformfields
Whatjusthappened?
Externalizingtextmessages
Timeforaction–externalizingmessages
Whatjusthappened?
UsingSpringSecuritytags
Timeforaction–addingaloginpage
Whatjusthappened?
Summary
5.WorkingwithViewResolver
Resolvingviews
Theredirectview
Timeforaction–examiningRedirectView
Whatjusthappened?
Popquiz–redirectview
Servingstaticresources
Timeforaction–servingstaticresources
Whatjusthappened?
Popquiz–staticview
Timeforaction–addingimagestotheproductdetailpage
Whatjusthappened?
Themultipartrequestinaction
Timeforaction–addingimagestotheproductpage
Whatjusthappened?
Haveagohero–uploadingproductusermanualstotheserver
UsingContentNegotiatingViewResolver
Timeforaction–configuringContentNegotiatingViewResolver
Whatjusthappened?
Workingwiththehandlerexceptionresolver
Timeforaction–addingtheresponsestatusexception
Whatjusthappened?
Timeforaction–addinganexceptionhandler
Whatjusthappened?
Summary
6.InterceptYourStorewithInterceptor
Workingwithinterceptors
Timeforaction–configuringaninterceptor
Whatjusthappened?
Popquiz–interceptor
Internationalization(i18n)
Timeforaction–addinginternationalization
Whatjusthappened?
Haveagohero–fullyinternationalizetheproductdetailpage
Auditlogging
Timeforaction–addingthedataauditinterceptor
Whatjusthappened?
Conditionalredirecting
Timeforaction–interceptingofferpagerequests
Whatjusthappened?
Summary
7.ValidateYourProductswithaValidator
Beanvalidation
Timeforaction–addingbeanvalidationsupport
Whatjusthappened?
Haveagohero–addingmorevalidationintheaddproductspage
CustomvalidationwithJSR-303/beanvalidation
Timeforaction–addingcustomvalidationsupport
Whatjusthappened?
Haveagohero–addingcustomvalidationtoacategory
Springvalidation
Timeforaction–addingSpringvalidation
Whatjusthappened?
Timeforaction–combiningSpringandbeanvalidations
Whatjusthappened?
Haveagohero–addingSpringvalidationtotheproductimage
Summary
8.GiveRESTtoYourApplicationwithAjax
IntroducingREST
Timeforaction–implementingRESTfulwebservices
Whatjusthappened?
Timeforaction–consumingRESTwebservices
Whatjusthappened?
HandlingawebserviceinAjax
Timeforaction–consumingRESTwebservicesviaAjax
Whatjusthappened?
Summary
9.ApacheTilesandSpringWebFlowinAction
WorkingwithSpringWebFlow
Timeforaction–implementingtheorder-processingservice
Whatjusthappened?
Timeforaction–implementingthecheckoutflow
Whatjusthappened?
Understandingtheflowdefinition
Understandingthecheckoutflow
Popquiz–webflow
Timeforaction–creatingviewsforeveryviewstate
Whatjusthappened?
Haveagohero–addingadecisionstate
EnhancingreusabilitythroughApacheTiles
Timeforaction–creatingviewsforeveryviewstate
Whatjusthappened?
Popquiz–ApacheTiles
Summary
10.TestingYourApplication
Unittesting
Timeforaction–unit-testingdomainobjects
Whatjusthappened?
Haveagohero–addingtestsforcart
IntegrationtestingwiththeSpringTestContextframework
Timeforaction–testingtheproductvalidator
Whatjusthappened?
Timeforaction–testingtheproductcontroller
Whatjusthappened?
Timeforaction–testingRESTcontrollers
Whatjusthappened?
Haveagohero–addingtestsfortheremainingRESTmethods
Summary
A.UsingtheGradleBuildTool
InstallingGradle
TheGradlebuildscriptforyourproject
UnderstandingtheGradlescript
B.PopQuizAnswers
Chapter2,SpringMVCArchitecture–ArchitectingYourWebStore
Popquiz–requestmapping
Popquiz–thewebapplicationcontext
Popquiz–webapplicationcontextconfiguration
Chapter3,ControlYourStorewithControllers
Popquiz–class-levelrequestmapping
Popquiz–requestpathvariable
Popquiz–therequestparameter
Chapter5,WorkingwithViewResolver
Popquiz–redirectview
Popquiz–staticview
Chapter6,InterceptYourStorewithInterceptor
Popquiz–interceptor
Chapter9,ApacheTilesandSpringWebFlowinAction
Popquiz–webflow
Popquiz–ApacheTiles
Index
SpringMVCBeginner’sGuide
SpringMVCBeginner’sGuide
Copyright©2014PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,
ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthe
publisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyofthe
informationpresented.However,theinformationcontainedinthisbookissoldwithout
warranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,andits
dealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecaused
directlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthe
companiesandproductsmentionedinthisbookbytheappropriateuseofcapitals.
However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:June2014
Productionreference:1190614
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78328-487-0
www.packtpub.com
CoverimagebyAniketSawant(<aniket_sawant_photography@hotmail.com>)
Credits
Author
AmuthanG
Reviewers
RafałBorowiec
PawanChopra
RubénClementeSerna
AcquisitionEditor
VinayArgekar
ContentDevelopmentEditor
AzharuddinSheikh
TechnicalEditors
MonicaJohn
NehaMankare
ShinyPoojary
CopyEditors
GladsonMonteiro
InsiyaMorbiwala
AdityaNair
StutiSrivastava
ProjectCoordinators
KinjalBari
WendellPalmer
Proofreaders
SimranBhogal
StephenCopestake
MariaGould
AmeeshaGreen
PaulHindle
Indexer
HemanginiBari
Graphics
DishaHaria
AbhinashSahu
ProductionCoordinator
AparnaBhagat
CoverWork
AparnaBhagat
AbouttheAuthor
AmuthanGhasoversixyearsofexperienceasaprofessionalsoftwaredeveloper.He
currentlyworksforalargecloudplatformcompanyandhasstrongproductdevelopment
experienceinJava,Spring,JPA,andmanyotherenterprisetechnologies.Inhisfreetime,
heenjoysbloggingonhissite(http://www.madebycode.in).Hecanbecontactedat
<mr.amuthan@gmail.com>.
IwouldliketogratefullyandsincerelythankMr.VincentKokforhisguidance,
understanding,patience,andmostimportantly,hisfriendshipduringmyfirstjobat
EducatorInc.Hismentorshiphasshapedmetobecomeawell-roundedprofessional.He
encouragedmetonotonlygrowasadeveloper,butalsoasanindependentthinker.
IwanttotakeamomentandexpressmygratitudetotheentireteamatPacktPublishing
fortheirpatienceandcooperation.WhenIsignedupforthisbook,Ireallyhadnoidea
howthingswouldturnout.Icouldn’thavepulledthisoffwithouttheirguidance.
Iwouldliketoexpressmygratitudetoallmyfriendsandfamilyforprovidingmewith
unendingencouragementandsupport.Ioweeverychallengeandaccomplishmenttoall
mylovelycolleagueswhotaughtmealotovertheyears.
AspecialthankstoDivyaandArunfortheirencouragement,friendship,andsupport.
Theywereastrongshouldertoleanoninthemostdifficulttimesduringthewritingof
thisbook.
Finally,andmostimportantly,IwouldliketothankmywifeManjuwhobelievesmemore
thanmyself.Hersupport,encouragement,quietpatience,andunwaveringlovewere
undeniablythebedrockuponwhichmylifehasbeenbuilt.
AbouttheReviewers
RafałBorowiecisanITspecialistwithabouteightyearsofcommercialexperience,
specializinginsoftwaretestingandqualityassurance,softwaredevelopment,project
management,andteamleadership.
HecurrentlyholdsthepositionofaTeamLeaderatGoyello,whereheismainly
responsibleforbuildingandmanagingteamsofprofessionaldevelopersandtesters.Heis
alsoresponsibleformaintainingrelationswithcustomersandacquiringnewones,mainly
throughconsultancy.
Hebelievesinagileprojectmanagementandisabigfanoftechnology,especially
technologythatisJavarelated(butnotlimitedtoit).Helikessharingknowledgeabout
softwaredevelopmentandpracticesthroughhisblog(blog.codeleak.pl)andTwitter
account(@kolorobot)andalsoatinternalandexternaleventssuchasconferencesor
workshops.
PawanChopraisanAgiledeveloperwitheightyearsofexperienceinthesoftware
industry.HecurrentlyworksatWebners(http://www.webnersolutions.com/)onsomecool
JavaScript,Java,HTML5,Node,andAngularJSprojects.Heisanopensourceenthusiast.
Helovessharingknowledgethroughtrainingandblogging.Heisalsoverystrongonthe
serversidewithvastexperienceinSpringandHibernatetools.Heblogsat
www.itspawan.com.
RubénClementeSernaisasoftwareengineerbyprofessionwithovereightyearsof
experienceinsoftwaredevelopment.HerecentlymovedtotheUKandiscurrently
workingasaJavaDeveloperatPiksel,acompanythatcreatesandmanagesOTTvideo
solutionsforsomeoftheworld’sleadingmediabrands.PriortoPiksel,hehasworkedat
GFIInformáticainSpainonmanyJavadevelopmentprojects,mainlyfortelecomand
governmentservicecustomers.
Moredetailedinformationabouthisskillsandexperiencecanbefoundat
http://www.linkedin.com/in/rubenclementeserna.Hecanbecontactedat
<rubenclemente@gmail.com>.
www.PacktPub.com
Supportfiles,eBooks,discountoffers,and
more
Youmightwanttovisitwww.PacktPub.comforsupportfilesanddownloadsrelatedto
yourbook.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFand
ePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandas
aprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwith
usat<service@packtpub.com>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signup
forarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooks
andeBooks.
http://PacktLib.PacktPub.com
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigital
booklibrary.Here,youcanaccess,readandsearchacrossPackt’sentirelibraryofbooks.
Whysubscribe?
FullysearchableacrosseverybookpublishedbyPackt
Copyandpaste,printandbookmarkcontent
Ondemandandaccessibleviawebbrowser
Preface
Thisbookhasaveryclearaim:tointroduceyoutotheincrediblesimplicityandpowerof
SpringMVC.IstillrememberfirstlearningabouttheSpringframeworkbackin2009.
Thebestwaytotestwhetherornotyoureallyunderstandaconceptistotrytoteachitto
someoneelse.Inmycase,IhavetaughtSpringMVCtoMVC;areyouconfused?Imean
thatbackin2009,ItaughtittomywifeManjuViswambaranChandrika(MVC).During
thatcourse,Iwasabletounderstandthekindofdoubtsthatariseinabeginner’smind.I
havegatheredallmyteachingknowledgeandputitinthisbookinanelegantwaysothat
itcanbeunderstoodwithoutconfusion.
Thisbookfollowsathemeofdevelopingasimplee-commercesitestep-by-step.Inevery
successivechapter,youwilllearnanewconceptofSpringMVC.Obviously,theaimisto
teachyouhowyoucanuseSpringMVCeffectively.Developingafull-blown,production-
readye-commercesiteisnotthepurposeofthisbook.
Whatthisbookcovers
Chapter1,ConfiguringaSpringDevelopmentEnvironment,willgiveyouaquick
overviewofSpringMVCanditsarchitectureandguideyouthroughdetailednotesand
step-by-stepinstructionstosetupyourdevelopmentenvironment.Afterinstallingthe
requiredprerequisites,youwilltryoutaquickexampleofhowtodevelopanapplication
withSpringMVC.Althoughthechapterdoesn’texplainallthecodeindetail,you’llpick
upafewthingsintuitively.
Chapter2,SpringMVCArchitecture–ArchitectingYourWebStore,willlaydownthe
groundworkforthesampleapplicationthatwearegoingtobuildalongtheway,chapter
bychapter.Thischapterwillintroduceyoutoconceptssuchasrequestmapping,web
applicationcontext,SpringMVCrequestflow,andthelayeredarchitectureofatypical
webapplication.
Chapter3,ControlYourStorewithControllers,willtakeyouthroughtheconceptofa
controller;youwilllearnmoreabouthowtodefineacontroller,anduseURItemplate
patterns,matrixvariables,andrequestparameters.
Chapter4,WorkingwithSpringTagLibraries,willteachyouhowtouseSpringand
Springformtaglibrariesinwebformhandling.Youwilllearnhowtobinddomainobjects
withviewsandhowtousemessagebundlestoexternalizelabelcaptiontexts.Attheend
ofthischapter,youwillseehowtoaddaloginform.
Chapter5,WorkingwithViewResolver,willpresenttheinnermechanicsofhow
InternalResourceViewResolverresolvesaviewandtakesyouthroughhowtouse
variousviewtypes,suchasredirectviewandstaticview.Youwillalsolearnaboutthe
multipartresolverandcontentnegotiationviewresolver.Finally,youwilllearnhowtouse
exceptionhandlerresolvers.
Chapter6,InterceptYourStorewithInterceptor,willpresenttheconceptofaninterceptor
toyou.Youwilllearnhowtoleveragetheinterceptortohandleortransformrequestsand
responsesflexibly.Thischapterwillteachyouhowtomakeyourwebpagesupport
internalizationwiththehelpofLocaleChangeInterceptor.Thischapteralsointroduces
howtoperformauditlogginginalogfileusingtheinterceptorconcept.
Chapter7,ValidateYourProductswithaValidator,willgiveyouanoverviewofthe
validationconcept.Youwilllearnaboutbeanvalidation,andyouwilllearnhowto
performcustomvalidationalongwiththestandardbeanvalidation.Youwillalsolearn
abouttheclassicSpringvalidationandhowtocombineitwithbeanvalidation.
Chapter8,GiveRESTtoYourApplicationwithAjax,willteachyouthebasicprinciplesof
RESTandAjax.YouwilllearnhowtodevelopanapplicationinRESTfulservices.The
basicconceptofHTTPverbsandhowtheyarerelatedtostandardCRUDoperationswill
beexplained,andyouwilllearnhowtofireanAjaxrequestandhandleitfromaweb
page.
Chapter9,ApacheTilesandSpringWebFlowinAction,willteachyouhowtousethe
Springwebflowtodevelopworkflow-basedwebpages.Youwilllearnmoreaboutstates
andtransitionsinwebflowandhowtodefineaflowdefinition.Thischapteralsoteaches
youhowtodecomposeapageusingApachetiles.Youwillalsolearnmoreabout
TileViewResolverandhowtodefinereusableApachetilestemplates.
Chapter10,TestingyourApplication,willteachyouhowtoleveragetheSpringtesting
capabilitytotestyourcontrollers.Youwilllearnhowtoloadthetestcontextandhowto
mocktheserviceandrepositorylayers.ThischapteralsointroducesyoutotheSpring
MVCtestmoduleandteachesyouhowtousethat.
AppendixA,UsingtheGradleBuildTool,introducesyoutousingtheGradlebuildtool
foroursampleapplication.YouwilllearnabouttheGradlescriptthatisrequiredtobuild
ourprojectusingGradlebuildtool.
Whatyouneedforthisbook
Toruntheexamplesinthebook,thefollowingsoftwarewillberequired:
JavaSEDevelopmentKit7u45ornewer
Maven3.1.0
ApacheTomcat7.0
STS3.4.0release(SpringToolSuite)
Whothisbookisfor
Thisbookisdesignedtobefollowedfrombeginningtoend,althoughthosewithexisting
knowledgeofSpringMVCwillbeabletojumpintothelaterchaptersandpickoutthings
thatareimportanttothem.YouarenotexpectedtobeexperiencedwiththeSpring
framework.Someknowledgeofservletprogramminganddependencyinjectionwillbe
helpfulbutnotessential.Inanutshell,thebookprovidesclearpictures,illustrations,
concepts,andisideallysuitedforbeginnersandintermediatedevelopers.
Conventions
Inthisbook,youwillfindseveralheadingsappearingfrequently.
Togiveclearinstructionsofhowtocompleteaprocedureortask,weuse:
Timeforaction–heading
1. Action1
2. Action2
3. Action3
Instructionsoftenneedsomeextraexplanationsothattheymakesense,sotheyare
followedwith:
Whatjusthappened?
Thisheadingexplainstheworkingoftasksorinstructionsthatyouhavejustcompleted.
Youwillalsofindsomeotherlearningaidsinthebook,including:
Popquiz–heading
Theseareshortmultiple-choicequestionsintendedtohelpyoutestyourown
understanding.
Haveagohero–heading
Thesepracticalchallengesgiveyouideasforexperimentingwithwhatyouhavelearned.
Youwillalsofindanumberofstylesoftextthatdistinguishbetweendifferentkindsof
information.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,
pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Once
thedownloadisfinished,gotothedownloadeddirectoryandextractthe.zipfileintoa
convenientdirectoryofyourchoice.”
Ablockofcodeissetasfollows:
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>${greeting}</h1>
<p>${tagline}</p>
</div>
</div>
</section>
</body>
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevant
linesoritemsaresetinbold:
<servlet>
<servlet-name>DefaultServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
Anycommand-lineinputoroutputiswrittenasfollows:
C:\>mvn-version
ApacheMaven3.2.1(ea8b2b07643dbb1b84b6d16e1f08391b666bc1e9;2014-02-
14T12:37:52-05:00)
Mavenhome:C:\ProgramFiles\apache-maven-3.2.1
Javaversion:1.7.0_51,vendor:OracleCorporation
Javahome:C:\ProgramFiles\Java\jdk1.7.0_51\jre
Defaultlocale:en_SG,platformencoding:Cp1252
OSname:"windows7",version:"6.1",arch:"amd64",family:"windows"
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,in
menusordialogboxesforexample,appearinthetextlikethis:“ASystemProperties
windowwillappear;inthiswindow,selecttheAdvancedtabandclickonthe
EnvironmentVariablesbuttontoopentheenvironmentvariableswindow.”
Note
Warningsorimportantnotesappearinaboxlikethis.
Tip
Tipsandtricksappearlikethis.
Readerfeedback
Feedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthis
book—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforusto
developtitlesthatyoureallygetthemostoutof.
Tosendusgeneralfeedback,simplysendane-mailto<feedback@packtpub.com>,and
mentionthebooktitlethroughthesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingor
contributingtoabook,seeourauthorguideonwww.packtpub.com/authors.
Customersupport
NowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelp
youtogetthemostfromyourpurchase.
Errata
Althoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdo
happen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthe
code—wewouldbegratefulifyouwouldreportthistous.Bydoingso,youcansave
otherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.If
youfindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-
errata,selectingyourbook,clickingontheerratasubmissionformlink,andenteringthe
detailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedand
theerratawillbeuploadedtoourwebsite,oraddedtoanylistofexistingerrata,underthe
Erratasectionofthattitle.
Piracy
PiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.At
Packt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucome
acrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswith
thelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<copyright@packtpub.com>withalinktothesuspectedpirated
material.
Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluable
content.
Chapter1.ConfiguringaSpring
DevelopmentEnvironment
Inthischapter,wearegoingtakealookathowwecancreateabasicSpringMVC
application.InordertodevelopaSpringMVCapplication,weneedsomeprerequisite
softwareandtools.First,wearegoingtolearnhowtoinstallalltheprerequisitesthatare
requiredtosetupourdevelopmentenvironmentsothatwecanstartdevelopingthe
application.
ThesetupandinstallationstepsgivenhereareforWindowsoperatingsystems,butdon’t
worry,asthestepsmaychangeonlyslightlyforotheroperatingsystems.Youcanalways
refertotherespectivetools/softwarevendor’swebsitestoinstalltheminotheroperating
systems.Inthischapter,wewilllearnhowtosetupJavaandconfiguretheMavenbuild
tool,installtheTomcatwebserver,installandconfiguretheSpringtoolsuite,andcreate
andrunourfirstSpringMVCproject.
SettingupJava
Obviously,thefirstthingthatweneedtodoisgetstartedwithJava.Themoretechnical
nameforJavaisJavaDevelopmentKit(JDK).JDKincludesaJavacompiler(javac),a
Javavirtualmachine,andavarietyofothertoolstocompileandrunJavaprograms.
Timeforaction–installingJDK
WearegoingtouseJava7butJava6oranyhigherversionisalsosufficient.Let’stakea
lookathowwecaninstallJDKonWindowsoperatingsystems:
1. GototheJavaSEdownloadpageontheOraclewebsitebyenteringthefollowing
URLinyourbrowser:
http://www.oracle.com/technetwork/java/javase/downloads/index.html.
2. ClickontheJavaplatformJDK7downloadlink;thiswilltakeyoutothelicense
agreementpage.Acceptthelicenseagreementbyselectingthatoptioninradio
button.
3. Now,clickonthelisteddownloadlinkthatcorrespondstoyourWindowsoperating
systemarchitecture;forinstance,ifyouroperatingsystemisoftype32bit,clickon
thedownloadlinkthatcorrespondstoWindowsx86.Or,ifyouroperatingsystemis
oftype64bit,clickonthedownloadlinkthatcorrespondstoWindowsx64.
4. Now,itwillstartdownloadingtheinstaller.Oncethedownloadisfinished,gotothe
downloadeddirectoryanddouble-clickontheinstaller.Thiswillopenupthe
followingwizardwindow;justclickontheNextbuttoninthewizard,leavingthe
defaultoptionsalone,andclickontheClosebuttonattheendofthewizard:
JDKinstallationwizard
Tip
Additionally,aseparatewizardalsopromptsyoutoinstallJavaRuntime
Environment(JRE).GothroughthatwizardaswelltoinstallJREinyoursystem.
5. NowyoucanseetheinstalledJDKdirectoryinthedefaultlocation;inourcase,the
defaultlocationisC:\ProgramFiles\Java\jdk1.7.0_25.
Timeforaction–settingupenvironment
variables
AfterinstallingJDK,westillneedtoperformsomemoreconfigurationstouseJava
convenientlyfromanydirectoryonourcomputer.Bysettinguptheenvironmentvariables
forJavaintheWindowsoperatingsystem,wecanmaketheJavacompilerandtools
availabletotheentireoperatingsystem:
1. NavigatetoControlPanel|System|Advancedsystemsettings.
2. ASystemPropertieswindowwillappear;inthiswindow,selecttheAdvancedtab
andclickontheEnvironmentVariablesbuttontoopentheenvironmentvariables
window.
3. Now,clickontheNewbuttonintheSystemvariablespanel,enterJAVA_HOMEasthe
variablename,andentertheinstalledJDKdirectorypathasthevariablevalue;inour
case,thisisC:\ProgramFiles\Java\jdk1.7.0_51.Incaseyoudonothaveproper
rightsfortheoperatingsystem,youwillnotbeabletoeditSystemvariables;inthat
case,youcancreatetheJAVA_HOMEvariableundertheUservariablespanel.
4. Now,inthesameSystemvariablespanel,double-clickonthePATHvariableentry;
anEditSystemVariablewindowwillappear.
SettingPATHEnvironmentvariable
5. EditVariablevalueofPathbyappendingthe;%JAVA_HOME%\bintexttoitsexisting
value.
Tip
Editthepathvariablecarefully;youshouldonlyappendthetextattheendofexisting
value.Don’tdeleteordisturbtheexistingvalues;makesureyouhaven’tmissedthe;
(semicolon)markasthatisthefirstletterinthetextthatyouwillappend.
6. NowclickontheOKbutton.
NowwehaveinstalledJavainourcomputer.Toverifywhetherourinstallationhasbeen
carriedoutcorrectly,openanewcommandwindowandtypejava–versionandpress
Enter;youwillseetheinstalledversionofJavaonthescreen:
C:\>java-version
javaversion"1.7.0_51"
Java(TM)SERuntimeEnvironment(build1.7.0_51-b13)
JavaHotSpot(TM)64-BitServerVM(build24.51-b03,mixedmode)
Configuringabuildtool
Buildingasoftwareprojecttypicallyincludessomeactivitiessuchasthefollowing:
Compilingallthesourcecode
Generatingthedocumentationfromthesourcecode
PackagingthecompiledcodeintoaJARorWARarchivefile
Installingthepackagedarchivesfilesonaserver
Manuallyperformingallthesetasksistimeconsumingandispronetoerrors.Therefore,
wetakethehelpofabuildtool.Abuildtoolisatoolthatautomateseverythingrelatedto
buildingasoftwareproject,fromcompilingtodeploying.
Timeforaction–installingtheMaven
buildtool
ManybuildtoolsareavailableforbuildingaJavaproject.WearegoingtouseMaven
3.2.1asourbuildtool.Let’stakealookathowwecaninstallMaven:
1. GotoMaven’sdownloadpagebyenteringthefollowingURLonyourbrowser:
http://maven.apache.org/download.cgi
2. Clickontheapache-maven-3.2.1-bin.zipdownloadlink,andstartthedownload.
3. Oncethedownloadisfinished,gotothedownloadeddirectoryandextractthe.zip
fileintoaconvenientdirectoryofyourchoice.
4. Nowweneedtocreateonemoreenvironmentvariable,calledM2_HOME,inawaythat
issimilartothewayinwhichwecreatedJAVA_HOME.EntertheextractedMavenzip
directory’spathasthevaluefortheM2_HOMEenvironmentvariable.
5. Createonemoreenvironmentvariable,calledM2,withthevalue%M2_HOME%\bin,as
showninthefollowingscreenshot:
SettingtheM2environmentvariable
6. FinallyappendtheM2variabletothePATHenvironmentvariableaswellbysimply
appendingthe;%M2%texttothePATHvariable’svalue.
NowwehaveinstalledtheMavenbuildtoolinourcomputer.Toverifywhetherour
installationhasbeencarriedoutcorrectly,weneedtofollowstepsthataresimilartothe
Javainstallationverification.Openanewcommandwindow,typemvn–version,and
pressEnter;youwillseethefollowingdetailsoftheMavenversion:
C:\>mvn-version
ApacheMaven3.2.1(ea8b2b07643dbb1b84b6d16e1f08391b666bc1e9;2014-02-
14T12:37:52-05:00)
Mavenhome:C:\ProgramFiles\apache-maven-3.2.1
Javaversion:1.7.0_51,vendor:OracleCorporation
Javahome:C:\ProgramFiles\Java\jdk1.7.0_51\jre
Defaultlocale:en_SG,platformencoding:Cp1252
OSname:"windows7",version:"6.1",arch:"amd64",family:"windows"
Installingawebserver
Sofar,wehavelearnedhowtoinstallJDKandMaven.Usingthesetools,wecancompile
theJavasourcecodeintothe.classfilesandpackagethese.classfilesintothe.jaror
.wararchives.However,howdowerunourpackagedarchives?Todothis,wetakethe
helpofawebserver;awebserverwillhostourpackagedarchivesasarunning
application.
Timeforaction–installingtheTomcat
webserver
ApacheTomcatisapopularJavawebservercumservletcontainer.Wearegoinguse
ApacheTomcatVersion7.0.Let’stakealookathowwecaninstalltheTomcatweb
server:
1. GototheApacheTomcathomepageusingthefollowingURLlink:
http://tomcat.apache.org/
2. ClickontheTomcat7.0downloadlink,anditwilltakeyoutothedownloadpage.
3. Clickonthe32-bit/64-bitWindowsServiceInstallerlink;itwillstartdownloading
theinstaller.
4. Oncethedownloadisfinished,gotothedownloadeddirectoryanddouble-clickon
theinstaller;thiswillopenupawizardwindow.
5. Justclickthroughthenextbuttonsinthewizard,leavingthedefaultoptionsalone,
andclickontheFinishbuttonattheendofthewizard.Notethatbeforeclickingon
theFinishbutton,justensurethatyouhaveuncheckedRunApacheTomcat
checkbox.
InstallingApacheTomcatwiththedefaultoptionworkssuccessfullyonlyifyouhave
installedJavainthedefaultlocation.Otherwise,youhavetocorrectlyprovidetheJRE
pathaccordingtothelocationofyourJavainstallationduringtheinstallationofTomcat,
asshowninthefollowingscreenshot:
TheJavaruntimeselectionfortheTomcatinstallation
Configuringadevelopmentenvironment
WeinstalledJavaandMaventocompileandpackageJavasourcecode,andweinstalled
Tomcattodeployandrunourapplication.However,priortoallthis,wehavetowritethe
SpringMVCcodesothatwecancompile,package,andrunthecode.
Wecanuseanysimpletexteditoronourcomputertowriteourcode,butthatwon’thelp
usmuchwithfeaturessuchasfindingsyntaxerrorsaswetype,autosuggestingimportant
keywords,syntaxhighlighting,easynavigation,andsoon.
IntegratedDevelopmentEnvironment(IDE)canhelpuswiththesefeaturestodevelop
thecodefasteranderrorfree.WearegoingtouseSpringToolSuite(STS)asourIDE.
Timeforaction–installingSpringTool
Suite
STSisthebestEclipse-powereddevelopmentenvironmenttobuildSpringapplications.
Let’stakealookathowwecaninstallSTS:
1. GototheSTSdownloadpageathttp://spring.io/tools/sts/all.
2. ClickontheSTSinstaller.exelinktodownloadthefilethatcorrespondstoyour
windowsoperatingsystemarchitecturetype(32bitor62bit);thiswillstartthe
downloadoftheinstaller.TheSTSstablereleaseversionatthetimeofwritingthis
bookisSTS3.4.0.RELEASEbasedonEclipse4.3.1.
3. Oncethedownloadisfinished,gotothedownloadeddirectoryanddouble-clickon
theinstaller;thiswillopenupawizardwindow.
4. Justclickthroughthenextbuttonsinthewizard,leavingthedefaultoptionsalone;if
youwanttocustomizetheinstallationdirectory,youcanspecifythatinthestepsyou
performinthewizard.
Tip
Downloadingtheexamplecode
YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchased
fromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbook
elsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethe
filese-maileddirectlytoyou.
5. Instep5ofthewizard,youhavetoprovidetheJDKpath;justentertheJDKpath
thatyouconfiguredfortheJAVA_HOMEenvironmentvariable,asshowninthe
followingscreenshot:
SettingtheJDKpathduringtheSTSinstallation
WehavealmostinstalledallthetoolsandsoftwarerequiredtodevelopaSpringMVC
application,sonow,wecancreateourSpringMVCprojectonSTS.However,before
jumpingintocreatingaproject,weneedtoperformafinalconfigurationforSTS.
Timeforaction–configuringTomcaton
STS
AsIalreadymentioned,wecanusetheTomcatwebservertodeployourapplication,but
wehavetoinformSTSaboutthelocationoftheTomcatcontainersothatwecaneasily
deployourprojectfromSTStoTomcat.Let’sconfigureTomcatonSTS:
1. OpenSTSfromthestartmenuoptionorthedesktopicon.
2. STSwillaskyoutoprovideaworkspacedirectorylocation;provideaworkspace
directorypathasyouwishandclickontheOKbutton.
3. Now,STSwillshowyouawelcomescreen.Closethewelcomescreenandgotothe
menubarandnavigatetoWindow|preferences|Server|RuntimeEnvironments.
4. Youcanseetheavailableserverslistedontheright-handside;youmayalsosee
VMwarevFabrictcServerlistedundertheavailableservers,whichcomesalong
withtheSTSinstallation.
5. NowclickontheAddbuttontoaddourTomcatwebserver.
6. Awizardwindowwillappear;typetomcatintheSelectthetypeofruntime
environment:textbox,andalistofavailableTomcatversionswillbeshown.Just
selectTomcatv7.0andselecttheCreateanewlocalservercheckbox.Finally,click
ontheNextbutton,asshowninthefollowingscreenshot:
SelectingtheservertypeduringtheTomcatconfigurationonSTS
7. Inthenextwindow,clickontheBrowsebuttonandlocateTomcat’sinstalled
directory,andclickontheOKbutton.YoucanfindTomcat’sinstalleddirectory
underC:\ProgramFiles\ApacheSoftwareFoundation\Tomcat7.0ifyouhave
installedTomcatinthedefaultlocation.Then,clickontheFinishbutton,asshownin
thefollowingscreenshot:
SelectingtheTomcatlocationduringtheTomcatconfigurationonSTS
Whatjusthappened?
Instep2,weprovidedaworkspacepathforSTS.WhenyouopenSTSfortheveryfirst
timeafterinstallingSTS,itwillaskyoutoprovideaworkspacelocation.Thisisbecause
whenyoucreateaprojectonSTS,allyourprojectfileswillbecreatedunderthislocation
only.
OnceweenterSTS,weshouldinformSTSwheretheTomcathasbeeninstalled.Only
thencanSTSuseyourTomcatwebservertodeploytheproject.Thisisalsoaone-time
configuration;youneednotperformthisconfigurationeverytimeyouopenSTS.Wedid
thisbycreatinganewserverruntimeenvironmentinstep5.AlthoughSTSmightcome
withaninternalVMwarevFabrictcServer,wechosetousetheTomcatwebserveras
ourserverruntimeenvironment.
Timeforaction–configuringMavenon
STS
WelearnedhowtoconfigureTomcatonSTS.Similarly,tobuildourproject,STSwilluse
Maven.ButwehavetotellSTSwhereMavenhasbeeninstalledsothatitcanusethe
Maveninstallationtobuildourprojects.Let’stakealookathowwecanconfigureMaven
onSTS:
1. OpenSTSifitisnotalreadyopen.
2. NavigatetoWindow|Preferences|Maven|Installations.
3. Ontheright-handside,youcanseetheAddbutton,tolocateMaven’sinstallation.
4. ClickontheAddbuttonandchooseMaven’sinstalleddirectory,asshowninthe
followingscreenshot:
SelectingMaven’slocationduringtheMavenconfigurationonSTS
5. NowclickontheOKbuttoninthePreferenceswindowandcloseit.
CreatingourfirstSpringMVCproject
Sofar,wehavelearnedhowwecaninstallalltheprerequisitetoolsandsoftware.Nowwe
aregoingtodevelopourfirstSpringMVCapplicationusingSTS.STSprovidesaneasy-
to-useprojecttemplate.Usingthesetemplates,wecanquicklycreateourprojectdirectory
structureswithoutmanyproblems.
Timeforaction–creatingaSpringMVC
projectinSTS
Let’screateourfirstspringMVCprojectinSTS:
1. InSTS,navigatetoFile|New|Project;aNewProjectwizardwindowwillappear.
2. SelectMavenProjectfromthelistandclickontheNextbutton,asshowninthe
followingscreenshot:
Mavenproject’stemplateselection
3. Now,aNewMavenProjectdialogwindowwillappear;justselectthecheckboxthat
hastheCreateasimpleproject(skiparchetypeselection)caption,andclickonthe
Nextbutton.
4. Thewizardwillaskyoutospecifyartifact-relatedinformationforyourproject;just
enterGroupIdascom.packt,ArtifactIdaswebstore.Then,selectPackagingas
warandclickontheFinishbutton,asshowninthefollowingscreenshot:
Whatjusthappened?
Wejustcreatedthebasicprojectstructure.AnyJavaprojectfollowsacertaindirectory
structuretoorganizeitssourcecodeandstaticresources.Insteadofmanuallycreatingthe
wholedirectoryhierarchybyourselves,wejusthandedoverthatjobtoSTS.Bycollecting
somebasicinformationaboutourproject,suchasGroupId,ArtifactId,andthe
Packagingstylefromus,itisclearthatSTSissmartenoughtocreatethewholeproject
directorystructurewiththehelpoftheMavenplugin.Actually,whatishappeningbehind
thescreenisthatSTSisinternallyusingMaventocreatetheprojectstructure.
Wewantourprojecttobedeployableinanyservletcontainer-basedwebserver,suchas
Tomcat,andthat’swhyweselectedthePackagingstyleaswar.Afterexecutingstep4,
youwillseetheprojectstructureinPackageExplorer,asshowninthefollowing
screenshot:
Theprojectstructureoftheapplication
SpringMVCdependencies
AswearegoingtouseSpringMVCAPIsheavilyinourproject,weneedtheSpringjars
inourprojectduringthedevelopment.AsIalreadymentioned,Mavenwilltakecareof
managingdependenciesandpackagingtheproject.
Timeforaction–addingSpringjarsto
theproject
Let’stakealookathowwecanaddthespring-relatedjarsviatheMavenconfiguration:
1. Openpom.xml;youcanfindpom.xmlundertherootdirectoryoftheprojectitself.
2. Youwillseesometabsatthebottomofthepom.xmlfile.Ifyoudonotseethesetabs,
thenright-clickonpom.xmlandselecttheOpenWithoptionfromthecontextmenu
andchooseMavenPOMeditor.SelecttheDependenciestabandclickontheAdd
buttonintheDependenciessection.Don’tgetconfusedwiththeAddbuttonofthe
DependenciesManagementsection.YoushouldchoosetheAddbuttonintheleft-
handsidepane.
3. ASelectDependencywindowwillappear;enterGroupIdas
org.springframework,ArtifactIdasspring-webmvc,andVersionas
4.0.3.RELEASE.SelectScopeascompileandthenclickontheOKbutton,asshown
inthefollowingscreenshot:
4. Similarly,addthedependencyforJavaServerPagesStandardTagLibrary(JSTL)
byclickingonthesameAddbutton;thistime,enterGroupIdasjavax.servlet,
ArtifactIdasjstl,Versionas1.2,andselectScopeascompile.
5. Finally,addonemoredependencyforservlet-api;repeatthesamestepwithGroup
Idasjavax.servlet,ArtifactIdasjavax.servlet-api,andVersionas3.1.0,but
thistime,selectScopeasprovidedandthenclickontheOKbutton.
6. Asalaststep,don’tforgettosavethepom.xmlfile.
Whatjusthappened?
IntheMavenworld,pom.xml(ProjectObjectModel)istheconfigurationfilethatdefines
therequireddependencies.Whilebuildingourproject,Mavenwillreadthatfileandtryto
downloadthespecifiedjarsfromtheMavencentralbinaryrepository.YouneedInternet
accessinordertodownloadjarsfromMaven’scentralrepository.Mavenusesan
addressingsystemtolocateajarinthecentralrepository,whichconsistsofGroupId,
ArtifactId,andVersion.
Everytimeweaddadependency,anentrywillbemadewithinthe<dependencies></
dependencies>tagsinthepom.xmlfile.Forexample,ifyougotothepom.xmltabafter
finishingstep3,youwillseeanentryforspring-mvcasfollowswithinthe
<dependencies></dependencies>tag:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
Weaddedthedependencyforspring-mvcinstep3,andinstep4,weaddedthe
dependencyforJSTL.JSTLisacollectionofusefulJSPtagsthatcanbeusedtowriteJSP
pageseasily.Finally,weneedaservlet-apijarinordertouseservlet-relatedcode;thisis
whatweaddedinstep5.
However,thereisalittledifferenceinthescopeoftheservlet-apidependencycompared
totheothertwodependencies.Weonlyneedservlet-apiwhilecompilingourproject.
Whilepackagingourprojectaswar,wedon’twanttotheshipservlet-apijaraspartofour
project.ThisisbecausetheTomcatwebserverwouldprovidetheservlet-apijarwhile
deployingourproject.Thisiswhyweselectedthescopeasprovidedfortheservlet-api.
Afterfinishingstep6,youwillseeallthedependentjarsconfiguredinyourproject,as
showninthefollowingscreenshot,undertheMavenDependencieslibrary:
Weaddedonlythreejarsasourdependencies,butifyounoticeinourMavendependency
librarylist,youwillseemorethanthreejarentries.Canyouguesswhy?Whatifour
dependentjarshaveadependencyonotherjarsandsoon?
Forexample,ourspring-mvcjarisdependentonthespring-core,spring-context,and
spring-aopjars,butwehavenotspecifiedthosejarsinourpom.xmlfile;thisiscalled
transitivedependenciesintheMavenworld.Inotherwords,wecansaythatourproject
istransitivelydependentonthesejars.Mavenwillautomaticallydownloadallthese
transitivedependentjars;thisisthebeautyofMaven.Itwilltakecareofallthe
dependencymanagementautomatically;weneedtoinformMavenonlyaboutthefirst
leveldependencies.
Timeforaction–addingJavaversion
propertiesinpom.xml
Wesuccessfullyaddedalltherequiredjarstoourproject,butweneedtoperformone
smallconfigurationinourpom.xmlfile,thatis,tellingMaventouseJavaVersion7while
buildingourproject.HowdowetellMaventodothis?Simplyaddtwopropertyentriesin
pom.xml.Let’sdothis.
1. Openpom.xml.Youwillseesometabsatthebottomofpom.xml;selecttheOverview
tabfromthebottomofpom.xml,expandthepropertiesaccordion,andclickonthe
Createbutton.
2. Now,anAddpropertywindowwillappear;enterNameasmaven.compiler.source
andValueas1.7.
AddingtheJavacompilerversionpropertiestoPOM
3. Similarly,createonemorepropertywithNameasmaven.compiler.targetand
Valueas1.7.
4. Finally,savepom.xml.
Ajump-starttoMVC
Wecreatedourprojectandaddedalltherequiredjars,sowearereadytocode.Weare
goingtoincrementallybuildanonlinewebstorethroughoutthisbook,chapterbychapter.
Asafirststep,let’screateahomepageinourprojecttowelcomeourcustomers.
Ouraimissimple;whenweenterthehttp://localhost:8080/webstore/URLonthe
browser,wewouldliketoshowawelcomepagethatissimilartothefollowing
screenshot:
Don’tworryifyouarenotabletounderstandsomeofthecode;wearegoingtotakea
lookateachconceptindetailintheupcomingchapters.Asofnow,ouraimistohave
quickhands-onexperienceofdevelopingasimplewebpageusingSpringMVC.
Timeforaction–addingawelcomepage
Tocreateandaddawelcomepage,weneedtoexecutethefollowingsteps:
1. CreateaWEB-INF/jsp/directorystructureunderthesrc/main/webapp/directory;
createajspviewfilecalledwelcome.jspunderthesrc/main/webapp/WEB-INF/jsp/
directory,andaddthefollowingcodesnippetsintoitandsaveit:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-
1">
<linkrel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<title>Welcome</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>${greeting}</h1>
<p>${tagline}</p>
</div>
</div>
</section>
</body>
</html>
2. CreateaclasscalledHomeControllerunderthecom.packt.webstore.controller
packageinthesourcedirectorysrc/main/java,andaddthefollowingcodeintoit:
packagecom.packt.webstore.controller;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.Model;
importorg.springframework.web.bind.annotation.RequestMapping;
@Controller
publicclassHomeController{
@RequestMapping("/")
publicStringwelcome(Modelmodel){
model.addAttribute("greeting","WelcometoWebStore!");
model.addAttribute("tagline","Theoneandonlyamazingwebstore");
return"welcome";
}
}
Whatjusthappened?
Instep1,wejustcreatedaJSPview;theimportantthingweneedtonoticehereisthe
<h1>tagandthe<p>tag.Boththetagshavesomeexpressionthatissurroundedbycurly
bracesandprefixedbythe$symbol:
<h1>${greeting}</h1>
<p>${tagline}</p>
So,whatisthemeaningof${greeting}?Itmeansthatgreetingisakindofvariable;
duringtherenderingofthisJSPpage,thevaluestoredinthegreetingvariablewillbe
shownintheheader1style,andsimilarly,thevaluestoredinthetaglinevariablewillbe
shownasaparagraph.
Sonow,thenextquestionofwherewewillassignvaluestothosevariablesarises.Thisis
wherethecontrollerwillbeofhelp;withinthewelcomemethodoftheHomeController
class,takealookatthefollowinglinesofcode:
model.addAttribute("greeting","WelcometoWebStore!");
model.addAttribute("tagline","Theoneandonlyamazingwebstore");
Youcanobservethatthetwovariablenames,greetingandtagline,arepassedasafirst
parameteroftheaddAttributemethodandthecorrespondingsecondparameteristhe
valueforeachvariable.Sowhatwearedoinghereissimplyputtingtwostrings,"Welcome
toWebStore!"and"Theoneandonlyamazingwebstore",intothemodelwiththeir
correspondingkeysasgreetingandtagline.Asofnow,simplyconsiderthefactthat
modelisakindofmap.Folkswithknowledgeofservletprogrammingcanconsiderthe
factthatmodel.addAttributeworksexactlylikerequest.setAttribute.
So,whatevervalueweputintothemodelcanberetrievedfromtheview(jsp)usingthe
correspondingkeywiththehelpofthe${}placeholderexpressionnotation.
Thedispatcherservlet
Wecreatedacontrollerthatcanputvaluesintothemodel,andwecreatedtheviewthat
canreadthosevaluesfromthemodel.So,themodelactsasanintermediatebetweenthe
viewandthecontroller;withthis,wehavefinishedallthecodingpartrequiredtopresent
thewelcomepage.Sowillwebeabletorunourprojectnow?No;atthisstage,ifwerun
ourprojectandenterthehttp://localhost:8080/webstore/URLonthebrowser,we
willgetanHTTPStatus404error.Thisisbecausewehavenotperformedanyservlet
mappingyet.InaSpringMVCproject,wemustconfigureafrontservletmapping.The
frontservlet(sometimescalledthefrontcontroller)mappingisadesignpatternwhereall
requestsforaparticularwebapplicationaredirectedtothesameservlet.Onesuchfront
servletgivenbySpringMVCframeworkisthedispatcherservlet
(org.springframework.web.servlet.DispatcherServlet).Wehavenotconfigureda
dispatcherservletforourprojectyet;thisiswhywegettheHTTPStatus404error.
Timeforaction–configuringthe
dispatcherservlet
ThedispatcherservletiswhatexaminestheincomingrequestURLandinvokestheright
correspondingcontrollermethod.Inourcase,thewelcomemethodfromthe
HomeControllerclassneedstobeinvokedifweenterthe
http://localhost:8080/webstore/URLonthebrowser.Solet’sconfigurethe
dispatcherservletforourproject:
1. Createweb.xmlunderthesrc/main/webapp/WEB-INF/directoryinyourprojectand
enterthefollowingcontentinsideweb.xmlandsaveit:
<web-appversion="3.0"xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>DefaultServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DefaultServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2. NowcreateonemorexmlfilecalledDefaultServlet-servlet.xmlunderthesame
src/main/webapp/WEB-INF/directoryandenterthefollowingcontentintoitand
saveit:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven/>
<context:component-scanbase-package="com.packt.webstore"/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver
">
<propertyname="prefix"value="/WEB-INF/jsp/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
</beans>
Whatjusthappened?
Ifyouknowaboutservletprogramming,youmightbequitefamiliarwiththeservlet
configurationandweb.xml.Inweb.xml,weconfiguredaservletnamedDefaultServlet,
whichismoreorlesssimilartoanyothernormalservletconfiguration.Theonly
differenceisthatwehavenotcreatedanyservletclassforthatconfiguration.Instead,the
servletclass(org.springframework.web.servlet.DispatcherServlet)isprovidedby
theSpringMVCframework,andwemakeuseofitinweb.xml.Afterthisstep,our
configuredDispatcherServlet(DefaultServlet)willbereadytohandleanyrequests
thatcometoourapplicationonruntimeandwilldispatchtherequesttothecorrect
controller’smethod.
However,DispatcherServletshouldknowwhereourcontrollersandviewfilesare
locatedinourproject,andonlythencanitproperlydispatchtherequesttothecorrect
controllers.SowehavetogivesomehinttoDispatcherServlettolocatethecontrollers
andviewfiles.Thisiswhatweconfiguredinstep2throughtheDispatcherServlet-
servlet.xmlfile.
Don’tworryifyouarenotabletounderstandeachandeveryconfigurationinweb.xmland
DispatcherServlet-servlet.xml;wewilltakealookattheseconfigurationfilesinnext
chapter.Asofnow,justrememberthatthisisaone-timeconfigurationthatisneededto
runourprojectsuccessfully.
Deployingourproject
Wesuccessfullycreatedtheprojectinthelastsection,soyoumightbecurioustoknow
whatwouldhappenifwerunourprojectnow.Asourprojectisawebproject,weneeda
webservertorunit.
Timeforaction–runningtheproject
AswealreadyconfiguredtheTomcatwebserverinourSTS,let’suseTomcattodeploy
andrunourproject:
1. Right-clickonyourprojectfromPackageExplorerandnavigatetoRunAs|Run
onServer.
2. Aserverselectionwindowwillappearwithalltheavailableserverslisted;justselect
theserverthatwehaveconfigured,Tomcatv7.0.
3. Atthebottomofthewindow,youcanseeacheckboxwiththecaptionthatsays
Alwaysusethisserverwhenrunningthisproject;selectthischeckboxandenter
theFinishbutton,asshowninthefollowingscreenshot:
ConfiguringthedefaultserverforaSpringMVCproject
4. Nowyouwillseeawebpagethatwillshowyouawelcomemessage.
Summary
Inthischapter,wesawhowtoinstallalltheprerequisitesthatareneededtogetstartedand
runourfirstSpringMVCapplication,forexample,installingJDK,theMavenbuildtool,
theTomcatservletcontainer,andSTSIDE.
WealsolearnedhowtoperformvariousconfigurationsinourSTSIDEforMavenand
Tomcat,createdourfirstSpringMVCproject,andaddedallSpring-relateddependentjars
throughtheMavenconfiguration.
Wehadaquickhands-onexperienceofdevelopingawelcomepageforourwebstore
application.Duringthatcourse,welearnedhowtoputvaluesintoamodelandhowto
retrievethesevaluesfromthemodel.
WhateverwehaveseensofarisjustaglimpseofSpringMVC,butthereismuchmoreto
uncover,forexample,howthemodelandviewcontrollerareconnectedtoeachotherand
howtherequestflowoccurs.Wearegoingtoexplorethesetopicsinthenextchapter,so
seeyouthere!
Chapter2.SpringMVCArchitecture–
ArchitectingYourWebStore
WhatwesawinthefirstchapterisnothingbutaglimpseofSpringMVC;intheprevious
chapter,ourtotalfocuswasjustongettingittorunaSpringMVCapplication.Now,it’s
timeforustodeep-diveintoSpringMVCarchitecture.
Bytheendofthischapter,youwillhaveaclearunderstandingof:
Thedispatcherservletandrequestmapping
Thewebapplicationcontextandconfiguration
TheSpringMVCrequestflowandWebMVC
Thewebapplicationarchitecture
Thedispatcherservlet
Inthefirstchapter,wewereintroducedtothedispatcherservletandsawhowtodefinea
dispatcherservletinweb.xml.Welearnedthateverywebrequestfirstcomestothe
dispatcherservlet.Thedispatcherservletistheonethatdecidesthecontrollermethodthat
itshoulddispatchthewebrequestto.Inthepreviouschapter,wecreatedawelcomepage
thatwillbeshownwheneverweentertheURLhttp://localhost:8080/webstore/on
thebrowser.MappingaURLtotheappropriatecontrollermethodistheprimarydutyofa
dispatcherservlet.
SothedispatcherservletreadsthewebrequestURLandfindstheappropriatecontroller
methodthatcanservethatwebrequestandinvokesit.Thisprocessofmappingaweb
requesttoaspecificcontrollermethodiscalledrequestmapping,andthedispatcher
servletisabletodothiswiththehelpofthe@RequestMappingannotation
(org.springframework.web.bind.annotation.RequestMapping).
Timeforaction–examiningrequest
mapping
Let’sobservewhatwillhappenwhenyouchangethevalueattributeofthe
@RequestMappingannotationbyexecutingthefollowingsteps:
1. OpenyourSTSandrunthewebstoreproject;justright-clickonyourprojectand
chooseRunAs|RunonServer.Youwillbeabletoviewthesamewelcome
messageonthebrowser.
2. Now,gototheaddressbarofthebrowserandentertheURL,
http://localhost:8080/webstore/welcome.
3. YouwillseetheHTTPStatus404errorpageonthebrowser,andyouwillalsosee
thefollowingwarningintheconsole:
WARNING:NomappingfoundforHTTPrequestwithURI[/webstore/welcome]
inDispatcherServletwithname'DefaultServlet'
Anerrorlogdisplayingthe“Nomappingfound”warningmessage
4. Now,opentheHomeControllerclass,changethe@RequestMappingannotation’s
valueattributeto/welcome,andsaveit.Basically,yournewrequestmapping
annotationwilllooklike@RequestMapping("/welcome").
5. Again,runtheapplicationandenterthesameURLthatyouenteredinstep2;now
youwillbeabletoseethesamewelcomemessageonthebrowser,withoutany
requestmappingerror.
6. Finally,opentheHomeControllerclassandrevertthechangesthatweremadetothe
@RequestMappingannotation’svalue;justmakeit@RequestMapping("/")againand
saveit.
Whatjusthappened?
Afterstartingourapplication,whenweentertheURL
http://localhost:8080/webstore/welcomeonthebrowser,thedispatcherservlet
(org.springframework.web.servlet.DispatcherServlet)immediatelytriestofinda
matchingcontrollermethodfortherequestpath,/welcome.
Tip
InaSpringMVCapplication,theURLcanlogicallybedividedintofiveparts(seethe
followingfigure);the@RequestMappingannotationonlymatchesagainsttheURLrequest
path.Itomitsthescheme,hostname,applicationname,andsoon.
The@RequestMappingannotationhasonemoreattributecalledmethodtofurthernarrow
downthemappingbasedontheHTTPrequestmethodtypes(GET,POST,HEAD,OPTIONS,
PUT,DELETE,andTRACE).Ifwedonotspecifythemethodattributeinthe
@RequestMappingannotation,thedefaultmethodwillbeGET.Wewilllearnmoreabout
themethodattributeofthe@RequestMappingannotationinChapter4,WorkingwithSpring
TagLibraries,underthesectiononformprocessing.
ThelogicalpartsofatypicalSpringMVCapplicationURL
Sincewedon’thaveacorrespondingrequestmappingforthegivenURLpath,/welcome,
wegettheHTTPStatus404erroronthebrowserandthefollowingerrorlogonthe
console:
WARNING:NomappingfoundforHTTPrequestwithURI[/webstore/welcome]in
DispatcherServletwithname'DefaultServlet'
Fromtheerrorlog,wecanclearlyunderstandthatthereisnorequestmappingforthe
URLpath,/webstore/welcome.So,wetrytomapthisURLpathtotheexistingcontroller
method;that’swhy,instep4,weputonlytherequestpathvalue,/welcome,inthe
@RequestMappingannotationasthevalueattribute.Noweverythingworksperfectlyfine.
Finally,werevertedour@RequestMappingannotation’svalueto/againinstep6.Whydid
wedothis?BecausewewantittoshowthewelcomepageunderthewebrequestURL
http://localhost:8080/webstore/again.Observecarefullythatherethelastsingle
character/istherequestpath.Wewillseemoreaboutrequestmappinginupcoming
chapters.
Popquiz–requestmapping
Q1.IfwehaveaSpringMVCapplicationforlibrarymanagementcalledBookPediaand
wanttomapawebrequestURL,
http://localhost:8080/BookPedia/category/fiction,toacontrollermethod,how
willweformthe@RequestMappingannotation?
1. @RequestMapping("/fiction").
2. @RequestMapping("/category/fiction").
3. @RequestMapping("/BookPedia/category/fiction").
Thewebapplicationcontext
InaSpring-basedapplication,ourapplicationobjectslivewithinanobjectcontainer.This
containercreatesobjectsandassociationsbetweenobjects,andmanagestheircomplete
lifecycle.ThesecontainerobjectsarecalledSpring-managedbeans(orsimplybeans),and
thecontaineriscalledanapplicationcontextintheSpringworld.
ASpringcontainerusesdependencyinjection(DI)tomanagethebeansthatmakeupan
application.Anapplicationcontext
(org.springframework.context.ApplicationContext)createsbeansandassociate
beanstogetherbasedonthebeanconfigurationanddispensesbeansonrequest.Abean
configurationcanbedefinedviaanXMLfile,annotation,orevenviaJavaconfiguration
classes.WewilluseonlyXML-andannotation-basedbeanconfigurationsinourchapters.
Awebapplicationcontextistheextensionofanapplicationcontext,designedtowork
withthestandardservletcontext(javax.servlet.ServletContext).Awebapplication
contexttypicallycontainsfrontend-relatedbeans,suchasviewsandviewresolvers.Inthe
firstchapter,wecreatedanXMLfilecalledDefaultServlet-servlet.xml,whichis
nothingbutabeanconfigurationfileforourwebapplicationcontext.
Timeforaction–understandingtheweb
applicationcontext
Youhavereceivedenoughofanintroductiononthewebapplicationcontext;now,tweaka
littlebitwiththenameandlocationofthewebapplicationcontextconfigurationfile
(DefaultServlet-servlet.xml)andobservetheeffect.Performthefollowingsteps:
1. RenametheDefaultServlet-servlet.xmlfiletoDispatcherServlet-
servlet.xml;youcanfindDefaultServlet-servlet.xmlunderthe
src/main/webapp/WEB-INF/directory.
2. Then,runyourwebstoreprojectagainandentertheURL,
http://localhost:8080/webstore/;youwillseeanHTTPStatus500error
messageonyourwebpageandaFileNotFoundExceptionerrorinthestacktraceas
follows:
java.io.FileNotFoundException:CouldnotopenServletContextresource
[/WEB-INF/DefaultServlet-servlet.xml]
AnerrormessagedisplayingFileNotFoundExceptionforDefaultServlet-servlet.xml
3. Tofixthiserror,changethenameofDefaultServlettoDispatcherServletin
web.xml;basically,afterchangingthenametoDispatcherServlet,yourservlet
configurationwilllooklikethefollowingintheweb.xmlfile:
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4. Now,runyourapplicationandentertheURL,http://localhost:8080/webstore/;
youwillseethewelcomemessageagain.
5. RenameyourDispatcherServlet-servlet.xmlfiletoDispatcherServlet-
context.xmloncemore.
6. Next,createadirectorystructurespring/webcontext/undertheWEB-INFdirectory
andmovetheDispatcherServlet-context.xmlfiletothesrc/main/webapp/WEB-
INF/spring/webcontext/directory.
7. Then,runyourapplication,andyouwillseeanHTTPStatus500errormessageon
yourwebpageagainandaFileNotFoundExceptionerrormessageinthestacktrace:
java.io.FileNotFoundException:CouldnotopenServletContextresource
[/WEB-INF/DispatcherServlet-servlet.xml]
8. Tofixthiserror,addthefollowingtagswithinthe<servlet>and</servlet>tags
inweb.xmlasshowninthefollowingcode:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/webcontext/DispatcherServlet-context.xml
</param-value>
</init-param>
9. Now,runtheapplicationagainandentertheURL,
http://localhost:8080/webstore/;youwillbeabletoseethewelcomemessage
again.
Whatjusthappened?
So,whatwedidfirstwasrenamedtheDefaultServlet-servlet.xmlfileto
DispatcherServlet-servlet.xml,andwegotaFileNotFoundExceptionerrorat
runtime,asfollows:
java.io.FileNotFoundException:CouldnotopenServletContextresource
[/WEB-INF/DefaultServlet-servlet.xml]
Tofixtheerror,wechangedourdispatcherservletconfiguration,asfollows,inthe
web.xmlfile:
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
WechangedtheservletnametoDispatcherServletinordertoalignwiththeweb
applicationcontextconfigurationfilenamedDispatcherServlet-servlet.xml.So,based
onthisexercise,wecanlearnthatduringthestart-upofanySpringMVCproject,the
dispatcherservletwilllookforawebapplicationcontextconfigurationfileofthepattern
<ConfigureddispatcherServletName>-servlet.xmlundertheWEB-INFdirectory.It
isourresponsibilitytokeepthewebapplicationcontextconfigurationfileundertheWEB-
INFdirectorywiththerightname.However,whatifwewishtokeepthefileinsomeother
directory?
Tip
Oneoftheimportantthingstobenotedin<servlet-mapping>isthevalueofthe<url-
pattern>/</url-pattern>tag.Byassigning/astheURLpatternforthedispatcher
servlet,wemakeDispatcherServletthedefaultservletforourwebapplication.So,
everywebrequestcomingtoourwebapplicationwillbehandledbyDispatcherServlet.
Forinstance,insteps5and6,werenamedthewebapplicationcontextconfigurationfile
andmovedittoacompletelynewdirectory(src/main/webapp/WEB-
INF/spring/webcontext/).Inthatcase,howdidwefixtheHTTPStatus500error?The
answerlieswithinapropertycalledcontextConfigLocation.Forthedispatcherservletto
locatethewebcontextconfigurationfileeasily,wegavethelocationofthisfiletothe
dispatcherservletthroughapropertycalledcontextConfigLocation.That’swhywe
addedthispropertytothedispatcherservletinstep8,asfollows:
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/webcontext/DispatcherServlet-context.xml
</param-value>
</init-param>
</servlet>
Now,weareabletorunourapplicationwithoutanyproblem.Okay,weplayedalotwith
thewebapplicationcontextconfigurationfileandlearnedthatthedispatcherservlet
shouldknowaboutthewebapplicationcontextconfigurationfileduringthestart-upof
ourproject.Sothenextquestionis:whyisthedispatcherservletlookingforthisweb
contextconfigurationfile,andwhatisdefinedinsidethisfile?Let’sfindouttheanswer,
butbeforethat,youmayanswerthefollowingpopquizquestionstomakesureyou
understandtheconceptofthewebapplicationcontextconfiguration.
Popquiz–thewebapplicationcontext
Q1.IfthecontextConfigLocationpropertywasnotconfiguredinourdispatcherservlet
configuration,underwhichlocationwouldSpringMVClookforthewebapplication
contextconfigurationfile?
1. IntheWEB-INFdirectory
2. InWEB-INF/spring
3. InWEB-INF/spring/appServlet
Q2.IfwedonotwanttoprovidecontextConfigLocationtothefollowingdispatcher
servletconfiguration,howdoweavoidtheHTTPStatus500error?
<servlet>
<servlet-name>FrontController</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
1. BycreatingacontextfilecalledFrontController-context.xmlintheWEB-INF
directory
2. BycreatingafilecalledDispatcherServlet-context.xmlinWEB-INF
3. BycreatingafilecalledFrontController-servlet.xmlinWEB-INF
Thewebapplicationcontextconfiguration
Thewebapplicationcontextconfigurationfile(DispatcherServlet-context.xml)is
nothingbutasimpleSpringbeanconfigurationfile.Springwillcreatebeans(objects)for
everybeandefinitionmentionedinthisfileduringbootupofourapplication.Ifyouopen
thiswebapplicationcontextconfigurationfile(/WEB-
INF/spring/webcontext/DispatcherServlet-context.xml),youwillfindsome
configurationandbeandefinitionasfollows:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven/>
<context:component-scanbase-package="com.packt.webstore"/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="prefix"value="/WEB-INF/jsp/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
</beans>
Thefirsttagwithinthe<beans>definitionis<mvc:annotation-driven/>.Bythistag,
wetellSpringMVCtoconfiguretheDefaultAnnotationHandlerMapping,
AnnotationMethodHandlerAdapter,andExceptionHandlerExceptionResolverbeans.
ThesebeansarerequiredforSpringMVCtodispatchrequeststothecontrollers.
Actually<mvc:annotation-driven/>doesmanythingsbehindthescreen.Italso
enablessupportforvariousconvenientannotationssuchas@NumberFormatand
@DateTimeFormattoformattheformbeanfieldsduringformbinding.Similarly,wehave
the@Validannotationtovalidatethecontrollermethod’sparameters.ItalsosupportsJava
objectsto/fromanXMLorJSONconversionviathe@RequestBodyand@ResponseBody
annotationsinthe@RequestMappingor@ExceptionHandlermethodduringformbinding.
Wewillseetheusageoftheseannotationsinlaterchapters.Asofnow,justrememberthat
the<mvc:annotation-driven/>tagisneededtoenableannotationssuchas
@controllerand@RequestMapping.
Whatisthepurposeofthesecondtag,<context:component-scan>?Youneedabitof
backgroundinformationtounderstandthepurposeofthe<context:component-scan>tag.
The@Controllerannotationindicatesthataparticularclassservestheroleofacontroller.
Wealreadylearnedthatthedispatcherservletsearchessuchannotatedclassesformapped
methods(the@RequestMappingannotatedmethods)toserveawebrequest.Inorderto
makethecontrolleravailableforsearching,weneedtocreateabeanforthiscontrollerin
ourwebapplicationcontext.
Wecancreatebeansforcontrollersexplicitlyviathebeanconfiguration(usingthe<bean>
tag—youcanseehowwecreatedabeanfortheInternalResourceViewResolverclass
usingthe<bean>taginthenextsection),orwecanhandoverthattasktoSpringviathe
autodetectionmechanism.Toenabletheautodetectionofthe@Controllerannotated
classes,weneedtoaddcomponentscanningtoourconfigurationusingthe
<context:component-scan>tag.Now,youfinallyunderstandthepurposeofthe
<context:component-scan>tag.
Springwillcreatebeans(objects)forevery@Controllerclassatruntime.Thedispatcher
servletwillsearchforthecorrectrequestmappingmethodinevery@Controllerbean
basedonthe@RequestMappingannotation,toserveawebrequest.Thebase-package
propertyofa<context:component-scan>tagindicatesthepackageunderwhichSpring
shouldsearchforcontrollerclassestocreatebeans:
<context:component-scanbase-package="com.packt.webstore"/>
TheprecedinglineinstructsSpringtosearchforcontrollerclasseswithinthe
com.packt.webstorepackageanditssubpackages.
Tip
The<context:component-scan>tagnotonlyrecognizescontrollerclasses,italso
recognizesotherstereotypessuchasservicesandrepositoryclassesaswell.Wewilllearn
moreaboutservicesandrepositorieslater.
Popquiz–webapplicationcontextconfiguration
Q1.WhatneedstobedonetoidentifyaclassbySpringasacontroller?
1. Thatparticularclassshouldhavethe@Controllerannotation.
2. The<mvc:annotation-driven/>and<context:component-scan>tagsshouldbe
specifiedinthewebapplicationcontextconfigurationfile.
3. Thatparticularclassshouldbeputupinapackageorsubpackagethathasbeen
specifiedasabasepackageinthe<context:component-scan>tag.
4. Alloftheabove.
Viewresolvers
Wesawthepurposeofthefirsttwotagsthatarespecifiedwithinthewebapplication
contextconfigurationfile:
<mvc:annotation-driven/>
<context:component-scanbase-package="com.packt.webstore"/>
Basedonthesetags,Springcreatesthenecessarybeanstohandleawebrequestandalso
createsbeansforallthe@Controllerclasses.However,torunaSpringMVCapplication
successfully,Springneedsonemorebean;thisbeaniscalledaviewresolver.
Aviewresolverhelpsthedispatcherservletidentifytheviewsthathavetoberenderedas
theresponseforaspecificwebrequest.SpringMVCprovidesvariousviewresolver
implementationstoidentifyviews,andInternalResourceViewResolverisonesuch
implementation.Thefinaltaginthewebapplicationcontextconfigurationisthebean
definitionfortheInternalResourceViewResolverclassasfollows:
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="prefix"value="/WEB-INF/jsp/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
Throughtheprecedingbeandefinitioninthewebapplicationcontextconfiguration,we
instructSpringMVCtocreateabeanfortheInternalResourceViewResolverclass
(org.springframework.web.servlet.view.InternalResourceViewResolver).Wewill
learnmoreabouttheviewresolverinChapter5,WorkingwithViewResolver.
Timeforaction–understanding
InternalResourceViewResolver
WeinstructSpringtocreateabeanforanInternalResourceViewResolverclass,but
why?Whoisgoingtousethisbean?Whatistheroleofthe
InternalResourceViewResolverbeaninSpringMVC?Findtheanswertothese
questionsthroughthefollowingexercise:
1. OpenDispatcherServlet-context.xml;youcanfindthisfileunderthe
src/main/webapp/WEB-INF/spring/webcontext/directoryinyourproject.
2. ChangetheprefixpropertyvalueoftheInternalResourceViewResolverbeanas
follows:
<propertyname="prefix"value="/WEB-INF/views/"/>
3. Now,runyourwebstoreprojectagainandentertheURL
http://localhost:8080/webstore/.YouwillseeanHTTPStatus404error
messageinyourbrowserasshowninthefollowingscreenshot:
Anerrorpagedisplayingthenoresourcefoundmessage
4. Then,renamethejspdirectory(/src/main/webapp/WEB-INF/jsp)toviews.
5. Finally,runyourapplicationandentertheURL,
http://localhost:8080/webstore/.Youwillseethewelcomemessageagain.
Whatjusthappened?
AfterchangingtheprefixpropertyvalueoftheInternalResourceViewResolverbean,
wegotanHTTPStatus404errorwhenweenteredtheURL,
http://localhost:8080/webstore/,inthebrowser.TheHTTPStatus404errormeans
thattheservercouldnotfindthewebpagethatweaskedfor.Ifthatisthecase,thenwhich
webpagedidweaskfor?
Asamatteroffact,wedidn’taskforanywebpagefromtheserverdirectly;instead,the
dispatcherservletasksaparticularwebpagefromtheserver.Whatwealreadylearnedis
thatthedispatcherservletinvokesamethodinanyofthecontrollerbeansthatcanserve
thiswebrequest.Inourcase,thismethodisnothingbutthewelcomemethodofour
HomeControllerclass,becausethisistheonlyrequestmappingmethodthatcanmatch
therequestpathofthegivenURL,http://localhost:8080/webstore/,inits
@RequestMappingannotation.
Now,observethefollowing:
TheprefixpropertyvalueoftheInternalResourceViewResolverbeandefinitionin
DispatcherServlet-context.xml;thatis,/WEB-INF/views/
ThereturnvalueofthewelcomemethodfromtheHomeControllerclass;thatis,
welcome
Finally,thesuffixpropertyvalueoftheInternalResourceViewResolverbean,that
is,.jsp
Ifyoucombinethesethreevaluestogether,youwillgetawebpagerequestURL:/WEB-
INF/views/welcome.jsp.Now,notetheerrormessageinthepreviousscreenshot,
showingtheHTTPStatus404errorforthesamewebpageURL:/WEB-
INF/views/welcome.jspundertheapplicationname,webstore/.
So,theconclusionisthatInternalResourceViewResolverresolvestheactualviewfile
pathbyprependingtheconfiguredprefixvalueandappendingthesuffixvaluewiththe
viewname—theviewnameisthevalueusuallyreturnedbythecontrollermethod.So,the
controllermethoddoesn’treturnthepathoftheactualviewfile;itreturnsonlythelogical
viewname.ItisthejobofInternalResourceViewResolvertoformtheURLoftheactual
viewfilecorrectly.
WhoisgoingtousethisfinalformedURL?Theansweristhedispatcherservlet.After
gettingthefinalformedURLoftheviewfilefromtheviewresolver,thedispatcherservlet
willtrytogettheviewfilefromtheserver.Duringthistime,iftheformedURLisfound
tobewrong,thenyouwillgettheHTTPStatus404error.
Usually,afterinvokingthecontrollermethod,thedispatcherservletwillwaittogetthe
logicalviewnamefromit.Oncethedispatcherservletgetsthelogicalviewname,itgives
thisnametotheviewresolver(InternalResourceViewResolver)togettheURLpathof
theactualviewfile;oncetheviewresolverreturnstheURLpathtothedispatcherservlet,
therenderedviewfileisservedtotheclientbrowserasawebpagebythedispatcher
servlet.
However,whydidwegettheerrorinstep3?Sincewechangedtheprefixpropertyofthe
InternalResourceViewResolverbeaninstep2,theURLpathvaluereturnedfrom
InternalResourceViewResolverbecame/WEB-INF/views/welcome.jspinstep3,which
isaninvalidpathvalue(thereisnodirectorycalledviewsunderWEB-INF).That’swhy,we
renamedthedirectoryjsptoviewsinstep4toalignitwiththepathgeneratedby
InternalResourceViewResolversothateverythingworksfineagain.
ModelViewController
Sofar,wehaveseenlotsofconcepts,suchasthedispatcherservlet,requestmapping,
controllers,andviewresolver;itwouldbegoodtoseetheoverallpictureoftheSpring
MVCrequestflowsothatwecanunderstandeachcomponent’sresponsibilities.However,
beforethat,weneedtounderstandtheModelViewController(MVC)conceptsome
more.Everyenterprise-levelapplication’spresentationlayercanlogicallybedividedinto
thefollowingthreemajorparts:
Thepartthatmanagesthedata(Model)
Thepartthatcreatestheuserinterfaceandscreens(View)
Thepartthathandlesinteractionsbetweentheuser,userinterface,anddata
(Controller)
Thefollowingdiagramwillhelpyouunderstandtheeventflowandcommandflowwithin
anMVCpattern:
TheclassicMVCpattern
Wheneverauserinteractswiththeviewbyclickingonalinkorbutton,theviewissuesan
eventnotificationtothecontroller,andthecontrollerissuesacommandnotificationtothe
modeltoupdatethedata.Similarly,wheneverthedatainthemodelgetsupdatedor
changed,achangenotificationeventisissuedtotheviewbythemodelinresponse,and
theviewissuesastatequerycommandtothemodeltogetthelatestdatafromthemodel.
Here,themodelandviewcaninteractdirectly;thispatterniscalledtheclassicMVC
pattern.However,whatSpringMVCemploysissomethingcalledawebMVCpattern
duetothelimitationsintheHTTPprotocol.
Tip
WebapplicationsrelyontheHTTPprotocol,whichisastatelesspullprotocol.Thismeans
thatnorequestimpliesnoreply;everytime,weneedtorequesttheapplicationtoknowits
state.TheMVCdesignpatternrequiresapushprotocolfortheviewstobenotifiedbythe
model.SoinwebMVC,thecontrollertakesmoreresponsibilityforthestatechanging,
statequerying,andchangenotification.
InwebMVC,everyinteractionbetweenthemodelandviewistakenthroughthe
controlleronly.So,thecontrolleractsasabridgebetweenthemodelandview.Thereisno
directinteractionbetweenthemodelandview,asintheclassicMVCpattern.
AnoverviewoftheSpringMVCrequest
flow
ThemainentrypointforawebrequestinaSpringMVCapplicationisviathedispatcher
servlet.Thedispatcherservletactsasthefrontcontrolleranddispatchestherequeststothe
othercontroller.Thefrontcontroller’smaindutyistofindtheappropriatecontrollerto
handovertherequestforfurtherprocessing.Thefollowingdiagramshowsanoverviewof
therequestflowinaSpringMVCapplication:
TheSpringMVCrequestflow
Now,let’sreviewtheSpringMVCrequestflowinshort:
1. WhenweenteraURLinthebrowser,therequestcomestothedispatcherservlet.
Thedispatcherservletthenactsasacentralizedentrypointtothewebapplication.
2. Thedispatcherservletdeterminesasuitablecontrollerthatiscapableofhandlingthe
requestanddispatchingthisrequesttothecontroller.
3. Thecontrollermethodupdatesobjectsinthemodelandreturnsthelogicalviewname
andupdatedmodeltothedispatcherservlet.
4. Thedispatcherservletconsultswiththeviewresolvertodeterminewhichviewto
renderandpassesthemodeldatatothatview.
5. Theviewfurnishesthedynamicvaluesinthewebpageusingthemodeldata,renders
thefinalwebpage,andreturnsthiswebpagetothedispatcherservlet.
6. Attheend,thedispatcherservletreturnsthefinal,renderedpageasaresponsetothe
browser.
Thewebapplicationarchitecture
Now,weunderstandtheoverallrequestflowandresponsibilityofeachcomponentina
typicalSpringMVCapplication.However,thisisnotenoughforustobuildanonlineweb
storeapplication.Wealsoneedtoknowthebestpracticestodevelopanenterprise-level
webapplication.Oneofthebestpracticesinatypicalwebapplicationistoorganize
sourcecodeintolayers,whichwillimprovereusabilityandloosecoupling.Atypicalweb
applicationnormallyhasfourlayers:thepresentation,domain,services,andpersistence.
Sofar,whateverwehaveseen,suchasthedispatcherservlet,controllers,viewresolvers,
andsoon,isconsideredapartofthepresentationlayercomponents.Let’sunderstandthe
remaininglayersandcomponentsonebyone.
Thedomainlayer
Let’sstartwiththedomainlayer.Adomainlayertypicallyconsistsofadomainmodel.So,
whatisadomainmodel?Adomainmodelisarepresentationofthedatastoragetypes
requiredbythebusinesslogic.Itdescribesthevariousdomainobjects(entities);their
attributes,roles,andrelationships;plustheconstraintsthatgoverntheproblemdomain.
Takealookatthefollowingdomainmodeldiagramfororderprocessingtogetaquick
ideaaboutthedomainmodel:
Sampledomainmodel
Eachblockintheprecedingdiagramrepresentsabusinessentity,andthelinesrepresent
theassociationsbetweentheentities.Basedontheprecedingdomainmodeldiagram,we
canunderstandthat,inanorderprocessingdomain,acustomercanhavemanyorders,
eachordercanhavemanyorderitems,andeachorderitemrepresentsasingleproduct.
Duringcoding,thedomainmodelwillbeconvertedintocorrespondingdomainobjects
andassociationsbyadeveloper.Adomainobjectisalogicalcontainerofpuredomain
information.Sincewearegoingtobuildanonlinewebstoreapplication,inourdomain,
theprimarydomainobjectmightbeaproduct.So,let’sstartwiththedomainobjectto
representaproduct.
Timeforaction–creatingadomainobject
Sofar,inyourwebstore,youhaveshowedonlyawelcomemessage.Itisnowtimefor
youtoshowyourfirstproductonthewebpage.Dothisbycreatingadomainobject,as
follows,torepresenttheproductinformation:
1. CreateaclasscalledProductunderthecom.packt.webstore.domainpackageinthe
sourcefoldersrc/main/java.Now,addthefollowingcodeintoit:
packagecom.packt.webstore.domain;
importjava.math.BigDecimal;
publicclassProduct{
privateStringproductId;
privateStringname;
privateBigDecimalunitPrice;
privateStringdescription;
privateStringmanufacturer;
privateStringcategory;
privatelongunitsInStock;
privatelongunitsInOrder;
privatebooleandiscontinued;
privateStringcondition;
publicProduct(){
super();
}
publicProduct(StringproductId,Stringname,BigDecimalunitPrice){
this.productId=productId;
this.name=name;
this.unitPrice=unitPrice;
}
//addsettersandgettersforallthefieldshere
@Override
publicbooleanequals(Objectobj){
if(this==obj)
returntrue;
if(obj==null)
returnfalse;
if(getClass()!=obj.getClass())
returnfalse;
Productother=(Product)obj;
if(productId==null){
if(other.productId!=null)
returnfalse;
}elseif(!productId.equals(other.productId))
returnfalse;
returntrue;
}
@Override
publicinthashCode(){
finalintprime=31;
intresult=1;
result=prime*result
+((productId==null)?0:productId.hashCode());
returnresult;
}
@Override
publicStringtoString(){
return"Product[productId="+productId+",name="+name+"]";
}
}
Addsettersandgettersforallofthefieldsintheprecedingclass.Ihaveomitteditto
makethecodecompact,butitisamust,sopleasedoaddit.
2. Now,createonemoreclasscalledProductControllerunderthe
com.packt.webstore.controllerpackageinthesourcefoldersrc/main/javaand
addthefollowingcodeintoit:
packagecom.packt.webstore.controller;
importjava.math.BigDecimal;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.Model;
importorg.springframework.web.bind.annotation.RequestMapping;
importcom.packt.webstore.domain.Product;
@Controller
publicclassProductController{
@RequestMapping("/products")
publicStringlist(Modelmodel){
Productiphone=newProduct("P1234","iPhone5s",new
BigDecimal(500));
iphone.setDescription("AppleiPhone5ssmartphonewith4.00-inch
640x1136displayand8-megapixelrearcamera");
iphone.setCategory("SmartPhone");
iphone.setManufacturer("Apple");
iphone.setUnitsInStock(1000);
model.addAttribute("product",iphone);
return"products";
}
}
3. Finally,addonemoreJSPviewfilecalledproducts.jspunderthedirectory
src/main/webapp/WEB-INF/views/,addthefollowingcodesnippetsintoit,andsave
it:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-
1">
<linkrel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>Products</h1>
<p>Alltheavailableproductsinourstore</p>
</div>
</div>
</section>
<sectionclass="container">
<divclass="row">
<divclass="col-sm-6col-md-3"style="padding-bottom:15px">
<divclass="thumbnail">
<divclass="caption">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>${product.unitPrice}USD</p>
<p>Available${product.unitsInStock}unitsinstock</p>
</div>
</div>
</div>
</div>
</section>
</body>
</html>
4. Finally,runtheapplicationandentertheURL
http://localhost:8080/webstore/products.Youwillbeabletoseeawebpage
displayingtheproductinformationasshowninthefollowingscreenshot:
TheProductspagedisplayingtheproductinformation
Whatjusthappened?
Ouraimistoshowthedetailsofaproductonourwebpage;thus,inordertodothis,we
firstneedadomainobjecttoholdthedetailsoftheproduct.That’swhatwedidinstep1;
wejustcreatedaclasscalledProduct(Product.java)tostoreinformationaboutthe
product,suchasthename,description,price,andsoon.
AswehavealreadylearnedfromtheAnoverviewoftheSpringMVCrequestflowsection,
toshowanydynamicdataonawebpage,priortothis,weneedtoputthisdatainamodel;
onlythenwilltheviewbeabletoreadthisdatafromthemodelandrenderitontheweb
page.So,toputtheproductinformationinamodel,wejustcreatedonemorecontroller
calledProductController(ProductController.java)instep3.
IntheProductControllerclass,wejusthaveasinglemethodcalledlistwhose
responsibilityistocreateaproductdomainobjecttoholdtheinformationabouttheApple
iPhone5sandaddthatobjecttothemodel.Andfinally,wereturntheviewnameas
products.That’swhatweweredoingthroughthefollowinglinesinthelistmethodof
ProductController:
model.addAttribute("product",iphone);
return"products";
SinceweconfiguredInternalResourceViewResolverasourviewresolverintheweb
applicationcontextconfigurationfile,intheprocessofresolvingtheviewfileforthe
givenviewname(inourcase,theviewnameisproducts),theviewresolverwilltryto
lookforafileproducts.jspunder/WEB-INF/views/.That’swhy,wecreated
products.jspinstep4.Ifyouskipstep4,youwillgettheHTTPStatus404errorwhen
runningtheproject.
Forabettervisualexperience,products.jspcontainslotsofdivtagswithBootstrapCSS
stylesapplied(BootstrapisanopensourceCSSframework),sodon’tthinkthat
products.jspisverycomplex;asamatteroffact,itisverysimple.Youneednotbother
aboutthedivtags.Thesearepresentjustfortheappeal.Youonlyneedtoobservethe
followingfourtagscarefullyinproducts.jsptounderstanddataretrievalfromthemodel:
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>${product.unitPrice}USD</p>
<p>Available${product.unitsInStock}unitsinstock</p>
Notethe${product.unitPrice}expressioncarefully;thetextproductintheexpression
isthenameofthekeythatweusedtostoretheiphoneobjectinthemodel.(Remember
thislinemodel.addAttribute("product",iphone);fromtheProductController
class.)ThetextunitPriceisnothingbutoneofthefieldsfromtheProductdomainclass
(Product.java).Similarly,weshowsomeimportantfieldsoftheproductdomainclassin
theproducts.jspfile.
Tip
WhenIsaythatpriceisthefieldname,Iamactuallymakinganassumptionherethatyou
havefollowedthestandardJavabeannamingconventionsforthegettersandsettersof
yourdomainclass.
Thisisbecause,whenSpringevaluatestheexpression${product.unitPrice},itis
actuallytryingtocallthegettermethodofthefieldtogetthevalue,soitwillexpecta
getUnitPrice()methodintheProduct.javafile.
Aftercompletingstep4,ifwerunourapplicationandentertheURL
http://localhost:8080/WebStore/products,wewillbeabletoseeawebpage
displayingtheproductinformationasshowninthepreviousscreenshot.
So,wehavecreatedadomainclasstoholdinformationaboutaproduct,createdasingle
productobjectinthecontroller,andaddedittothemodel.Finally,weshowedtheproduct
informationintheview.
Thepersistencelayer
Sincewehadasingleproduct,wejustinstantiateditinthecontrolleritselfanddisplayed
thisproductinformationonourwebpagesuccessfully.However,atypicalwebstore
containsthousandsofproducts;alltheinformationfortheseproductsisusuallystoredina
database.So,weneedtomakeourProductControllerclasssmartenoughtoloadallthe
productinformationfromthedatabaseintothemodel.However,ifwewriteallthedata
retrievallogicintheProductControllerclassitselftoretrieveproductinformationfrom
thedatabase,ourProductControllerclasswillblowdownintoabigchunkoffile.
Logicallyspeaking,dataretrievalisnotthedutyofthecontrollerbecausethecontrolleris
apresentationlayercomponent.Moreover,weneedtoorganizedataretrievalcodeina
separatelayersothatwecanreusethislogicasmuchaspossiblefromothercontrollers
andlayers.
HowdoweretrievedatafromthedatabasetheSpringMVCway?Therecomesthe
conceptofthepersistencelayer.Apersistencelayerusuallycontainsrepositoryobjectsto
accessdomainobjects.Arepositoryobjectmakesqueriestothedatasourceforthedata,
thereaftermapsthedatafromthedatasourcetoadomainobject,andfinally,persiststhe
changesinthedomainobjecttothedatasource.So,arepositoryobjectistypically
responsibleforCRUDoperations(Create,Read,Update,andDelete)ondomainobjects.
The@Repositoryannotation(org.springframework.stereotype.Repository)isan
annotationthatmarksaspecificclassasarepository.The@Repositoryannotationalso
indicatesthattheSQLexceptionsthrownfromtherepositoryobject’smethodsshouldbe
translatedintoSpring’sDataAccessExceptions.Let’screatearepositorylayerforour
application.
Timeforaction–creatingarepository
object
Performthefollowingstepstocreatearepositoryclasstoaccessyourproductdomain
objects:
1. CreateaninterfacecalledProductRepositoryunderthepackage
com.packt.webstore.domain.repositoryinthesourcefoldersrc/main/java.Add
asinglemethoddeclarationinit,asfollows:
List<Product>getAllProducts();
2. CreateaclasscalledInMemoryProductRepositoryunderthepackage
com.packt.webstore.domain.repository.implinthesourcefolder
src/main/java.Now,addthefollowingcodeintoit:
packagecom.packt.webstore.domain.repository.impl;
importjava.math.BigDecimal;
importjava.util.ArrayList;
importjava.util.List;
importorg.springframework.stereotype.Repository;
importcom.packt.webstore.domain.Product;
importcom.packt.webstore.domain.repository.ProductRepository;
@Repository
publicclassInMemoryProductRepositoryimplementsProductRepository{
privateList<Product>listOfProducts=newArrayList<Product>();
publicInMemoryProductRepository(){
Productiphone=newProduct("P1234","iPhone5s",new
BigDecimal(500));
iphone.setDescription("AppleiPhone5ssmartphonewith4.00-inch
640x1136displayand8-megapixelrearcamera");
iphone.setCategory("SmartPhone");
iphone.setManufacturer("Apple");
iphone.setUnitsInStock(1000);
Productlaptop_dell=newProduct("P1235","DellInspiron",new
BigDecimal(700));
laptop_dell.setDescription("DellInspiron14-inchLaptop(Black)
with3rdGenerationIntelCoreprocessors");
laptop_dell.setCategory("Laptop");
laptop_dell.setManufacturer("Dell");
laptop_dell.setUnitsInStock(1000);
Producttablet_Nexus=newProduct("P1236","Nexus7",new
BigDecimal(300));
tablet_Nexus.setDescription("GoogleNexus7isthelightest7inch
tabletWithaquad-coreQualcommSnapdragon™S4Proprocessor");
tablet_Nexus.setCategory("Tablet");
tablet_Nexus.setManufacturer("Google");
tablet_Nexus.setUnitsInStock(1000);
listOfProducts.add(iphone);
listOfProducts.add(laptop_dell);
listOfProducts.add(tablet_Nexus);
}
publicList<Product>getAllProducts(){
returnlistOfProducts;
}
}
3. OpenProductControllerfromthepackagecom.packt.webstore.controllerin
thesourcefoldersrc/main/java,andaddaprivatereferencetoProductRepository
withthe@Autowiredannotation
(org.springframework.beans.factory.annotation.Autowired),asfollows:
@Autowired
privateProductRepositoryproductRepository;
4. Now,alterthebodyofthelistmethod,asfollows,inProductController:
@RequestMapping("/products")
publicStringlist(Modelmodel){
model.addAttribute("products",productRepository.getAllProducts());
return"products";
}
5. Then,opentheviewfileproducts.jspfromsrc/main/webapp/WEB-INF/views/,
andremovealloftheexistingcodeandreplaceitwiththefollowingcodesnippet:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-
8859-1">
<linkrel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>Products</h1>
<p>Alltheavailableproductsinourstore</p>
</div>
</div>
</section>
<sectionclass="container">
<divclass="row">
<c:forEachitems="${products}"var="product">
<divclass="col-sm-6col-md-3"style="padding-bottom:15px">
<divclass="thumbnail">
<divclass="caption">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>$${product.unitPrice}</p>
<p>Available${product.unitsInStock}unitsinstock</p>
</div>
</div>
</div>
</c:forEach>
</div>
</section>
</body>
</html>
6. Finally,runtheapplicationandentertheURL
http://localhost:8080/webstore/products.Youwillseeawebpagedisplaying
theproductinformationasshowninthefollowingscreenshot:
TheProductspagedisplayingalloftheproductinformationfromthein-memory
repository
Whatjusthappened?
Sincewedon’twanttowriteallofthedataretrievallogicinsideProductController
itself,wedelegatedthistasktoanotherclasscalledInMemoryProductRepository.The
InMemoryProductRepositoryclasshasasinglemethodcalledgetAllProducts(),which
returnsalistofproductdomainobjects.
Asthenameimplies,InMemoryProductRepositoryisjustadummy,in-memoryproduct
repository.Itdoesnotretrieveanyrealproductdomainobjectinformationfromany
databaseassuch;rather,itjustinstantiatesalistofproductdomainobjectsinits
constructor.So,instep2,wejustcreatedtheInMemoryProductRepositoryclass,addeda
singlemethodgetAllProducts()toit,andinstantiatedsomeproductsintheconstructor.
Youmaywonder,then,whatwedidinstep1.Instep1,wejustcreatedaninterfacecalled
ProductRepository,whichdefinestheexpectedbehaviorofaproductrepository.Asof
now,theonlyexpectedbehaviorofaProductRepositoryinterfaceistoreturnalistof
productdomainobjects(getAllProducts),andourInMemoryProductRepositoryclassis
justanimplementationofthisinterface.
Writingrealdataretrievalcodeisbeyondthescopeofthisbook,soIhavecreated
InMemoryProductRepositoryjustfordemonstrationpurposes.However,itispossibleto
replacetheInMemoryProductRepositoryclasswithanyotherrealimplementationthat
canretrieverealdatafromthedatabase.
Whydowehaveaninterfaceandanimplementationfortheproductrepository?
Rememberthatweareactuallycreatingapersistencelayerforourapplication.Whois
goingtouseourpersistencelayerrepositoryobject?Itwillpossiblybeusedbya
controllerobject(inourcase,ProductController)fromthecontrollerlayer,soitisnot
thebestpracticetoconnecttwolayers(controllerandpersistence)withadirectreference.
Instead,wecan,infuture,haveaninterfacereferenceinthecontrollersothatwecan
easilyswitchtodifferentimplementationsoftherepositorywithoutdoinganycode
changesinthecontrollerclass,ifwewant.
That’sthereasonwhywehadtheProductRepositoryreferenceinour
ProductControllerclassinstep3,andnottheInMemoryProductRepositoryclass
reference.NotethefollowinglinesinProductController:
@Autowired
privateProductRepositoryproductRepository;
Whatistheneedofthe@Autowiredannotationhere?Ifyouobservethe
ProductControllerclasscarefully,youmaywonderwhywedidn’tinstantiateanyobject
forthereference,productRepository.Nowherecouldweseeasinglelinesaying
somethinglikeproductRepository=newInMemoryProductRepository();.
SohowcometheexecutionofthelineproductRepository.getAllProducts()worksjust
finewithoutanyNullPointerExceptionerrorinthelistmethodofthe
ProductControllerclass?
model.addAttribute("products",productRepository.getAllProducts());
WhoassignstheInMemoryProductRepositoryobjecttotheproductRepository
reference?TheansweristhattheSpringFrameworkassignsthe
InMemoryProductRepositoryobjecttotheproductRepositoryreference.
RememberwelearnedthatSpringcreatesandmanagesbeans(objects)forevery
@controllerclass?Similarly,Springcreatesandmanagesbeansfor@Repositoryclasses
aswell.AssoonasSpringseesthe@Autowiredannotationontopofthe
ProductRepositoryreference,itassignstheobjectofInMemoryProductRepositoryto
thisreferencesinceSpringalreadycreatedandholdstheInMemoryProductRepository
objectinitsobjectcontainer(thewebapplicationcontext).
Ifyouremember,weconfiguredacomponentscanthroughthefollowingtagintheweb
applicationcontextconfigurationfile:
<context:component-scanbase-package="com.packt.webstore"/>
Also,welearnedearlierthatifweconfigureourwebapplicationcontextasmentioned,it
notonlydetectscontrollers(@controller),butitalsodetectsotherstereotypessuchas
repositories(@Repository)andservices(@Service).
Sinceweaddedthe@RepositoryannotationontopoftheInMemoryProductRepository
class,SpringknowsthatifanyreferenceofthetypeproductRepositoryhasan
@Autowiredannotationontopofit,thenitshouldassigntheimplementationobject
InMemoryProductRepositorytothatreference.Thisprocessofmanagingthedependency
betweenclassesiscalleddependencyinjectionorwiringintheSpringworld.So,tomark
anyclassasarepositoryobject,weneedtoannotatethatclasswiththe@Repository
annotation(org.springframework.stereotype.Repository).
Weunderstandhowthepersistencelayerworks,butaftertherepositoryobjectreturnsa
listofproducts,howdoweshowitonthewebpage?Ifyourememberhowweaddedour
firstproducttothemodel,itisverysimilartothat.Insteadofasingleobject,thistimewe
addalistofobjectstothemodelthroughthefollowinglineinthelistmethodof
ProductController:
model.addAttribute("products",productRepository.getAllProducts());
Intheprecedingcode,productRepository.getAllProducts()justreturnsalistof
productdomainobjects(List<Product>),andwedirectlyaddthislisttothemodel.
Inthecorrespondingviewfile(products.jsp),usingthe<C:forEach>tag,weloop
throughthelistanddisplaytheinformationforeachproductinsideastyleddivtag:
<c:forEachitems="${products}"var="product">
<divclass="col-sm-6col-md-3"style="padding-bottom:15px">
<divclass="thumbnail">
<divclass="caption">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>${product.unitPrice}USD</p>
<p>Available${product.unitsInStock}unitsinstock</p>
</div>
</div>
</div>
</c:forEach>
Again,notethatthetextproductsintheexpression${products}isnothingbutthekey
thatweusedwhenaddingtheproductlisttothemodelfromtheProductController
class.
TheforeachloopisaspecialJSTLloopingtagthatwillrunthroughthelistofproducts
andassigneachproducttoavariablecalledproduct(var="product")oneachiteration.
Fromtheproductvariable,wefetchinformationsuchasthename,description,and
unitPriceoftheproductanddisplayitwithinthe<h3>and<p>tags.That’showweare
finallyabletoseethelistofproductsontheproductswebpage.
Theservicelayer
Sofarsogood;wecreatedapresentationlayerthatcontainsacontroller,dispatcher
servlet,viewresolvers,andsoon.Then,wecreatedadomainlayerthatcontainsasingle
domainclass,Product.Finally,wecreatedthepersistencelayer,whichcontainsa
repositoryinterfaceandanimplementationtoaccessourProductdomainobjects.
However,wearestillmissingonemoreconceptcalledtheservicelayer.Whydoweneed
theservicelayer?Wesawhowapersistencelayerdealswithallofthelogicrelatedtodata
access(CRUD)andthepresentationlayerdealswithalloftheactivitiesrelatedtotheweb
requestandview;thedomainlayercontainsclassestoholdinformationthatisretrieved
fromdatabaserecords/thepersistencelayer.However,wherecanweputthecodefor
businessoperations?
TheservicelayerexposesbusinessoperationsthatcouldbecomposedofmultipleCRUD
operations.TheseCRUDoperationsareusuallyperformedbytherepositoryobjects.For
example,youcouldhaveabusinessoperationtoprocessacustomerorder,andinorderto
performsuchabusinessoperation,youwouldneedtoperformthefollowingoperations:
1. First,ensurethatalloftheproductsintherequestedorderareavailableinyourstore.
2. Second,haveasufficientquantityoftheseproductsinyourstore.
3. Finally,updatetheproductinventorybyreducingtheavailablecountforeach
productthatwasordered.
Serviceobjectsaregoodcandidatesforsuchbusinessoperationslogic.Theservice
operationscouldalsorepresenttheboundariesofSQLtransactions;thismeansthatallof
theelementaryCRUDoperationsperformedinsidethebusinessoperationshouldbeinside
atransaction:eitherallofthemshouldsucceedortheyshouldrollbackincaseoferror.
Timeforaction–creatingaserviceobject
Performthefollowingstepstocreateaserviceobjectthatwillperformthesimplebusiness
operationoforderprocessing:
1. OpentheinterfaceProductRepositoryfromthepackage
com.packt.webstore.domain.repositoryinthesourcefoldersrc/main/java,and
addonemoremethoddeclarationonit,asfollows:
ProductgetProductById(StringproductID);
2. OpentheimplementationclassInMemoryProductRepositoryandaddan
implantationforthepreviouslydeclaredmethod,asfollows:
publicProductgetProductById(StringproductId){
ProductproductById=null;
for(Productproduct:listOfProducts){
if(product!=null&&product.getProductId()!=null&&
product.getProductId().equals(productId)){
productById=product;
break;
}
}
if(productById==null){
thrownewIllegalArgumentException("Noproductsfoundwiththe
productid:"+productId);
}
returnproductById;
}
3. CreateaninterfacecalledOrderServiceunderthepackage
com.packt.webstore.serviceinthesourcefoldersrc/main/java.Now,adda
methoddeclarationinitasfollows:
voidprocessOrder(StringproductId,intcount);
4. CreateaclasscalledOrderServiceImplunderthepackage
com.packt.webstore.service.implinthesourcefoldersrc/main/java.Then,add
thefollowingcodeintoit:
packagecom.packt.webstore.service.impl;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Service;
importcom.packt.webstore.domain.Product;
importcom.packt.webstore.domain.repository.ProductRepository;
importcom.packt.webstore.service.OrderService;
@Service
publicclassOrderServiceImplimplementsOrderService{
@Autowired
privateProductRepositoryproductRepository;
publicvoidprocessOrder(StringproductId,longquantity){
ProductproductById=productRepository.getProductById(productId);
if(productById.getUnitsInStock()<quantity){
thrownewIllegalArgumentException("OutofStock.AvailableUnits
instock"+productById.getUnitsInStock());
}
productById.setUnitsInStock(productById.getUnitsInStock()-
quantity);
}
}
5. Now,createonemorecontrollerclasscalledOrderControllerunderthepackage
com.packt.webstore.controllerinthesourcefoldersrc/main/java,andaddthe
followingcodeintoit:
packagecom.packt.webstore.controller;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.Model;
importorg.springframework.web.bind.annotation.RequestMapping;
importcom.packt.webstore.service.OrderService;
@Controller
publicclassOrderController{
@Autowired
privateOrderServiceorderService;
@RequestMapping("/order/P1234/2")
publicStringprocess(){
orderService.processOrder("P1234",2);
return"redirect:/products";
}
}
6. Finally,runtheapplicationandentertheURL
http://localhost:8080/webstore/order/P1234/2.Youwillbeabletoseeaweb
pagedisplayingtheproductinformationasshowninthefollowingscreenshot(note
thattheavailableunitsinstockforiPhone5sshowasAvailable998unitsinstock):
TheProductspagedisplayingtheproductinformationafterthestockwasupdated
viaaservicecall
Whatjusthappened?
Beforegoingthroughthesteps,Ijustwanttoremindyouofafactregardingrepository
objects:allofthedataaccess(CRUD)operationsonadomainobjectshouldbecarried
throughrepositoryobjectsonly.Factnumbertwoisthatserviceobjectsrelyonrepository
objectstocarryoutalloperationsrelatedtodataaccess.That’swhy,beforecreatingthe
actualserviceinterface/implementation,wecreatedarepositoryinterface/
implementationmethod(getProductById)insteps1and2.
ThegetProductByIdmethodfromtheInMemoryProductRepositoryclassjustreturnsa
productdomainobjectforthegivenproductID.Weneedthismethodwhenwewritethe
logicforourserviceobjectmethod(processOrder)intheOrderServiceImplclass.Ifthe
productisnotfoundforthegivenID,thenInMemoryProductRepositorythrows
IllegalArgumentException.
Now,let’sreviewsteps3and4,wherewecreatedtheactualservicedefinitionand
implementation.Instep3,wecreatedaninterfacecalledOrderServicetodefineallofthe
expectedresponsibilityofanorderservice.Wedefinedonlyoneresponsibility,asofnow,
withinthatinterface;thatis,toprocesstheorderviathemethod,processOrder.The
processOrdermethodhastwoparameters:oneisproductIdandtheotherisquantity.In
step4,weimplementedtheprocessOrdermethodwithintheOrderServiceImplclass,
wherewereducedtheamountofstockavailableforthegivenproductIdbythequantity
parameter.
Inthepreviousexercise,withintheProductControllerclass,weconnectedcontroller
andrepositorythroughtheProductRepositoryinterfacereferencetomaximizeloose
coupling.Similarly,wehavenowconnectedtheservicelayerandrepositorylayerthrough
theProductRepositoryinterfacereference,asfollows,intheOrderServiceImplclass:
@Autowired
privateProductRepositoryproductRepository;
Aswehavealreadylearned,SpringassignedtheInMemoryProductRepositoryobjectto
theproductRepositoryreferenceinthepreviouslymentionedcodebecausethe
productRepositoryreferencehasthe@Autowiredannotation,andweknowthatSpring
createsandmanagesallofthe@Serviceand@Repositoryobjects.Notethat
OrderServiceImplhasthe@Serviceannotation
(org.springframework.stereotype.Service)ontopofit.Weusedthe
productRepositoryreferencetogettheproductforthegivenIDwithintheprocessOrder
methodoftheOrderServiceImplclassasfollows:
publicvoidprocessOrder(StringproductId,longquantity){
ProductproductById=productRepository.getProductById(productId);
if(productById.getUnitsInStock()<quantity){
thrownewIllegalArgumentException("OutofStock.AvailableUnitsin
stock"+productById.getUnitsInStock());
}
productById.setUnitsInStock(productById.getUnitsInStock()-quantity);
}
Tip
Toensuretransactionalbehavior,Springprovidesthe@Transactionalannotation
(org.springframework.transaction.annotation.Transactional).Weneedtoannotate
servicemethodswiththe@Transactionalannotationtodefinetransactionattributes,and
weneedtodosomemoreconfigurationinourapplicationcontextfortransactional
behaviortotakeeffect.
However,sinceweareusingdummy,in-memoryrepositoriestomimicdataaccess,to
annotateservicemethodswiththe@Transactionalannotationismeaningless.Toknow
moreabouttransactionmanagementinSpring,referto
http://docs.spring.io/spring/docs/current/spring-framework-
reference/html/transaction.html.
Wealreadycreatedtheservicelayer,andnowitisreadytobeconsumedfromthe
presentationlayer.Itistimeforustoconnectourservicelayerwiththecontroller.Instep
5,wecreatedonemorecontroller,OrderController,witharequestmappingmethod
calledprocessinitthatisshowninthefollowingcodesnippet:
@RequestMapping("/order/P1234/2")
publicStringprocess(){
orderService.processOrder("P1234",2);
return"redirect:/products";
}
TheprocessmethodfromtheOrderControllerclassusesourorderServicereferenceto
processtheorderfortheproductID,P1234.Aftersuccessfullyexecutingtheprocess
methodofOrderController,theavailableunitsinstockshouldgetreducedby2forthe
productwiththeID,P1234.
Youwillalsonoticethatwemappedthe/order/P1234/2URLpathtotheprocess
methodusingthe@RequestMappingannotation.So,whenwefinallytrytohittheURL
http://localhost:8080/webshop/order/P1234/2,wewillbeabletoseethatthe
availableunitsinstockgetreducedbytwofortheproduct,P1234.
Haveagohero–accessingtheproductdomain
objectviaaservice
InourProductControllerclass,weonlyhavetheProductRepositoryreferenceto
accesstheProductdomainobject.However,toaccessProductRepositorydirectlyfrom
ProductControllerisnotthebestpractice;itisalwaysgoodtoaccessthepersistence
layerrepositoryviatheserviceobject.However,wehavenotcreatedanyserviceobjectto
mediatebetweenProductControllerandProductRepository.
Whydon’tyoucreateaservicelayertomediatebetweenProductControllerand
ProductRepository?Thefollowingaresomeofthethingsyoucantryout:
1. CreateaninterfacecalledProductServicewithamethoddeclaration,List
<Products>getAllProducts();.
2. Createanimplementationclass,ProductServiceImpl,fortheProductService
interface.
3. AutowiretheProductRepositoryreferenceintheProductServiceImplclassand
usethisreferencewithinthegetAllProductsmethodtogetalloftheproductsfrom
ProductRepository.
4. ReplacetheProductRepositoryreferencewiththeProductServicereferenceinthe
ProductControllerclass.Accordingly,changethelistmethodinthe
ProductControllerclass.
5. Afterfinishingthis,youwillbeabletoseethesameproductlistingsundertheURL,
http://localhost:8080/webshop/products/.
Anoverviewofthewebapplication
architecture
Sofar,wehaveseenhowtoorganizeourcodeintolayerssothatwecanavoidtight
couplingbetweenvariouscodefiles,andimprovereusabilityandtheseparationof
concerns.Wejustcreatedonedomainclass,onerepositoryclass,andoneserviceclassfor
demonstrationpurposes,butatypical,real-worldMVCapplicationmaycontainasmany
domain,repository,andserviceclassesasrequired.Eachlayerisusuallyconnected
throughinterfacesandalwayscontrolleraccessdomainobjectsfromtherepositoryviathe
serviceinterfaceonly.
Everytypical,enterprise-levelSpringMVCapplicationwilllogicallyhavefourlayers:
presentation,domains,persistence,andservices.Thedomainlayerissometimescalledthe
modellayer.Thefollowingblockdiagramwillhelpyouconceptualizethisidea:
ThelayersofaSpringMVCapplication
So,welearnedhowtocreateaservicelayerobjectandrepositorylayerobject;whatwe
sawintheservicelayerandrepositorylayerwasjustaglimpse.Springhasextensive
supporttodealwithdatabasesandtransactions;handlingtheseisaveryvasttopicand
deservesitsownbook.Intheupcomingchapters,wewillconcentratemoreonthe
presentationlayer,whichcontainsmostoftheconceptsrelatedtoSpringMVCratherthan
thoserelatedtothedatabaseandtransaction.
Haveagohero–listingallourcustomers
ItisgreatthatyouhavelistedalloftheproductsinyourwebapplicationundertheURL,
http://localhost:8080/webstore/products,butinordertobecomeasuccessfulweb
store,maintainingonlytheproductinformationisnotenough.Youneedtomaintain
informationaboutthecustomeraswellsothatyoucanattractthembygivingspecial
discountsbasedontheirpurchasehistory.
Whydon’tyoumaintaincustomerinformationinyourapplication?Executethefollowing
stepstomakesomeimprovementstoyourapplicationtomaintaincustomerinformation:
1. AddonemoredomainclasscalledtheCustomerdomainclassinthesamepackage
wheretheproductexists.
2. AddfieldssuchascustomerId,name,address,andnoOfOrdersMadetotheCustomer
class.
3. Createapersistencelayertoreturnallcustomers.
4. CreateaninterfacecalledCustomerRepositorywithamethoddeclaration,List
<Customers>getAllCustomers();.
5. CreateanimplementationInMemoryCustomerRepositoryforCustomerRepository
andinstantiateadummycustomerintheconstructorof
InMemoryCustomerRepository,asyoudidforInMemoryProductRepository.
6. Createaservicelayertogetallofthecustomersfromtherepository.
7. CreateaninterfacecalledCustomerServicewithamethoddeclaration,List
<Customers>getAllCustomers().
8. CreateanimplementationCustomerServiceImplforCustomerService.
9. CreateonemorecontrollercalledCustomerController.
10. AddarequestmappingmethodtomaptheURL,
http://localhost:8080/webstore/customers.
11. Createaviewfilecalledcustomers.jsp.
Afterfinishingthisexercise,youwillbeabletoseeallofyourcustomersundertheURL,
http://localhost:8080/webstore/customers.Thisisverysimilartothewaywelisted
allofourproductsundertheURL,http://localhost:8080/webstore/products.
Summary
Atthestartofthischapter,welearnedthedutyofadispatcherservletandhowitmapsa
requestusingthe@RequestMappingannotation.Next,wesawwhatawebapplication
contextisandhowtoconfigureitforourwebapplication.Afterthat,wegotalittle
introductionaboutviewresolversandhowInternalResourceViewResolverresolvesthe
viewfileforthegivenlogicalviewname.WealsolearnedtheconceptofMVCandthe
overallrequestflowofaSpringMVCapplication,andthenwelearnedaboutweb
applicationarchitecture.Inthewebapplicationarchitecturesection,wesawhowtocreate
andorganizecodeunderthevariouslayersofaSpringMVCapplication,suchasthe
domainlayer,persistencelayer,andservicelayer.Atthesametime,wesawhowto
retrieveproductdomainobjectsfromtherepositoryandpresentthemonthewebpage
usingthecontroller.Wealsolearnedwhereaserviceobjectfitsin.Finally,wesawan
overviewofthewebapplicationarchitecture.
IhopeyougotagoodoverviewofSpringMVCandthevariouscomponentsinvolvedin
developingaSpringMVCapplication.Inthenextchapter,wearespecificallygoingto
learnmoreaboutcontrollersandrelatedconcepts.Meetyouinthenextchapter!
Chapter3.ControlYourStorewith
Controllers
InChapter2,SpringMVCArchitecture–ArchitectingYourWebStore,welearnedthe
overallarchitectureofaSpringMVCapplication.Wedidn’tgointoanyoftheconceptsin
detail;ourtotalaimwastounderstandtheoverallflow.Inthischapter,wearegoingto
haveanin-depthlookatthecontrollersinSpringMVCastheyhaveanimportantrole.
Thischapterwillcoverthefollowingconcepts:
Definingacontroller
URItemplatepatterns
Matrixvariables
Requestparameters
Definingacontroller
Controllersarepresentationlayercomponentsthatareresponsibleforrespondingtouser
actions.TheseactionscouldbeenteringaparticularURLonthebrowser,clickingona
link,submittingaformonawebpage,andsoon.AnyregularJavaclasscanbe
transformedintoacontrollerbysimplybeingannotatedwiththe@Controllerannotation
(org.springframework.stereotype.Controller).
Andwehadalreadylearnedthatthe@ControllerannotationsupportsSpring’s
autodetectionmechanismforauto-registeringthebeandefinitioninthewebapplication
context.Toenablesuchauto-registering,wemustaddthe<context:component-scan>tag
inthewebapplicationcontextconfigurationfile;wehaveseenhowtodothatintheThe
webapplicationcontextconfigurationsectionofChapter2,SpringMVCArchitecture–
ArchitectingYourWebStore.
Acontrollerclassismadeupofrequest-mappedmethods,alsocalledhandlermethods.
Handlermethodsareannotatedwiththe@RequestMappingannotation
(org.springframework.web.bind.annotation.RequestMapping).The@RequestMapping
annotationisusedtomapURLstoparticularhandlermethods.InChapter2,SpringMVC
Architecture–ArchitectingYourWebStore,wesawabriefintroductiononthe
@RequestMappingannotationandlearnedhowtoapplythe@RequestMappingannotation
onthehandlermethodlevel.However,inSpringMVC,wecanevenspecifythe
@RequestMappingannotationatthecontrollerclasslevel.Inthatcase,SpringMVCwill
considerthecontrollerclasslevel@RequestMappingannotationvaluebeforemappingthe
URLtothehandlermethods.Thisfeatureiscalledrelativerequestmapping.
Note
Thetermsrequestmappedmethod,mappedmethod,handlermethod,andcontroller
methodallmeanthesamething;thesetermsareusedtospecifythecontrollermethod
withan@RequestMappingannotation.Theyhavebeenusedinterchangeablyinthisbook.
Timeforaction–addingclass-level
requestmapping
Let’sadda@RequestMappingannotationonourProductControllerclasstodemonstrate
therelativerequestmappingfeature.However,beforethat,wejustwanttoensurethatyou
havealreadyreplacedtheProductRepositoryreferencewiththeProductService
referenceintheProductControllerclassaspartofthepreviouschapter’sTimeforaction
–creatingaserviceobjectsection.Becausecontactingthepersistencelayerdirectlyfrom
thepresentationlayerisnotabestpractice,allaccesstothepersistencelayershouldgo
throughtheservicelayer.Performthefollowingsteps(thosewhohavecompletedthis
exercisecandirectlystartfromstep5;otherspleasecontinuefromstep1):
1. CreateaninterfacecalledProductServiceunderthecom.packt.webstore.service
packageinsrc/main/javaandaddtwomethoddeclarationsinitasfollows:
List<Product>getAllProducts();
ProductgetProductById(StringproductID);
2. CreateaclasscalledProductServiceImplunderthe
com.packt.webstore.service.implpackageinsrc/main/javaandaddthe
followingcodeintoit:
packagecom.packt.webstore.service.impl;
importjava.util.List;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Service;
importcom.packt.webstore.domain.Product;
importcom.packt.webstore.domain.repository.ProductRepository;
importcom.packt.webstore.service.ProductService;
@Service
publicclassProductServiceImplimplementsProductService{
@Autowired
privateProductRepositoryproductRepository;
publicList<Product>getAllProducts(){
returnproductRepository.getAllProducts();
}
publicProductgetProductById(StringproductID){
returnproductRepository.getProductById(productID);
}
}
3. OpenProductController,removetheexistingProductRepositoryreference,and
addtheProductServicereferenceasfollows:
@Autowired
privateProductServiceproductService;
4. Now,alterthebodyofthelistmethodintheProductControllerclassasfollows
(notethatthistimeweusedtheproductServicereferencetogetalloftheproducts):
@RequestMapping("/products")
publicStringlist(Modelmodel){
model.addAttribute("products",productService.getAllProducts());
return"products";
}
5. IntheProductControllerclass,addthefollowingannotationontopoftheclass:
@RequestMapping("/products")
6. Fromthelistmethod’s@RequestMappingannotation,removethevalueattribute
completely;sonow,thelistmethodwillhaveaplain@RequestMappingannotation
withoutanyattributesasfollows:
@RequestMapping
publicStringlist(Modelmodel){
7. Now,addonemorehandlermethodintheProductControllerclassasfollows:
@RequestMapping("/all")
publicStringallProducts(Modelmodel){
model.addAttribute("products",productService.getAllProducts());
return"products";
}
8. Finally,runtheapplicationagainandentertheURL
http://localhost:8080/webstore/products/allinthebrowsertoviewallofthe
products.
Whatjusthappened?
Whatwehavedemonstratedhereisasimpleconceptcalledrelativerequestmapping.We
didthefollowingthreethingsintheProductControllerclass:
Weaddedan@RequestMappingannotationattheclasslevelwithavalueattribute
definedas"/products"instep5
Weremovedthevalueattributefromthe@RequestMappingannotationofthelist
methodinstep6
Finally,weaddedonemorehandlermethodcalledallProducts,whichalsoputsthe
samelistofproductsonthemodelasthelistmethod,butunderadifferentURL
mapping—@RequestMapping("/all")
Inallourpreviousexamples,weannotatedthe@RequestMappingannotationsonlyatthe
controllermethodlevel,butSpringMVCalsoallowsustospecifyrequestmappingatthe
controllerclasslevel.Inthiscase,SpringMVCmapsaspecificURLpathatthemethod
levelthatisrelativetotheclasslevel@RequestMappingURLvalue.
Instep5,wejustaddedthe@RequestMappingannotationattheProductControllerclass
levelwiththeURLmappingvalue/products.Andinstep7,weaddedanewhandler
methodcalledallProductswithaURLmappingvalue/all.So,thefinalrequestpathfor
theallProductsmethodisformedbycombiningtheclassandmethodrequestmapping
values,whichis/products/all.So,ifwedefinedanyclasslevelrequestmapping,
SpringMVCwouldconsiderthatclasslevelrequestpathbeforemappingtherequestto
themethod.
Note
Steps1to4justteachyouhowtocreateandconnectaservicelayerobjectwiththe
ProductControllerclass.Asofnow,theProductServiceImplclassdoesnothaveany
distinguishablebusinesslogicinit;rather,itsimplydelegatesthecalltothepersistence
layer’srepositoryobject(ProductRepository)toaccesstheProductdomainobject.Soas
ofnow,thereisnorealmeaningtohaveaservicelayerforProductRepository;however,
infuture,ifwedecidetoreplacetheInMemoryProductRepositoryobjectwithareal
databasebackedrepositoryobject,wewilldefinitelyneedthisservicelayertowritecode
tohandletransaction-relatedtasks.So,justtomaintaintheindustry’sbestpractices,Ihave
retainedtheservicelayersinmostoftheexamplesinthisbook.
Instep6,wesimplydidn’tspecifyanyrequestpathvalueinthe@RequestMapping
annotationofthelistmethod.Bydoingso,wemadethelistmethodthedefaultrequest
mappingmethodfortheProductControllerclass.So,wheneverarequestURLendsup
withthecontrollerclasslevelrequestpathvaluewithoutanyfurtherrelativepath,Spring
MVCinvokesthismethodasaresponsetotherequest.
So,finallyinourcase,theURLhttp://localhost:8080/webstore/productswillbe
mappedtothelistmethodandhttp://localhost:8080/webstore/products/allwill
bemappedtotheallProductsmethod.
Note
Ifyouspecifymorethanonedefaultmappingmethodinsideacontroller,youwillget
IllegalStateExceptionwiththemessageAmbiguousmappingfound.So,acontroller
canhaveonlyonedefaultrequestmappingmethodatmost.
Popquiz–class-levelrequestmapping
Q1.Inawebapplicationcalledlibrarythathasthefollowingrequestmappingata
controllerclasslevelandinthemethodlevel,whichistheappropriaterequestURLto
maptherequesttothebooksmethod?
@RequestMapping("/books")
publicclassBookController{
...
@RequestMapping(value="/list")
publicStringbooks(Modelmodel){
...
1. http://localhost:8080/library/books/list
2. http://localhost:8080/library/list
3. http://localhost:8080/library/list/books
4. http://localhost:8080/library/
Q2.IfwehaveanotherhandlermethodcalledbookDetailsunderBookControlleras
follows,whatwilltheURLthatmapstothatmethodbe?
@RequestMapping()
publicStringbookDetails(Modelmodel){
...
1. http://localhost:8080/library/books/details
2. http://localhost:8080/library/books
3. http://localhost:8080/library/details
4. http://localhost:8080/library/
TheroleofacontrollerinSpringMVC
InSpringMVC,controllermethodsarethefinaldestinationpointthatawebrequestcan
reach.Afterbeinginvoked,thecontrollermethodstartstoprocessthewebrequestby
interactingwiththeservicelayertocompletetheworkthatneedstobedone.Usually,the
servicelayerexecutessomebusinessoperationsondomainobjectsandcallsthe
persistencelayertoupdatethedomainobjects.Aftertheprocessinghasbeencompleted
bytheservicelayerobject,thecontrollerisresponsibleforupdatingandbuildingupthe
modelobjectandchoosesaviewfortheusertoseenextasaresponse.
RememberthatSpringMVCalwayskeepsthecontrollersunawareofanyview
technologyused.That’swhythecontrollerreturnsonlyalogicalviewname;later,
DispatcherServletconsultswithViewResolvertofindouttheexactviewtobe
rendered.Accordingtothecontroller,ModelisacollectionofarbitraryobjectsandViewis
specifiedwithalogicalname.
Inallourpreviousexercises,thecontrollersusedtoreturnthelogicalviewnameand
updatethemodelviathemodelparameteravailableinthecontrollermethod.Thereis
another,seldomusedwayofupdatingthemodelandreturningtheviewnamefromthe
controllerwiththehelpoftheModelAndViewobject
(org.springframework.web.servlet.ModelAndView).Lookatthefollowingcode
snippet,forexample:
@RequestMapping("/all")
publicModelAndViewallProducts(){
ModelAndViewmodelAndView=newModelAndView();
modelAndView.addObject("products",productService.getAllProducts());
modelAndView.setViewName("products");
returnmodelAndView;
}
Theprecedingcodesnippetjustshowshowwecanencapsulatethemodelandviewusing
theModelAndViewobject.
Handlermapping
WehavelearnedthatDispatcherServletistheonethatdispatchestherequesttothe
handlermethodsbasedontherequestmapping;however,inordertointerpretthe
mappingsdefinedintherequestmapping,DispatcherServletneedsaHandlerMapping
implementation(org.springframework.web.servlet.HandlerMapping).The
DispatcherServletconsultswithoneormoreHandlerMappingimplementationstofind
outwhichcontroller(handler)canhandletherequest.So,HandlerMapping
determineswhichcontrollertocall.
TheHandlerMappinginterfaceprovidestheabstractionformappingrequeststohandlers.
TheHandlerMappingimplementationsarecapableofinspectingtherequestandcoming
upwithanappropriatecontroller.SpringMVCprovidesmanyHandlerMapping
implementations,andtheoneweareusingtodetectandinterpretmappingsfromthe
@RequestMappingannotationistheRequestMappingHandlerMappingclass
(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
TostartusingRequestMappingHandlerMapping,wehavetoaddthe<mvc:annotation-
driven>elementinourwebapplicationcontextconfigurationfilesothatSpringMVC
cancreateandregisterabeanforRequestMappingHandlerMappinginourwebapplication
context.Wealreadyconfigured<mvc:annotation-driven>inChapter2,SpringMVC
Architecture–ArchitectingYourWebStore,intheThewebapplicationcontext
configurationsection.
UsingURItemplatepatterns
Inthepreviouschapters,wesawhowtomapaparticularURLtoacontrollermethod;for
example,iftheURLenteredwashttp://localhost:8080/webstore/products,we
mappedthatrequesttothelistmethodofProductControllerandlistedalltheproduct
informationonthewebpage.
Whatifwewanttolistonlyasubsetoftheproductsbasedoncategory,forinstance,we
wanttodisplayonlytheproductsthatfallunderthecategoryoflaptopsiftheuserentered
theURLhttp://localhost:8080/webstore/products/laptop?Similarly,whatifthe
URLishttp://localhost:8080/webstore/products/tabletandwewouldliketoshow
onlytabletsonthewebpage?
Onewaytodothisistohaveaseparaterequestmappingmethodinthecontrollerfor
everyuniquecategory.However,itwon’tscaleifwehavehundredsofcategories;inthat
case,we’llhavetowriteahundredrequestmappingmethodsinthecontroller.Sohowdo
wedothisinanelegantway?
WeusetheSpringMVCURItemplatepatternfeature.IfyounotethefollowingURLs,
theonlypartthatchangesintheURListhecategorytype(laptopandtablet);otherthan
that,everythingremainsthesame:
http://localhost:8080/webstore/products/laptop
http://localhost:8080/webstore/products/tablet
So,wecandefineacommonURItemplateforthepreviouslymentionedURLs,which
mightlooklikehttp://localhost:8080/webstore/products/{category}.SpringMVC
canleveragethisfactandmakethattemplateportion({category})oftheURLavariable,
calledapathvariableintheSpringworld.
Timeforaction–showingproductsbased
oncategory
Let’saddacategoryviewtotheproductspageusingthepathvariable:
1. OpentheProductRepositoryinterfaceandaddonemoremethoddeclarationonits
getProductsByCategorymethod:
List<Product>getProductsByCategory(Stringcategory);
2. OpentheimplementationclassInMemoryProductRepositoryandaddan
implementationforthepreviouslydeclaredmethodasfollows:
publicList<Product>getProductsByCategory(Stringcategory){
List<Product>productsByCategory=newArrayList<Product>();
for(Productproduct:listOfProducts){
if(category.equalsIgnoreCase(product.getCategory())){
productsByCategory.add(product);
}
}
returnproductsByCategory;
}
3. Similarly,opentheProductServiceinterfaceandaddonemoremethoddeclaration
onitsgetProductsByCategorymethod:
List<Product>getProductsByCategory(Stringcategory);
4. OpentheserviceimplementationclassProductServiceImplandaddan
implementationasfollows:
publicList<Product>getProductsByCategory(Stringcategory){
returnproductRepository.getProductsByCategory(category);
}
5. OpentheProductControllerclassandaddonemorerequestmappingmethodas
follows:
@RequestMapping("/{category}")
publicStringgetProductsByCategory(Modelmodel,
@PathVariable("category")StringproductCategory){
model.addAttribute("products",
productService.getProductsByCategory(productCategory));
return"products";
}
6. RuntheapplicationandentertheURL
http://localhost:8080/webstore/products/tablet;youwillseesomethingas
specifiedinthefollowingscreenshot:
Showingproductsbycategorywiththehelpofpathvariables
Whatjusthappened?
Step5isthemostimportantinthewholesequencefromthepreviouslist,becauseallthe
stepspriortostep5aretheprerequisitesforstep5.Whatwearedoinginstep5isnothing
butaddingalistofproductobjectstothemodellikewenormallywould:
model.addAttribute("products",productService.getProductsByCategory(ProductC
ategory));
OnethingweneedtonotehereisthegetProductsByCategorymethodfrom
productService;weneedthismethodtogetthelistofproductsforthegivencategory,
andproductServiceassuchcannotgivethelistofproductsforthegivencategory.Itwill
asktherepository.That’swhy,instep4,weusedtheproductRepositoryreferencetoget
thelistofproductsbycategorywithintheProductServiceImplclass.Notethefollowing
codesnippetfromProductServiceImpl:
returnproductRepository.getProductsByCategory(category);
Anotherimportantthingtobenotedinthecodesnippetfromstep5isthe
@RequestMappingannotation’srequestpathvalueasfollows:
@RequestMapping("/{category}")
Byenclosingaportionofarequestpathwithincurlybraces,weindicatetotheSpring
MVCthatitisaURItemplatevariable.AccordingtoSpringMVCdocumentation,aURI
templateisaURI-likestringthatcontainsoneormorevariablenames.Whenyou
substitutevaluesforthesevariables,thetemplatebecomesaURI.
Forexample,theURItemplate
http://localhost:8080/webstore/products/{category}containsthevariable
category.Assigningthevaluelaptoptothevariableyields
http://localhost:8080/webstore/products/laptop.InSpringMVC,wecanusethe
@PathVariableannotation
(org.springframework.web.bind.annotation.PathVariable)toreadaURItemplate
variable.
Sincewehavethe@RequestMapping("/products")annotationattheProductController
level,theactualrequestpathofthegetProductsByCategorymethodwillbe
/products/{category}.Soatruntime,ifwegiveawebrequestURLas
http://localhost:8080/webstore/products/laptop,thenthecategorypathvariable
willhavethevaluelaptop.Similarly,forthewebrequest
http://localhost:8080/webstore/products/tablet,thecategorypathvariablewill
havethevaluetablet.
HowdoweretrievethevaluestoredintheURItemplatepathvariablecategory?Aswe
alreadymentioned,the@PathVariableannotationwillhelpusreadthatvariable.Allwe
needtodoissimplyannotatethegetProductsByCategorymethodparameterwiththe
@PathVariableannotationasfollows:
publicStringgetProductsByCategory(@PathVariable("category")String
productCategory,Modelmodel){
So,SpringMVCwillreadwhatevervalueispresentinthecategoryURItemplate
variableandassignittothemethodparameterproductCategory.So,wehavethe
categoryvalueinavariable,andwejustpassittoproductServicetogetthelistof
productsinthatcategory.Oncewegetthatlistofproducts,wesimplyaddittothemodel
andreturnthesameviewnamethatwehaveusedtolistalltheproducts.
Thevalueattributeinthe@PathVariableannotationshouldbethesameasthevariable
nameinthepathexpressionofthe@RequestMappingannotation.Forexample,ifthepath
expressionis"/products/{identity}",thentoretrievethepathvariableidentity,you
havetoformthe@PathVariableannotationas@PathVariable("identity").
Note
Ifthe@PathVariableannotationhasbeenspecifiedwithoutanyvalueattribute,itwilltry
toretrieveapathvariablewiththesamenameasthatofthevariablethatithasbeen
annotatedwith.
Forexample,ifyouspecifysimply@PathVariableStringproductId,thenSpringwill
assumethatitshouldlookforaURItemplatevariable"{productId}"intheURL.A
requestmappingmethodcanhaveanynumberof@PathVariableannotations.
Finally,instep6,whenweentertheURL
http://localhost:8080/webstore/products/tablet,weseeinformationabout
Google’sNexus7,whichisatablet.Similarly,ifweentertheURL
http://localhost:8080/webstore/products/laptop,weseeinformationaboutDell’s
Inspironlaptop.
Popquiz–requestpathvariable
Q1.InawebapplicationcalledWebStorethathasthefollowingrequestmappingata
controllerclasslevelandinthemethodlevel,whichistheappropriaterequestURLthat
canbeused?
@RequestMapping("/items")
publicclassProductController{
...
@RequestMapping(value="/type/{type}",method=RequestMethod.GET)
publicStringproductDetails(@PathVariable("type")StringproductType,
Modelmodel){
1. http://localhost:8080/WebStore/items/electronics
2. http://localhost:8080/WebStore/items/type/electronics
3. http://localhost:8080/WebStore/items/productType/electronics
4. http://localhost:8080/WebStore/type/electronics
Q2.Forthefollowingrequestmappingannotation,whicharethecorrectmethod
signaturestoretrievethepathvariables?
@RequestMapping(value="/manufacturer/{
manufacturerId}/product/{productId}")
1. publicStringproductByManufacturer(@PathVariableString
manufacturerId,@PathVariableStringproductId,Modelmodel)
2. publicStringproductByManufacturer(@PathVariableStringmanufacturer,
@PathVariableStringproduct,Modelmodel)
3. publicStringproductByManufacturer(@PathVariable("manufacturer")
StringmanufacturerId,@PathVariable("product")StringproductId,Model
model)
4. publicStringproductByManufacturer(@PathVariable("manufacturerId")
Stringmanufacturer,@PathVariable("productId")Stringproduct,Model
model)
Usingmatrixvariables
Inthelastsection,wesawtheURItemplatefacilitytobindvariablesintheURLrequest
path.However,thereisonemorewaytobindvariablesintherequestURLinaname-
valuepairstyle;theseboundvariablesarereferredtoasmatrixvariableswithinSpring
MVC.LookatthefollowingURL:
http://localhost:8080/webstore/products/filter/price;low=500;high=1000
InthisURL,theactualrequestpathisjustupto
http://localhost:8080/webstore/products/filter/price,afterwhichwehave
somethinglikelow=500;high=1000;here,lowandhigharejustmatrixvariables.
However,whatmakesmatrixvariablessospecialistheabilitytoassignmultiplevalues
forasinglevariable;thismeansthatwecanassignalistofvaluestoaURIvariable.Take
alookatthefollowingURL:
http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop
InthegivenURL,wehavetwovariables,namely,brandandcategory;bothhave
multiplevalues,brand=google,dellandcategory=tablet,laptop.Howdoweread
thesevariablesfromtheURLduringrequestmapping?Weusethespecialbinding
annotation@MatrixVariable
(org.springframework.web.bind.annotation.MatrixVariable).Onecoolthingabout
the@MatrixVariableannotationisthatitallowsustocollectthematrixvariablesina
mapofcollections(Map<String,List<String>>),whichwillbemorehelpfulwhenwe
aredealingwithcomplexwebrequests.
Timeforaction–showingtheproducts
basedonfilter
Considerasituationwherewewanttofiltertheproductlistbasedonthebrandand
categoryvariables.Forexample,youwanttolistalltheproductsthatfallunderthe
categorylaptopandtabletsandfromthemanufacturersgoogleanddell.Withthehelp
ofthematrixvariables,wecanformaURLtobindthebrandandcategoryvariables’
valuesintotheURLasfollows:
http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop
Let’smapthisURLtoahandlermethodwiththehelpofthe@MatrixVariableannotation:
1. OpentheProductRepositoryinterfaceandaddonemoremethoddeclaration,
getProductsByFilter,onit:
Set<Product>getProductsByFilter(Map<String,List<String>>
filterParams);
2. Opentheimplementationclass,InMemoryProductRepository,andaddthefollowing
methodimplementationforgetProductsByFilter:
publicSet<Product>getProductsByFilter(Map<String,List<String>>
filterParams){
Set<Product>productsByBrand=newHashSet<Product>();
Set<Product>productsByCategory=newHashSet<Product>();
Set<String>criterias=filterParams.keySet();
if(criterias.contains("brand")){
for(StringbrandName:filterParams.get("brand")){
for(Productproduct:listOfProducts){
if(brandName.equalsIgnoreCase(product.getManufacturer())){
productsByBrand.add(product);
}
}
}
}
if(criterias.contains("category")){
for(Stringcategory:filterParams.get("category")){
productsByCategory.addAll(this.getProductsByCategory(category));
}
}
productsByCategory.retainAll(productsByBrand);
returnproductsByCategory;
}
3. OpentheinterfaceProductServiceandaddonemoremethoddeclarationonit
calledgetProductsByFilterasfollows:
Set<Product>getProductsByFilter(Map<String,List<String>>
filterParams);
4. Opentheserviceimplementationclass,ProductServiceImpl,andaddthefollowing
methodimplementationforgetProductsByFilter:
publicSet<Product>getProductsByFilter(Map<String,List<String>>
filterParams){
returnproductRepository.getProductsByFilter(filterParams);
}
5. OpenProductControllerandaddonemorerequestmappingmethodasfollows:
@RequestMapping("/filter/{ByCriteria}")
publicStringgetProductsByFilter(@MatrixVariable(pathVar=
"ByCriteria")Map<String,List<String>>filterParams,Modelmodel){
model.addAttribute("products",
productService.getProductsByFilter(filterParams));
return"products";
}
6. Openthewebapplicationcontextconfigurationfile(DispatcherServlet-
context.xml)fromsrc/main/webapp/WEB-INF/spring/webcontext/andenable
matrixvariablesupportbysettingenable-matrix-variablestotrueinthe
<mvc:annotation-driven>tagasfollows:
<mvc:annotation-drivenenable-matrix-variables="true"/>
7. Finally,runtheapplicationandentertheURL
http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop
youwillseetheproductlistingasshowninthefollowingscreenshot:
Usageofmatrixvariablesshowingproductlistfilteredbycriteria
Whatjusthappened?
OuraimistoretrievethematrixvariablevaluesfromtheURLanddosomethinguseful;
inourcase,theURLwearetryingtomapis
http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop
wherewewanttoextractthematrixvariablesbrandandcategory.Thebrandand
categoryvariableshavethevaluesgoogle,dellandtablet,laptop,respectively.Inthe
previousURL,therequestpathisupto
http://localhost:8080/webstore/products/filter/ByCriteriaonly.That’swhy,in
step5,weannotatedourgetProductsByFilterrequestmappingmethodasfollows:
@RequestMapping("/filter/{ByCriteria}")
However,youmaywonderwhywehaveaURItemplate(/{ByCriteria})inthe
@RequestMappingannotation,whichislikemappingtoapathvariable.Itisbecauseifour
requestURLcontainsthematrixvariable,thenwewillhavetoformthe@RequestMapping
annotationwithaURItemplatetoidentifythestartingofmatrixvariable’ssegments
withinURL.That’swhywedefinedByCriteriaasaURItemplateintherequestmapping
annotation(@RequestMapping("/filter/{ByCriteria}")).
Note
AURLcanhavemultiplematrixvariables;eachmatrixvariablewillbeseparatedwitha;
(semicolon).Toassignmultiplevaluestoasinglevariable,eachvaluemustbeseparated
bya“,”(comma),orwecanrepeatthevariablename.SeethefollowingURL,whichisa
repeatedvariableversionofthesameURLthatweusedinourexample:
http://localhost:8080/webstore/products/filter/ByCategory;brand=google;brand=dell;category=tablet;category=laptop
NotethatwerepeatedthevariablesbrandandcategorytwiceintheURL.
WemappedthewebrequesttothegetProductsByFiltermethod,buthowdoweretrieve
thevaluefromthematrixvariables?Theansweristhe@MatrixVariableannotation.Itis
quitesimilartothe@PathVariableannotation;ifyounoticethegetProductsByFilter
methodsignatureinstep5,weannotatedthemethodparameterfilterParamswiththe
@MatrixVariableannotationasfollows:
publicStringgetProductsByFilter(@MatrixVariable(pathVar="ByCriteria")
Map<String,List<String>>filterParams,Modelmodel)
So,SpringMVCwillreadallthematrixvariablesfoundintheURLafterthe
{ByCriteria}URItemplateandplacethosematrixvariablesintothemapofthemethod
parameterfilterParams.ThefilterParamsmapwillhaveeachmatrixvariablenameas
keyandthecorrespondinglistwillcontainthemultiplevaluesassignedforthematrix
variable.ThepathVarattributefromthe@MatrixVariableannotationisusedtoidentify
thematrixvariablesegmentintheURL;that’swhyithasthevalueByCriteria,whichis
nothingbuttheURItemplatevaluethatweusedinourrequestmappingURL.
AURLcanhavemultiplematrixvariablesegments.TakealookatthefollowingURL:
http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop/BySpecification;dimention=10,20,15;color=red,green,blue
Itcontainstwomatrixvariablesegments,eachidentifiedbytheprefixesByCriteriaand
BySpecification,respectively.Soinordertocaptureeachmatrixvariablesegmentintoa
map,wehavetoformthecontrollermethodsignatureasfollows:
@RequestMapping("/filter/{ByCriteria}/{BySpecification}")
publicStringfilter(@MatrixVariable(pathVar="ByCriteria")
Map<String,List<String>>criteriaFilter,@MatrixVariable(pathVar="
BySpecification")Map<String,List<String>>specFilter,Modelmodel){
WegotthevalueofthematrixvariablesintothemethodparameterfilterParams,but
whatdidwedowiththatfilterParamsmap?Wesimplypasseditasaparametertothe
servicemethodtoretrievetheproductsbasedoncriteriaasfollows:
productService.getProductsByFilter(filterParams)
Again,theservicepassesthatmaptotherepositorytogetthelistofproductsbasedonthe
criteria.Oncewegetthelist,wesimplyaddthatlisttothemodelandreturnthesame
viewnamethatwasusedtolisttheproducts.
ToenabletheuseofmatrixvariablesinSpringMVC,wesettheenable-matrix-
variablesattributeofthe<mvc:annotation-driven>tagtotrue;wedidthisinstep6.
Finally,wewereabletoviewtheproductsbasedonthespecifiedcriteriainstep7onour
productlistingpage.
Understandingrequestparameters
MatrixvariablesandpathvariablesareagreatwayofbindingvariablesintheURL
requestpath.However,thereisonemorewaytobindvariablesintheHTTPrequest,not
onlyasapartoftheURLbutalsointhebodyoftheHTTPwebrequest,whicharetheso-
calledHTTPparameters.YoumighthaveheardabouttheGETorPOSTparameters.GET
parametershavebeenusedforyearsasastandardwaytobindvariablesintheURL,and
POSTparametersareusedtobindvariablesinthebodyoftheHTTPrequest.Wewill
learnaboutPOSTparametersinthenextchapterduringformsubmission.
Okay,nowlet’sseehowtoreadGETrequestparametersusingtheSpringMVCstyle.To
demonstratetheuseoftherequestparameter,let’saddaproductdetailspagetoour
application.
Timeforaction–addingtheproduct
detailspage
Sofarinourproductlistingpage,wehaveonlyshownproductinformationsuchasthe
product’sname,description,price,andavailableunitsinstock.However,wehaven’t
showninformationsuchasthemanufacturer’sname,category,productID,andsoon.
Let’screateaproductdetailspagedisplayingthisinformationasfollows:
1. OpentheProductControllerclassandaddonemorerequestmappingmethodas
follows:
@RequestMapping("/product")
publicStringgetProductById(@RequestParam("id")StringproductId,
Modelmodel){
model.addAttribute("product",
productService.getProductById(productId));
return"product";
}
2. AddonemoreJSPviewfilecalledproduct.jspunderthedirectory
src/main/webapp/WEB-INF/views/,andaddthefollowingcodesnippetintoitand
saveit:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-
1">
<linkrel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>Products</h1>
</div>
</div>
</section>
<sectionclass="container">
<divclass="row">
<divclass="col-md-5">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>
<strong>ItemCode:</strong><spanclass="labellabel-
warning">${product.productId}</span>
</p>
<p>
<strong>manufacturer</strong>:${product.manufacturer}
</p>
<p>
<strong>category</strong>:${product.category}
</p>
<p>
<strong>Availbleunitsinstock</strong>:
${product.unitsInStock}
</p>
<h4>${product.unitPrice}USD</h4>
<p>
<ahref="#"class="btnbtn-warningbtn-large"><span
class="glyphicon-shopping-cartglyphicon"></span>OrderNow
</a>
</p>
</div>
</div>
</section>
</body>
</html>
3. Now,runtheapplicationandentertheURL
http://localhost:8080/webstore/products/product?id=P1234;youwillseethe
productdetailspageasshowninthefollowingscreenshot:
Usageofrequestparametershowingproductdetailspage
Whatjusthappened?
Whatwedidinstep1isverysimilartowhatwedidinthegetProductsByCategory
methodofProductController.Wejustaddedaproductobjecttothemodelthatis
returnedbytheserviceobjectasfollows:
model.addAttribute("product",productService.getProductById(productId));
However,theimportantquestionhereis,whoisgivingthevalueoftheparameter
productId?Theanswerissimple,asyouguessed;sinceweannotatedtheparameter
productIdwiththe@RequestParam("id")annotation
(org.springframework.web.bind.annotation.RequestParam),SpringMVCwilltryto
readaGETrequestparameterwiththenameidfromtheURLandassignittothe
getProductByIdmethodparameter,productId.
The@RequestParamannotationalsofollowsthesameconventionforotherbinding
annotations;thatis,ifthenameoftheGETrequestparameterandthenameofthevariable
itisannotatedwitharethesame,thentherewillbenoneedtospecifythevalueattribute
inthe@RequestParamannotation.
Finally,instep6,weaddedonemoreviewfilecalledproduct.jspbecausewewanteda
detailedviewoftheproductsothatwecoulddisplayalltheinformationabouttheproduct.
Nothingfancyinthisproduct.jsp;asusual,wegetthevaluefromthemodelandshowit
withinHTMLtagsusingtheusualJSTLexpressionlanguagenotation${}asfollows:
<h3>${product.name}</h3>
<p>${product.description}</p>
......
WesawhowtoretrieveaGETrequestparameterfromtheURL,buthowdowepassmore
thanoneGETrequestparameterintheURL?Theansweristhatweneedtodelimiteach
parametervaluepairwithan&symbol;forexample,ifwewanttopasscategoryand
priceasGETrequestparametersinaURL,wehavetoformtheURLasfollows:
http://localhost:8080/WebStore/products/product?category=laptop&price=700
Similarly,tomaptheprecedingURLinarequestmappingmethod,ourrequestmapping
methodshouldhaveatleasttwoparameterswiththe@RequestParamannotation:
publicStringgetProducts(@RequestParamStringcategory,@RequestParam
Stringprice){
Popquiz–therequestparameter
Q1.WhichistheappropriaterequestURLforthefollowingrequestmappingmethod
signature?
@RequestMapping(value="/products",method=RequestMethod.GET)
publicStringproductDetails(@RequestParamStringrate,Modelmodel)
1. http://localhost:8080/webstore/products/rate=400
2. http://localhost:8080/webstore/products?rate=400
3. http://localhost:8080/webstore/products?rate/400
4. http://localhost:8080/webstore/products/rate=400
Timeforaction–implementingamaster
detailview
Amasterdetailviewisnothingbutthedisplayofveryimportantinformationonamaster
page.Onceweselectaniteminthemasterview,adetailedpageoftheselecteditemwill
beshowninthedetailviewpage.Let’sbuildamasterdetailviewforourproductlisting
pagesothatwhenweclickonanyproduct,weseethedetailedviewofthatproduct.
Wehavealreadyimplementedtheproductlistingpage
(http://localhost:8080/webshop/products)andproductdetailspage
(http://localhost:8080/webstore/products/product?id=P1234),sotheonlything
neededistoconnectthesetwoviewstomakeamasterdetailview.Performthefollowing
steps:
1. Openproducts.jsp;youcanfindproducts.jspundersrc/main/webapp/WEB-
INF/views/inyourprojectandaddthefollowingspringtaglibreferenceontopof
thefile:
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%>
2. AddthefollowinglinesaftertheAvailableunitsinstockparagraphtagin
products.jsp:
<p>
<ahref="<spring:urlvalue="/products/product?
id=${product.productId}"/>"class="btnbtn-primary">
<spanclass="glyphicon-info-signglyphicon"/></span>Details
</a>
</p>
3. Now,openproduct.jsp;youcanfindproduct.jspundersrc/main/webapp/WEB-
INF/views/inyourprojectandaddthefollowingspringtaglibreferenceontopof
thefile:
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%>
And,addthefollowinglinesjustbeforetheOrderNowlinkinproduct.jsp:
<ahref="<spring:urlvalue="/products"/>"class="btnbtn-default">
<spanclass="glyphicon-hand-leftglyphicon"></span>back
</a>
4. RuntheapplicationandentertheURL
http://localhost:8080/webstore/products;youwillbeabletoseeaproductlist
pagethathasaDetailsbuttonwitheveryproduct,asspecifiedinthefollowing
screenshot:
Themasterviewoftheproductlisting
5. Finally,clickonanyproduct’sDetailsbutton,andyouwillbeabletoseethedetail
viewwiththebackbuttonlinktotheproductlistingpage.
Whatjusthappened?
Whatwedidissimple.Instep2,weaddedahyperlinkusingthefollowingtagin
products.jsp:
<ahref="<spring:urlvalue="/products/product?id=${product.productId}"
/>"htmlEscape="true"/>"class="btnbtn-primary">
<spanclass="glyphicon-info-signglyphicon"/></span>Details
</a>
Notethehrefattributeofthe<a>tagasfollows,whichhasa<spring:url>tagasthe
value:
<spring:urlvalue="/products/product?id=${product.productId}"/>
This<spring:url>tagisusedtoconstructavalidSpringURL.Weneededthis
<spring:url>tobeusedinstep2;that’swhyweaddedareferencetothespringtag
libraryinstep1.Observethevalueattributeofthe<spring:url>tag;wecannotethatfor
theidURLparameter,weassignedtheexpression${product.productId}.So,while
renderingthislink,SpringMVCwillassignthecorrespondingproductIDinthat
expression.
Forexample,whilerenderingthelinkofthefirstproduct,SpringMVCwillassignthe
valueP1234fortheproductID.So,thefinalURLvaluewithin<spring:url>willbecome
/products/product?id=P1234,whichisnothingbuttherequestmappingpathofthe
productdetailspage.So,whenyouclickonthislink,youlandonthedetailspageofthat
product.
Similarly,weneedalinktotheproductlistingpagefromtheproductdetailspage;that’s
whyweaddedanotherlinkintheproduct.jsptaginstep4asfollows:
<ahref="<spring:urlvalue="/products"/>"class="btnbtn-default">
<spanclass="glyphicon-hand-leftglyphicon"></span>back
</a>
Notethatthespantagisjustforstylingthebuttonwiththeicon,soyouneedn’tmindit
thatmuch.Theonlyinterestingthingforusisthehrefattributeofthe<a>tag,whichhas
the<spring:url>tagwiththevalueattribute/productsonit.
Haveagohero–addingmultiplefilterstolist
products
ItisgoodthatwelearnedvarioustechniquestobindparameterswithURLs,suchasusing
pathvariables,matrixvariables,andGETparameters.Wesawhowtogetproductsofa
particularcategoryusingpathvariables,howtogetproductswithinaparticularprice
range,andfinally,wesawhowtogetaparticularproductbytheproductID.
Now,imaginethatyouwanttoapplymultiplecriteriatoviewadesiredproduct;for
example,whatifyouwanttoviewaproductthatfallsunderthetabletcategory,iswithin
thepricerangeof$200to$400,andhasbeenmanufacturedbyGoogle?
Toretrieveaproductthatcansatisfyallofthepreviouslymentionedcriteria,wecanform
aURLasfollows:
http://localhost:8080/webstore/products/tablet/price;low=200;high=400?
manufacturer="Google"
Whydon’tyouwriteacorrespondingcontrollermethodtoservetheprecedingrequest
URL?Herearesomehintstoaccomplishtherequirement:
Createarepositorylayermethodtoreturnalltheproductsbasedonmanufacturer.
Forthis,addamethoddeclarationintheproductRepositoryinterfacetogetthe
productsbymanufacturerasfollows:
List<Product>getProductsByManufacturer(Stringmanufacturer);
AddanimplementationforthegetProductsByManufacturer()methodin
InMemoryProductRepository.ItislikethegetProductsByCategory()method;the
onlydifferenceisthatinsteadofthecategory,wefetchtheproductsbymanufacturer
name.
ExtendtheproductServiceinterfacewiththegetProductsByManufacturer()
methodandimplementthesamemethodintheproductServiceImplclass.
CreateonemorerequestmappingmethodcalledfilterProductsinthe
productControllerclasstomapthefollowingURL:
http://localhost:8080/webstore/products/tablet/price;low=200;high=400?
manufacturer="Google"
RememberthatthisURLcontainsthematrixvariableslowandhightorepresentthe
pricerange,theGETparametermanufacturertoidentifythemanufacturer,and
finally,aURItemplatepathvariabletablettorepresentthecategory.
Usethesameviewfileproducts.jsptolistthefilteredproducts.
RememberthatgetProductsByCategoryfromproductServicereturnsproductsbasedon
category,thegetProductsBypriceFiltermethodreturnsproductswithinacertainprice
range,andfinally,ournewlyintroducedmethod,getProductsByManufacturer,returns
productsbelongingtoaparticularmanufacturer.Youhavetocombinethesethreemethod
resultsbeforeupdatingthemodelwiththeproductlistinthefilterProductscontroller
method.Youcanprobablyusejava.util.Settocombinetheresultsofthesethree
servicemethodstoavoidduplication.Goodluck!
Summary
Inthischapter,welearnedhowtodefineacontrollerandtheusageofthe@Controller
annotation.Afterthat,welearnedtheconceptofrelativerequestmapping,wherewesaw
howtodefinerequestmappingatthecontrollerlevelandunderstoodhowSpring
relativelymapsawebrequesttothecontrollerrequestmappingmethod.Wethenlearned
abouttheroleofacontrollerinSpringMVCandabouthowthedispatcherservletuses
handlermappingtofindouttheexacthandlermethods.Wealsosawvariousparameter
bindingtechniques,suchasURItemplatepatterns,matrixvariables,andHTTPGET
requestparameterstobindparameterswithURLs.Finally,wesawhowtoimplementa
masterdetailview.
Inthenextchapter,wearegoingtoexplorevariousSpringtagsthatareavailableinthe
springtaglibrary.Wewillalsolearnmoreaboutformprocessingandhowtobindform
datawiththeHTTPPOSTparameter.Getreadyforthenextchapter!
Chapter4.WorkingwithSpringTag
Libraries
Inpreviouschapters,welearnedhowtoputdataintothemodelfromthecontroller,but
wehaven’tseenhowtodothistheotherwayaround.Thismeansthatwehaven’tlearned
howtoputthedatafromtheviewintothemodel.InSpringMVC,theprocessofputtingan
HTMLformelement’svaluesintomodeldataiscalledformbinding.
SpringMVCprovidessomeJSPtaglibrariestomakeiteasiertobindformelementsto
modeldata.Springtaglibrariesalsosupportvariousothercommonfunctionalities,such
as,externalizingmessagesanderrorhandling.Inthischapter,wearegoingtolearnmore
abouthowtomakeuseofthesepredefinedtaglibrariesofSpring.
Afterfinishingthischapter,wewillhaveagoodideaaboutthefollowingtopics:
Servingandprocessingwebforms
Formbindingandwhitelisting
Springtaglibraries
Servingandprocessingforms
Springsupportsdifferentviewtechnologies,butifweareusingJSP-basedviews,wecan
makeuseoftheSpringtaglibrarytagstomakeupourJSPpages.Thesetagsprovide
manyuseful,commonfunctionalitiessuchasformbinding,evaluatingerrorsoutputting
internationalizedmessages,andsoon.Inordertousethesetags,wemustaddreferences
tothistaglibraryinourJSPpagesasfollows:
<%@taglibprefix="form"uri="http://www.springframework.org/tags/form"%>
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%>
Inallofourpreviouschapters’examples,wesawthatthedatatransfertookplacefrom
modeltoviewviathecontroller.Thefollowinglineisatypicalexampleofhowweput
dataintothemodelfromacontroller:
model.addAttribute(greeting,"Welcome")
SimilarlythenextlineshowshowweretrievethatdataintheviewusingtheJSTL
expression:
<p>${greeting}</p>
Note
JavaServerPagesStandardTagLibrary(JSTL)isalsoataglibraryprovidedby
Oracle.AnditisacollectionofusefulJSPtagsthatencapsulatesthecorefunctionality
commontomanyJSPpages.WecanaddareferencetotheJSTLtaglibraryinourJSP
pagesas<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>.
However,whatifwewanttoputdataintothemodelfromtheview?Howdoweretrieve
thatdatafromthecontroller?Forexample,considerascenariowhereanadminofour
storewantstoaddnewproductinformationinourstorebyfillingandsubmittingan
HTMLform.HowcanwecollectthevaluesfilledintheHTMLformelementsand
processitinthecontroller?ThisiswheretheSpringtaglibrarytagshelpustobindthe
HTMLtagelement’svaluestoaform-backingbeaninthemodel.Later,thecontrollercan
retrievetheform-backingbeanfromthemodelusingthe@ModelAttributeannotation
(org.springframework.web.bind.annotation.ModelAttribute).
Note
Form-backingbeans(sometimescalledformbeans)areusedtostoreformdata.Wecan
evenuseourdomainobjectsasformbeans;thisworkswellwhenthere’saclosematch
betweenthefieldsontheformandthepropertiesonourdomainobject.Anotherapproach
istocreateseparateclassesforformbeans,whicharesometimescalledDataTransfer
Objects(DTOs).
Timeforaction–servingandprocessing
forms
TheSpringtaglibraryprovidessomespecial<form>and<input>tagsthataremoreor
lesssimilartoHTMLformandinputtags,butithassomespecialattributestobindthe
formelementsdatawiththeform-backingbean.Let’screateaSpringwebforminour
applicationtoaddnewproductstoourproductlistbyperformingthefollowingsteps:
1. WeopenourProductRepositoryinterfaceandaddonemoremethoddeclarationin
itasfollows:
voidaddProduct(Productproduct);
2. WethenaddanimplementationforthismethodintheInMemoryProductRepository
classasfollows:
publicvoidaddProduct(Productproduct){
listOfProducts.add(product);
}
3. WeopenourProductServiceinterfaceandaddonemoremethoddeclarationinitas
follows:
voidaddProduct(Productproduct);
4. And,weaddanimplementationforthismethodintheProductServiceImplclassas
follows:
publicvoidaddProduct(Productproduct){
productRepository.addProduct(product);
}
5. WeopenourProductControllerclassandaddtwomorerequestmappingmethods
asfollows:
@RequestMapping(value="/add",method=RequestMethod.GET)
publicStringgetAddNewProductForm(Modelmodel){
ProductnewProduct=newProduct();
model.addAttribute("newProduct",newProduct);
return"addProduct";
}
@RequestMapping(value="/add",method=RequestMethod.POST)
publicStringprocessAddNewProductForm(@ModelAttribute("newProduct")
ProductnewProduct){
productService.addProduct(newProduct);
return"redirect:/products";
}
6. Finally,weaddonemoreJSPviewfilecalledaddProduct.jspunder
src/main/webapp/WEB-INF/views/andaddthefollowingtagreferencedeclaration
initastheveryfirstline:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglibprefix="form"uri="http://www.springframework.org/tags/form"
%>
7. Now,weaddthefollowingcodesnippetunderthetagdeclarationlineandsave
addProduct.jsp(notethatIhaveskippedthe<form:input>bindingtagsforsomeof
thefieldsoftheproductdomainobject,butIstronglyencouragethatyouaddbinding
tagsfortheskippedfieldswhenyoutryoutthisexercise):
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-
1">
<link
rel="stylesheet"href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/boo
tstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>Products</h1>
<p>Addproducts</p>
</div>
</div>
</section>
<sectionclass="container">
<form:formmodelAttribute="newProduct"class="form-horizontal">
<fieldset>
<legend>Addnewproduct</legend>
<divclass="form-group">
<labelclass="control-labelcol-lg-2col-lg-2"
for="productId">ProductId</label>
<divclass="col-lg-10">
<form:inputid="productId"path="productId"type="text"
class="form:input-large"/>
</div>
</div>
<!--Similarlybind<form:input>tagfor
name,unitPrice,manufacturer,category,unitsInStockandunitsInOrder
fields-->
<divclass="form-group">
<labelclass="control-labelcol-lg-2"
for="description">Description</label>
<divclass="col-lg-10">
form:textareaid="description"path="description"rows=
"2"/>
</div>
</div>
<divclass="form-group">
<labelclass="control-labelcol-lg-2"
for="discontinued">Discontinued</label>
<divclass="col-lg-10">
<form:checkboxid="discontinued"path="discontinued"/>
</div>
</div>
<divclass="form-group">
<labelclass="control-labelcol-lg-2"
for="condition">Condition</label>
<divclass="col-lg-10">
<form:radiobuttonpath="condition"value="New"/>New
<form:radiobuttonpath="condition"value="Old"/>Old
<form:radiobuttonpath="condition"value="Refurbished"
/>Refurbished
</div>
</div>
<divclass="form-group">
<divclass="col-lg-offset-2col-lg-10">
<inputtype="submit"id="btnAdd"class="btnbtn-primary"
value="Add"/>
</div>
</div>
</fieldset>
</form:form>
</section>
</body>
</html>
8. Now,werunourapplicationandentertheURL
http://localhost:8080/webstore/products/add.Wewillbeabletoseeaweb
pagethatdisplaysawebformwherewecanaddtheproductinformationasshownin
thefollowingscreenshot:
Addtheproduct’swebform
9. Now,weenteralltheinformationrelatedtothenewproductthatwewanttoaddand
clickontheAddbutton;wewillseethenewproductaddedintheproductlisting
pageundertheURLhttp://localhost:8080/webstore/products.
Whatjusthappened?
Inthewholesequence,steps5and6areveryimportantstepsthatneedtobeobserved
carefully.Whateverismentionedpriortostep5isfamiliaraswehaveseenitinprevious
recipes;anyhow,Iwillgiveyouabriefnoteonwhatwehavedoneinsteps1to4.
Instep1,wecreatedamethoddeclarationaddProductinourProductRepository
interfacetoaddnewproducts.Instep2,weimplementedtheaddProductmethodinour
InMemoryProductRepositoryclass;theimplementationisjusttoupdatetheexisting
listOfProductsbyaddinganewproducttothelist.Steps3and4arejustaservicelayer
extensionforProductRepository.Instep3,wedeclaredasimilarmethod,addProduct,
inourProductServiceinterfaceandimplementeditinstep4toaddproductstothe
repositoryviatheproductRepositoryreference.
Okay,comingbacktotheimportantstep;wehavedonenothingbutaddedtworequest
mappingmethods,namely,getAddNewProductFormandprocessAddNewProductForm,in
step5asfollows:
@RequestMapping(value="/add",method=RequestMethod.GET)
publicStringgetAddNewProductForm(Modelmodel){
ProductnewProduct=newProduct();
model.addAttribute("newProduct",newProduct);
return"addProduct";
}
@RequestMapping(value="/add",method=RequestMethod.POST)
publicStringprocessAddNewProductForm(@ModelAttribute("newProduct")
ProductproductToBeAdded){
productService.addProduct(productToBeAdded);
return"redirect:/products";
}
Ifyouobservethesemethodscarefully,youwillnoticeapeculiarthing,whichisthatboth
themethodshavethesameURLmappingvalueintheir@RequestMappingannotation
(value="/add").So,ifweentertheURL
http://localhost:8080/webstore/products/addinthebrowser,whichmethodwill
SpringMVCmapthatrequestto?
Theanswerliesinthesecondattributeofthe@RequestMappingannotation(method=
RequestMethod.GETandmethod=RequestMethod.POST).Ifyouwillnoticeagain,even
thoughbothmethodshavethesameURLmapping,theydifferinrequestmethod.
So,whatishappeningbehindthescreenisthatwhenweentertheURL
http://localhost:8080/webstore/products/addinthebrowser,itisconsideredasa
GETrequest.So,SpringMVCmapsthisrequesttothegetAddNewProductFormmethod,
andwithinthismethod,wesimplyattachanewemptyProductdomainobjecttothe
modelundertheattributename,newProduct.
ProductnewProduct=newProduct();
model.addAttribute("newProduct",newProduct);
Sointheviewaddproduct.jsp,wecanaccessthismodelobject,newProduct.Before
jumpingintotheprocessAddNewProductFormmethod,let’sreviewtheaddproduct.jsp
viewfileforsometimesothatweareabletounderstandtheformprocessingflowwithout
confusion.Inaddproduct.jsp,wehavejustaddeda<form:form>tagfromtheSpringtag
libraryusingthefollowinglineofcode:
<form:formmodelAttribute="newProduct"class="form-horizontal">
Sincethisspecial<form:form>tagisacquiredfromtheSpringtaglibrary,weneedtoadd
areferencetothistaglibraryinourJSPfile.That’swhywehaveaddedthefollowingline
atthetopoftheaddProducts.jspfileinstep6:
<%@taglibprefix="form"uri="http://www.springframework.org/tags/form"%>
IntheSpring<form:form>tag,oneoftheimportantattributesismodelAttribute.Inour
case,weassignedthevaluenewProductasthevalueofmodelAttributeinthe
<form:form>tag.Ifyourecallcorrectly,youwillnoticethatthisvalueofmodelAttribute
andtheattributenameweusedtostorethenewProductobjectinthemodelfromour
getAddNewProductFormmethodarethesame.So,thenewProductobjectthatweattached
tothemodelinthecontrollermethod(getAddNewProductForm)isnowboundtotheform.
Thisobjectiscalledtheform-backingbeaninSpringMVC.
Okay,nownoticeeach<form:input>taginsidethe<form:form>tagshowninthe
followingcode.Youwillobservethatthereisacommonattributeineverytag.This
attributenameispath:
<form:inputid="productId"path="productId"type="text"class="form:input-
large"/>
Thepathattributejustindicatesthefieldnamethatisrelativetotheform-backingbean.
So,thevaluethatisenteredinthisinputboxatruntimewillbeboundtothe
correspondingfieldoftheformbean.
Okay,nowisthetimetocomebackandreviewourprocessAddNewProductFormmethod.
Whenwillthismethodbeinvoked?Thismethodwillbeinvokedoncewepressthesubmit
buttonofourform.Yes,sinceeveryformsubmissionisconsideredasaPOSTrequest,
thistimethebrowserwillsendaPOSTrequesttothesameURL,thatis,
http://localhost:8080/webstore/products/add.
So,thistime,theprocessAddNewProductFormmethodwillgetinvokedsinceitisaPOST
request.InsidetheprocessAddNewProductFormmethod,wesimplycalltheservice
methodaddProducttoaddthenewproducttotherepository,asfollows:
productService.addProduct(productToBeAdded);
However,theinterestingquestionhereis,howistheproductToBeAddedobjectpopulated
withthedatathatweenteredintheform?Theanswerlieswithinthe@ModelAttribute
annotation(org.springframework.web.bind.annotation.ModelAttribute).Notethe
methodsignatureoftheprocessAddNewProductFormmethodshowninthefollowingline
ofcode:
publicStringprocessAddNewProductForm(@ModelAttribute("newProduct")
ProductproductToBeAdded)
Here,ifyounoticethevalueattributeofthe@ModelAttributeannotation,youwill
observeapattern.Thevaluesofthe@ModelAttributeannotationandmodelAttribute
fromthe<form:form>tagarethesame.So,SpringMVCknowsthatitshouldassignthe
form-boundnewProductobjecttotheproductToBeAddedparameterofthe
processAddNewProductFormmethod.
The@ModelAttributeannotationisnotonlyusedtoretrieveanobjectfromamodel,but
ifwewantto,wecanevenuseittoaddobjectstothemodel.Forinstance,werewriteour
getAddNewProductFormmethodtosomethinglikethefollowingcodewiththeuseofthe
@ModelAttributeannotation:
@RequestMapping(value="/add",method=RequestMethod.GET)
publicStringgetAddNewProductForm(@ModelAttribute("newProduct")Product
newProduct){
return"addProduct";
}
Youcannoticethatwehaven’tcreatedanynewemptyProductdomainobjectand
attachedittothemodel.AllwehavedonewasaddedaparameterofthetypeProductand
annotateditwiththe@ModelAttributeannotationsothatSpringMVCwouldknowthatit
shouldcreateanobjectofProductandattachittothemodelunderthenamenewProduct.
OnemorethingthatneedstobeobservedintheprocessAddNewProductFormmethodis
thelogicalviewname,redirect:/products,thatitreturns.So,whatarewetryingtotell
SpringMVCbyreturningastringredirect:/products?Togettheanswer,observethe
logicalviewnamestringcarefully.Ifwesplitthisstringwiththe:(colon)symbol,we
willgettwoparts;thefirstpartistheprefixredirectandthesecondpartissomething
thatlookslikearequestpath,/products.So,insteadofreturningaviewname,wesimply
instructSpringtoissuearedirectrequesttotherequestpath,/products,whichisthe
requestpathforthelistmethodofourProductControllerclass.So,aftersubmittingthe
form,welisttheproductsusingthelistmethodofProductController.
Note
Asamatteroffact,whenwereturnanyrequestpathwiththeredirect:prefixfroma
requestmappingmethod,Springusesaspecialviewobject,RedirectView
(org.springframework.web.servlet.view.RedirectView),toissuearedirectcommand
behindthescreen.WewillseemoreaboutRedirectViewintheupcomingchapter.
Insteadoflandinginawebpageafterthesuccessfulsubmissionofawebform,weare
spawninganewrequesttotherequestpath/productswiththehelpofRedirectView.
ThispatterniscalledRedirectAfterPost,whichisacommonpatterntousewithweb-
basedforms.Weareusingthispatterntoavoiddoublesubmissionofthesameform;
sometimes,ifwepressthebrowser’srefreshbuttonorbackbuttonaftersubmittingthe
form,therearechancesthatthesameformwillberesubmitted.
Customizingdatabinding
Inthelastsection,wesawhowtobinddatasubmittedbyanHTMLformorbyquery
stringparameterstoaform-backingbean.Inordertodothebinding,SpringMVC
internallyusesaspecialbindingobjectcalledWebDataBinder
(org.springframework.web.bind.WebDataBinder).
TheWebDataBinderobjectextractsthedataoutoftheHttpServletRequestobject,
convertsittoaproperdataformat,loadsitintoaform-backingbean,andvalidatesit.To
customizethebehaviorofthedatabinding,wecaninitializeandconfigurethe
WebDataBinderobjectinourcontroller.The@InitBinderannotation
(org.springframework.web.bind.annotation.InitBinder)helpsusdothis.The
@InitBinderannotationdesignatesamethodtoinitializeWebDataBinder.
Let’sseeapracticalwayofcustomizingWebDataBinder.Sinceweareusingtheactual
domainobjectitselfastheform-backingbean,duringformsubmission,thereisachance
ofsecurityvulnerability.SinceSpringautomaticallybindsHTTPparameterstoformbean
properties,anattackercouldbindsuitablynamedHTTPparameterswithformproperties
thatweren’tintendedforbinding.Toaddressthisproblem,wecanexplicitlytellSpring
whichfieldsareallowedforformbinding.Technicallyspeaking,theprocessofexplicitly
specifyingtheallowedfieldsforformbindingiscalledwhitelistingformfieldsinSpring
MVC;wecandowhitelistingusingWebDataBinder.
Timeforaction–whitelistingformfields
Inthepreviousexercise,whileaddinganewproduct,weboundeveryfieldoftheProduct
domainintheform.However,itismeaninglesstospecifytheunitsInOrderand
discontinuedvaluesduringtheadditionofanewproductbecausenobodycanmakean
orderbeforeaddingtheproducttothestore,andsimilarly,thediscontinuedproducts
neednotbeaddedinourproductlist.So,weshouldnotallowthesefieldstobeboundto
theformbeanwhileaddinganewproducttoourstore.However,alltheotherfieldsofthe
Productdomainobjectneedtobebound.Let’sseehowtodothiswiththefollowing
steps:
1. WeopenourProductControllerclassandaddamethodasfollows:
@InitBinder
publicvoidinitialiseBinder(WebDataBinderbinder){
binder.setDisallowedFields("unitsInOrder","discontinued");
}
2. WethenaddanextraparameterofthetypeBindingResult
(org.springframework.validation.BindingResult)tothe
processAddNewProductFormmethodasfollows:
publicStringprocessAddNewProductForm(@ModelAttribute("newProduct")
ProductproductToBeAdded,BindingResultresult)
3. InthesameprocessAddNewProductFormmethod,weaddthefollowingconditionjust
beforethelinewherewesavedtheproductToBeAddedobject:
String[]suppressedFields=result.getSuppressedFields();
if(suppressedFields.length>0){
thrownewRuntimeException("Attemptingtobinddisallowedfields:"
+StringUtils.arrayToCommaDelimitedString(suppressedFields));
}
4. Now,werunourapplicationandentertheURL
http://localhost:8080/webstore/products/add;wewillbeabletoseeaweb
pagethatdisplaysawebformwherewecanaddnewproductinformation.Let’sfill
everyfield,particularlyUnitsinorderandDiscontinued.
5. Now,clickontheAddbutton.YouwillseeanHTTPStatus500errorontheweb
page,asshowninthefollowingscreenshot:
Addproductpageshowingerrorfordisallowedfields
6. Now,weopenaddProduct.jspfrom/webstore/src/main/webapp/WEB-INF/views/
inourprojectandremovetheinputtagsthatarerelatedtotheUnitsinorderand
Discontinuedfields.Basically,weneedtoremovethefollowingblockofcode:
<divclass="form-group">
<labelclass="control-labelcol-lg-2"for="unitsInOrder">UnitsIn
Order</label>
<divclass="col-lg-10">
<form:inputid="unitsInOrder"path="unitsInOrder"type="text"
class="form:input-large"/>
</div>
</div>
<divclass="form-group">
<labelclass="control-labelcol-lg-2"
for="discontinued">Discontinued</label>
<divclass="col-lg-10">
<form:checkboxid="discontinued"path="discontinued"/>
</div>
</div>
7. Now,werunourapplicationagainandentertheURL
http://localhost:8080/webstore/products/add.Wewillbeabletoseeaweb
pagethatdisplaysawebformwherewecanaddanewproduct,butthistime,
withouttheUnitsinorderandDiscontinuedfields.
8. Now,weenteralloftheinformationrelatedtothenewproductandclickontheAdd
button;wewillseethenewproductaddedintheproductlistingpageundertheURL
http://localhost:8080/webstore/products.
Whatjusthappened?
OurintentionwastoputsomerestrictionsonbindingtheHTTPparameterswiththeform-
backingbean.Aswealreadydiscussed,theautomaticbindingfeatureofSpringcouldlead
toapotentialsecurityvulnerability,incaseweusedthedomainobjectitselfastheform
bean.So,wehavetoexplicitlyspecifytoSpringMVCwhattheonlyallowedfieldsare.
That’swhatwedidinstep1.
The@InitBinderannotationdesignatesacontrollermethodasahookmethodtodosome
customconfigurationregardingdatabindingonWebDataBinder.And,WebDataBinderis
theonethatdoesthedatabindingatruntime,soweneedtospecifytoWebDataBinder
onlythefieldsallowedforbinding.IfyouobserveourinitialiseBindermethodfrom
ProductController,ithasaparametercalledbinder,whichisofthetype
WebDataBinder.WesimplycallthesetAllowedFieldsmethodonthebinderobjectand
passthefields’namesthatareallowedforbinding.SpringMVCcallsthismethodto
initializeWebDataBinderbeforebindingsinceithasthe@InitBinderannotation.
Note
TheWebDataBinderclassalsohasamethodcalledsetDisallowedFieldstostrictly
specifythedisallowedfieldsforbinding.Ifyouusethismethod,SpringMVCallowsany
HTTPrequestparameterstobebound,exceptthatthesefieldnamesarespecifiedinthe
setDisallowedFieldsmethod.
Okay,weconfiguredwhichfieldsareallowedforbinding,butweneedtoverifywhether
anyotherfieldsotherthantheonesallowedareboundwiththeform-backingbean.That’s
whatwedidinsteps2and3.
WechangedprocessAddNewProductFormbyaddingoneextraparametercalledresult,
whichisofthetypeBindingResult.SpringMVCwillfillthisobjectwiththeresultofthe
binding.Ifanyattemptismadetobindanythingotherthantheallowedfields,the
getSuppressedFieldscountoftheBindingResultobjectwillbegreaterthanzero.That’s
why,wecheckedsuppressedfieldcountandthrewRuntimeExceptionasfollows:
if(suppressedFields.length>0){
thrownewRuntimeException("Attemptingtobinddisallowedfields:"+
StringUtils.arrayToCommaDelimitedString(suppressedFields));
}
Wewantedtoensurethatourbindingconfigurationisworking;that’swhy,weranour
applicationwithoutchangingtheviewfileaddProduct.jspinstep4.Asexpected,wegot
theHTTPStatus500errorsayingAttemptingtobinddisallowedfieldswhenwe
submittedtheaddproductformwiththeunitsInOrderanddiscontinuedfieldsfilled.
Werealizedthatourbinderconfigurationisworking,sowechangedourviewfiletonot
bindthedisallowedfields.That’swhatwedidinstep6;wejustremovedtheinputfield
elementsthatarerelatedtothedisallowedfieldsfromtheaddProduct.jspfile.
Afterthis,ourpageforaddingnewproductsworksjustfine,asexpected.Incaseanyof
theoutsideattackerstrytotamperthePOSTrequestandattachaHTTPparameterwiththe
samefieldnameoftheform-backingbean,theywillgetRuntimeException.
Whitelistingisjustanexampleofhowwecancustomizethebindingwiththehelpof
WebDataBinder.However,usingWebDataBinder,wecandoothertypesofbinding
customizationsaswell.Forexample,WebDataBinderinternallyusesmany
PropertyEditor(java.beans.PropertyEditor)implementationstoconvertHTTP
requestparameterstothetargetfieldoftheform-backingbean.Wecanevenregisterthe
customPropertyEditorobjectswithWebDataBindertoconvertmorecomplexdatatypes.
Forinstance,takealookatthefollowingcodesnippetthatshowshowtoregisterthe
customPropertyEditorclasstoconvertaDatetype:
@InitBinder
publicvoidinitialiseBinder(WebDataBinderbinder){
DateFormatdateFormat=newSimpleDateFormat("MMMd,YYYY");
CustomDateEditororderDateEditor=newCustomDateEditor(dateFormat,
true);
binder.registerCustomEditor(Date.class,orderDateEditor);
}
TherearemanyadvancedconfigurationswecandowithWebDataBinderintermsofdata
binding,butforabeginnerlevel,wedon’tneedtogosodeep.
Externalizingtextmessages
Sofar,inallourviewfiles,wehardcodedtextvaluesforallofthelabels.Forinstance,
takeouraddProduct.jspfilefortheproductIDinputtag;wehavealabeltagwitha
hardcodedtextvalueasProductId,asfollows:
<labelclass="control-labelcol-lg-2col-lg-2"for="productId">Product
Id</label>
Externalizingthesetextsfromaviewfileintoapropertiesfilewillhelpushavesingle,
centralizedcontrolofallthelabelmessages,andmoreover,itwillhelpusmakeourweb
pagesreadyforinternationalization.Wewillseemoreaboutinternationalizationin
Chapter6,InterceptYourStorewithInterceptor,butinordertodointernationalization,we
needtoexternalizethelabelmessagesfirst.Sonow,wearegoingtoseeonlyhowto
externalizethelocale-sensitivetextmessagesfromourwebpage.
Timeforaction–externalizingmessages
Let’sseehowtoexternalizethelabeltextsinouraddProduct.jspfile:
1. WeopenouraddProduct.jspfileandaddthefollowingtaglibreferenceatthetop:
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%>
2. ChangetheproductID<label>tag’svalueas<spring:message
code="addProdcut.form.productId.label"/>,asshownasfollows:
<labelclass="control-labelcol-lg-2col-lg-2"for="productId">
<spring:messagecode="addProduct.form.productId.label"/></label>
3. Wecreateafilecalledmessages.propertiesunder/src/main/resourcesinour
projectandaddthefollowinglineinit:
addProduct.form.productId.label=NewProductID
4. Now,weopenourwebapplicationcontextconfigurationfileDispatcherServlet-
context.xmlfromsrc/main/webapp/WEB-INF/spring/webcontext/andaddthe
followingbeandefinitioninit:
<beanid="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
>
<propertyname="basename"value="messages"/>
</bean>
5. Now,werunourapplicationagainandentertheURL
http://localhost:8080/webstore/products/add.Wewillbeabletoseetheadd
productpagewiththeproductIDlabelNewProductID.
Whatjusthappened?
SpringMVChasaspecialatagcalled<spring:message>toexternalizetextsfromJSP
files.Inordertousethistag,weneedtoaddareferencetotheSpringtaglibrary,whichis
whatwedidinstep1.WejustaddedareferencetotheSpringtaglibraryinour
addProduct.jspfileasfollows:
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%>
Instep2,weusedthistagtoexternalizethelabeltextoftheproductIdinputtag,as
follows:
<labelclass="control-labelcol-lg-2col-lg-2"for="productId">
<spring:messagecode="addProduct.form.productId.label"/></label>
Here,animportantthingthatneedstobenotedisthecodeattributeofthe
<spring:message>tagthatwehaveassignedthevalue
addProduct.form.productId.labelasthecodeforthis<spring:message>tag.This
codeattributeisakindofkey,andatruntime,Springwilltrytoreadthecorresponding
valueforthegivenkey(code)fromamessagesourcepropertyfile.
WesaidthatSpringwillreadthemessagevaluefromamessagesourcepropertyfile,so
weneedtocreatethatpropertyfile,whichiswhatwedidinstep3.Wejustcreateda
propertyfilewiththenamemessages.propertiesundertheresourcedirectory.Insidethis
file,weassignedthelabeltextvaluetothemessagetagcodeasfollows:
addProduct.form.productId.label=NewProductID
Notethatfordemonstrationpurposes,Ijustexternalizedasinglelabel,butatypicalweb
applicationwillhaveexternalizedmessagesalmostforallofthelabels.Inthatcase,the
messages.propertiesfilewillhavemanycode-valuepairentries.
Okay,wehavecreatedthemessagesourcepropertyfileandaddedthe<spring:message>
taginourJSPfile.However,toconnectthesetwo,weneedtocreateonemoreSpring
beaninourwebapplicationcontextforthe
org.springframework.context.support.ResourceBundleMessageSourceclasswiththe
namemessageSource.Wedidthisinstep4asfollows:
<beanid="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<propertyname="basename"value="messages"/>
</bean>
Oneimportantpropertythatneedstobenotedhereisthebasenameproperty.Weassigned
thevaluemessagesforthatproperty;ifyouremember,thisisthenameofthepropertyfile
thatwecreatedinstep3.
ThatisallthatwehavedonetoenabletheexternalizationofmessagesinaJSPfile.Now,
ifweruntheapplicationandopenuptheaddproductpage,wecanseethattheproductID
labelhasthesametextasthatassignedtothecodeaddProduct.form.productId.label
inthemessages.propertiesfile.
UsingSpringSecuritytags
Atthestartofthischapter,wesawhowtoserveandprocesswebforms.Inthatexercise,
wecreatedawebpagetoaddproducts.Anyonewithaccesstotheaddproductspagecan
addnewproductstoourwebstore.However,inatypicalwebstore,onlytheadministrator
canaddproducts.So,howdowerestrictotherusersfromaccessingtheaddproducts
page?TherecomesSpringSecuritytohelpus.
SpringSecurityisavasttopic,sowearenotgoingtoseeallofthecapabilitiesofSpring
Security;instead,weareonlygoingtoseehowtoaddbasicauthenticationtoourweb
pages.
Timeforaction–addingaloginpage
WearegoingtouseSpringSecurityfeaturestorestrictaccesstotheaddproductspage.
Onlyanauthorizeduserwithavalidusernameandpasswordwillbeabletoaccesstheadd
productspage.Let’sseehowwecandothisinSpringMVCwiththefollowingsteps:
1. Weopenpom.xml,whichcanbefoundundertheprojectrootfolderitself.
2. Wewillbeabletoseesometabsatthebottom,underthepom.xmlfile;weselectthe
DependenciestabandclickontheAddbuttonoftheDependenciessection.
3. ASelectDependencywindowwillappear;here,weenterGroupIdas
org.springframework.security,ArtifactIdasspring-security-config,Version
as3.1.4.RELEASE,andselectScopeascompileandclickontheOKbutton.
4. Similarly,weaddonemoredependencyGroupIdas
org.springframework.security,ArtifactIdasspring-security-web,Versionas
3.1.4.RELEASE,andselectScopeascompileandclickontheOKbutton.Andmost
importantly,wesavepom.xml.
5. Now,wegototheadjacenttab,whichistheDependencyHierarchytabinpom.xml.
WecanseetheResolvedDependenciessectionontheright,whichlistsallthe
resolveddependencyentries.
6. Wejustright-clickontheentrywiththenamespring-
asm:3.0.7.RELEASE[compile]fromtheResolvedDependencieslistandchoosethe
ExcludeMavenArtifact…optionandclickonOK.Then,wesavepom.xml.
7. Now,wecreateonemorecontrollerclasscalledLoginControllerunderthe
com.packt.webstore.controllerpackageinsrc/main/javaandaddthefollowing
codeintoit:
packagecom.packt.webstore.controller;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.Model;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestMethod;
@Controller
publicclassLoginController{
@RequestMapping(value="/login",method=RequestMethod.GET)
publicStringlogin(){
return"login";
}
@RequestMapping(value="/loginfailed",method=RequestMethod.GET)
publicStringloginerror(Modelmodel){
model.addAttribute("error","true");
return"login";
}
@RequestMapping(value="/logout",method=RequestMethod.GET)
publicStringlogout(Modelmodel){
return"login";
}
}
8. And,weaddonemoreJSPviewfilecalledlogin.jspundersrc/main/webapp/WEB-
INF/views/andaddthefollowingcodesnippetintoitandsaveit:
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglibprefix="form"uri="http://www.springframework.org/tags/form"
%>
<%@taglibprefix="spring"uri="http://www.springframework.org/tags"
%>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-
1">
<link
rel="stylesheet"href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/boo
tstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<divclass="jumbotron">
<divclass="container">
<h1>Products</h1>
<p>Addproducts</p>
</div>
</div>
</section>
<divclass="container">
<divclass="row">
<divclass="col-md-4col-md-offset-4">
<divclass="panelpanel-default">
<divclass="panel-heading">
<h3class="panel-title">Pleasesignin</h3>
</div>
<divclass="panel-body">
<c:iftest="${notemptyerror}">
<divclass="alertalert-danger">
<spring:message
code="AbstractUserDetailsAuthenticationProvider.
badCredentials"/><br/>
</div>
</c:if>
<formaction="<c:urlvalue="/j_spring_security_check">
</c:url>"method="post">
<fieldset>
<divclass="form-group">
<inputclass="form-control"placeholder="UserName"
name='j_username'type="text">
</div>
<divclass="form-group">
<inputclass="form-control"placeholder="Password"
name='j_password'type="password"value="">
</div>
<inputclass="btnbtn-lgbtn-successbtn-block"
type="submit"value="Login">
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
9. Now,weopenouraddProduct.jspfileandaddthefollowingcodetagwithinthe
jumbotrondivtag:
<ahref="<c:urlvalue="/j_spring_security_logout"/>"class="btnbtn-
dangerbtn-minipull-right">logout</a>
10. Then,weopenourmessagesourcefilemessages.propertiesfrom
/src/main/resourcesandaddthefollowinglineinit:
AbstractUserDetailsAuthenticationProvider.badCredentials=Theusername
orpasswordyouenteredisincorrect.
11. Now,wecreateonemorebeanconfigurationfilecalledsecurity-context.xml
undersrc/main/webapp/WEB-INF/spring/webcontextandaddthefollowing
contentintoitandsaveit:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
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-3.2.xsd">
<security:httpauto-config="true">
<security:intercept-urlpattern="/products/add"access="ROLE_ADMIN"
/>
<security:form-loginlogin-page="/login"
default-target-url="/products/add"
authentication-failure-url="/loginfailed"/>
<security:logoutlogout-success-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:username="Admin"password="Admin123"
authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
12. Then,weaddthefollowingtagsinweb.xmlunderthe<web-app>tag:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/webcontext/security-context.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
13. Now,wealsoaddthefollowingtagsinweb.xmlunderthe<web-app>tagandsaveit:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
14. WerunourapplicationandentertheURL
http://localhost:8080/webstore/products/add.Wewillbeabletoseealogin
page,asshowninthefollowingscreenshot:
Theloginpageshowinganerrormessageforinvalidcredentials
15. Now,weenterUserNameasAdminandPasswordasAdmin123andclickonthe
Loginbutton.Finally,wewillbeabletoseetheregularaddproductspagewitha
Logoutbutton.
Whatjusthappened?
Asusual,inordertouseSpringSecurityinourproject,weneedsomeSpringSecurity
relatedjars;fromsteps1to4,wejustaddedthosejarsasMavendependencies.However,
wedidsomethingunusualinsteps5and6.Weexcludedthespring-asmdependency
(spring-asm:3.0.7.RELEASE[compile])fromtheresolveddependencieslist.
FromtheSpring3.2versiononwards,thespring-asmmodulehadalreadybeenincluded
inthespring-coremodule,sothereisnoneedtohavespring-asmasaseparate
transitivedependency.Ifyouhaveskippedsteps5and6,youwillget
java.lang.IncompatibleClassChangeErrorwhenstartinguptheproject.
Instep7,wecreatedonemorecontroller(LoginController)tohandleallourlogin-
relatedwebrequeststhatcontainsimplythreerequestmappingmethodscorrespondingly
tohandlelogin,loginfailure,andlogoutrequests.Allthreemethodsreturnthesameview
name,outofwhichtheloginerrormethodsetsamodelvariableerrortotrueinthe
model.
Sinceinstep7,alltherequestmappingmethodsreturntheviewnamelogin,weneedto
createaviewfilelogin.jsp,whichiswhatwedidinstep8.
Thelogin.jspfilecontainsmanytagswithaBootstrap-styleclassappliedtoenhancethe
lookandfeeloftheloginform;wedon’tneedtoconcentrateonthesetags.However,
therearesomeimportanttagsouttherethatcanbeusedtounderstandtheflow;thefirst
oneisthe<c:if>tag,asshowninthefollowingcode:
<c:iftest="${notemptyerror}">
<divclass="alertalert-danger">
<spring:message
code="AbstractUserDetailsAuthenticationProvider.badCredentials"/>
</div>
</c:if>
The<c:if>tagisaspecialJSTLtagusedtocheckacondition;itismorelikeanif
conditionthatweuseinourprogramminglanguage.Usingthis<c:if>tag,wesimply
checkwhetherthemodelvariableerrorcontainsanyvalue.Ifthemodelvariableerroris
notempty,wesimplyshowanerrormessagewithinthedivtagusingthe
<spring:message>tag.
RememberthatfromtheExternalizingtextmessagesexercise,wealreadylearned
howtoexternalizemessages.Inthisrecipe,wesimplyusedthepredefinederrorkey,
AbstractUserDetailsAuthenticationProvider.badCredentials,ofSpringSecurityas
themessagekey.Sincewedidthis,wejustoverrodethedefaulterrormessageinstep10.
Okay,comingbacktostep8,whataretheotherimportanttagsinthelogin.jspfile?The
nextimportanttagistheformtag,whichrepresentstheloginform.Notetheaction
attributeoftheformtagshowninthefollowingcode:
<formaction="<c:urlvalue="/j_spring_security_check"></c:url>"
method="post">
Wesimplypostourloginformvalues,suchasusernameandpassword,totheSpring
SecurityauthenticationhandlerURL,whichis/j_spring_security_check.Here,the
special<c:url>JSTLtagisusedtoformattheURL.
WhilepostingtheusernameandpasswordtotheSpringSecurityauthenticationhandler,
Springexpectsthesevaluestobeboundunderthevariablenamesj_usernameand
j_passwordcorrespondingly.That’swhy,ifyounoticetheinputtagfortheusernameand
password,itcarriesthenameattributesasj_usernameandj_password,asfollows:
<inputclass="form-control"placeholder="UserName"name='j_username'
type="text">
<inputclass="form-control"placeholder="Password"name='j_password'
type="password"value="">
Similarly,Springhandlesthelogoutoperationunderthej_spring_security_logout
URL;that’swhy,instep9,weformedthelogoutlinkontheaddproductspageasfollows:
<ahref="<c:urlvalue="/j_spring_security_logout"/>"class="btnbtn-danger
btn-minipull-right">logout</a>
WearealmostdonewiththecodingtoincorporateSpringSecurityintoourproject,but
stillweneedtodosomemoreconfigurationtogetitupandrunningwiththebasic
authenticationfortheaddproductspage.Forthefirstconfiguration,weneedtodefineour
authenticationmanagerandspecifytheauthenticatedusersandrolestoSpringSecurity.
Wedidthiswiththehelpofasecuritycontextfile.
Asecuritycontextfileismoresimilartoawebapplicationcontextconfigurationfile.
Basedontheconfigurationandbeandefinitionfoundinthisfile,Springcreatesand
managesthenecessarybeansrelatedtoSpringSecurity.Wecreatedsuchasecurity
contextfileinstep11.Thefirstconfigurationtagthatisfoundinthissecuritycontextfile
(security-context.xml)is<security:http>,asfollows:
<security:httpauto-config="true">
<security:intercept-urlpattern="/products/add"access="ROLE_ADMIN"/>
<security:form-loginlogin-page="/login"
default-target-url="/products/add"
authentication-failure-url="/loginfailed"/>
<security:logoutlogout-success-url="/logout"/>
</security:http>
The<security:http>tagcontainsalotofinformation,andwewillseethemonebyone.
Thefirstconfigurationwithinthe<security:http>tagisasfollows:
<security:intercept-urlpattern="/products/add"access="ROLE_ADMIN"/>
ThisinstructsSpringtointercepteverywebrequestthatisreceivedbytherequestpath
/products/addandonlyallowsaccesstowhicheveruserhastheroleofROLE_ADMIN.If
yourecall,/products/addisnothingbuttherequestpathforouraddproductspage.
Thenextconfigurationwithinthe<security:http>tagisasfollows:
<security:form-loginlogin-page="/login"
default-target-url="/products/add"
authentication-failure-url="/loginfailed"/>
Here,thelogin-pageattributedenotestheURLthatitshouldforwardtherequestto,to
gettheloginform;rememberthatthisrequestpathshouldbethesameastherequest
mappingofthelogin()methodofLoginController.Also,default-target-urldenotes
thedefaultlandingpageafterasuccessfullogin,andthefinalattributeauthentication-
failure-urlindicatestheURLthattherequestneedstobeforwardedtointhecaseofa
loginfailure.
Thefinalconfiguration,<security:logoutlogout-success-url="/logout"/>,denotes
wheretherequestneedstobeforwardedafteralogout.Rememberthatthisalsocarriesthe
samerequestmappingvalue,whichisthevalueofthelogoutmethod,fromthe
LoginControllerclass.
Thenextconfigurationtaginthesecuritycontextfileisthe<security:authentication-
manager>tag;refertothefollowingcode:
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:username="Admin"password="Admin123"
authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
Theimportantinformationconfiguredundertheauthenticationmanageriswhotheusers
are,whattheircorrespondingpasswordis,andwhichrolestheyhave,asfollows:
<security:username="Admin"password="Admin123"authorities="ROLE_ADMIN"/>
TheprecedingpieceofconfigurationsaysthatitisauserwiththenameAdminandhasa
passwordAdmin123andaroleROLE_ADMIN.Wecanaddasmanyrolesaswewantby
separatingthemwithacomma.
Okay,wedefinedthesecurity-relatedconfigurationinthesecuritycontextfile,butSpring
shouldknowaboutthisfileandhavetoreadthisfilebeforebootingtheapplication.Then
onlywillitbeabletocreateandmanagethesecurity-relatedbeans.Howdoweinstruct
Springtopickupthisfile?Theansweristhesame:thecontextConfigLocationlocation
propertythatwehaveusedtolocatethewebapplicationcontextconfigurationfile.
However,thistime,weloadedthesecuritycontextfilethroughthe
ContextLoaderListenerclassandnotthroughthedispatcherservlet.That’swhy,we
initiatedContextLoaderListenerinweb.xmlandgavecontextConfigLocationviathe
<context-param>taginstep12,asfollows:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webcontext/security-context.xml</param-
value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Basedontheprecedingconfiguration,theContextLoaderListenerclasswillloadour
securitycontextfile(/WEB-INF/spring/webcontext/security-context.xml)intothe
SpringruntimesothatSpringcancreatethenecessarybeanswhilebootingthe
application.
Asafinalstep,weneedtoconfiguretheSpringSecurityfilterinourweb.xmlfilesothat
everywebrequestcanbeexaminedforuserauthentication.Thisiswhatweconfiguredin
step13,asfollows:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Afterfinishingallthesteps,ifweaccesstheURL
http://localhost:8080/WebStore/products/add,SpringMVCwillpromptusto
providetheusernameandpassword.Sincewehaveconfiguredanadminuserinstep11
withUserNameasAdminandPasswordasAdmin123,wehavetoprovidethese
credentialstoproceedtotheaddproductspage.
Summary
Atthestartofthischapter,wesawhowtoserveandprocessforms;welearnedhowto
bindformdatawithaform-backingbeanandreadthatbeaninthecontroller.Afterthat,
wewentalittledeeperintoformbeanbindingandconfiguredthebinderinourcontroller
towhitelistsomeofthePOSTparametersfrombeingboundtotheformbean.Wesaw
howtouseonemorespecialtag,<spring:message>,ofSpringtoexternalizemessagesin
aJSPfile.Finallywealsosawhowtoincorporatespringsecuritytodobasic
authenticationtoaccessproductaddpage.
Inthenextchapter,wewilllearnmoreaboutviewandviewresolvers.
Chapter5.WorkingwithViewResolver
Inthepreviouschapter,welearnedhowwecanusesomeoftheSpringtagsthatcanonly
beusedinJSPandJSTLviews.However,Springhasexcellentsupportforotherview
technologiesaswell,suchastheXMLview,JSONview,andsoon.SpringMVCmaintains
ahighlevelofdecouplingbetweentheviewandcontroller.Thecontrollerknowsnothing
aboutviewexcepttheviewname.Itistheresponsibilityoftheviewresolvertomapthe
correctviewforthegivenviewname.
Inthischapter,wewilltakeadeeperlookintoviewsandviewresolvers.Afterfinishing
thischapter,youwillhaveaclearideaaboutthefollowingtopics:
Viewsandresolvingviews
Staticviews
Themultipartviewresolver
Contentnegotiation
Thehandlerexceptionresolver
Resolvingviews
Aswealreadymentioned,SpringMVCdoesnotmakeanyassumptionaboutanyspecific
viewtechnology.AccordingtoSpringMVC,aviewisidentifiableasanimplementation
oftheorg.springframework.web.servlet.Viewinterface,shownasfollows:
publicinterfaceView{
StringgetContentType();
voidrender(Map<String,?>model,HttpServletRequestrequest,
HttpServletResponseresponse)throwsException;
}
TherendermethodfromtheSpringMVCViewinterfacedefinesthemainresponsibility
ofaviewobject.Theresponsibilityisthatitshouldrenderofpropercontentasaresponse
(javax.servlet.http.HttpServletResponse)basedonModelandrequest
(javax.servlet.http.HttpServletRequest).
BecauseofthesimplicityofSpringMVC’sViewinterface,wecanwriteourownview
implementationifwewant.However,SpringMVCprovidesmanyconvenientview
implementationsthatarereadyforusebysimplyconfiguringitinourwebapplication’s
contextconfigurationfile.
OnesuchviewisInternalResourceView
(org.springframework.web.servlet.view.InternalResourceView),whichrendersthe
responseasaJSPpage.Similarly,thereareotherviewimplementationssuchas
RedirectView,TilesView,FreeMarkerView,andVelocityView,whichareavailablefor
specificviewtechnologies.SpringMVCdoesnotencouragecouplingtheviewobject
withthecontrollerasitwillleadthecontrollermethodtotightlycouplewithonespecific
viewtechnology.However,ifyouwanttodoso,youcandosomethingsimilartowhatis
showninthefollowingcodesnippet:
@RequestMapping("/home")
publicModelAndViewgreeting(Map<String,Object>model){
model.put("greeting","WelcometoWebStore!");
model.put("tagline","Theoneandonlyamazingwebstore");
Viewview=newInternalResourceView("/WEB-INF/views/welcome.jsp");
returnnewModelAndView(view,model);
}
Intheprecedingcodehandlermethod,wehaven’treturnedanylogicalviewname;rather,
wedirectlyinstantiatedInternalResourceViewoutofwelcome.jspandcomposeditinto
theModelAndView(org.springframework.web.servlet.ModelAndView)object.The
precedingexampleisnotencouragedsinceithastightlycoupledthegreetinghandler
methodwithInternalResourceView.Instead,whatwecandoisreturnalogicalview
nameandconfigureanappropriateviewresolverofourchoiceinourwebapplication’s
contexttocreateaviewobject.
Springcomeswithquiteafewviewresolverstoresolvevarioustypesofviews.We
alreadylearnedhowwecanconfigureInternalResourceViewResolverasourview
resolvertoresolveJSPviewsinChapter2,SpringMVCArchitecture–ArchitectingYour
WebStore,andwealsolearnedhowInternalResourceViewResolverresolvesa
particularlogicalviewnameintoaview(seetheViewresolverssection,inChapter2,
SpringMVCArchitecture–ArchitectingYourWebStore).Anyhow,Iwillrepeatitbriefly
here.
InternalResourceViewResolverwillresolvetheactualview’sfilepathbyprepending
theconfiguredprefixvalueandappendingthesuffixvaluewiththelogicalviewname;the
logicalviewnameisthevalueusuallyreturnedbythecontrollermethod.So,the
controllermethoddidn’treturnanyactualview;itjustreturnedtheviewname.Itisthe
jobofInternalResourceViewResolvertoformthecorrectURLpathofactualJSPview
fileforInternalResourceView.
Theredirectview
Inawebapplication,URLredirectionorforwardingarethetechniquestomovevisitorsto
adifferentwebpagethantheonetheyrequest.Mostofthetime,thistechniqueisused
aftersubmittingawebformtoavoidresubmissionofthesameformduetotheeventof
pressingthebrowser’sbackbuttonorrefreshbutton.SpringMVChasaspecialView
objectcalledRedirectviewtohandleredirectionandforwarding.TouseRedirectview
(org.springframework.web.servlet.view.Redirectview)withourcontroller,we
simplyneedtoreturnthetargetURLstringwiththeredirectionprefixfromthecontroller.
TherearetworedirectionprefixesavailableinSpringMVC,asshowninthefollowing
codesnippet:
returnredirect:/products/productDetail
And:
returnforward:/products/productDetail
Timeforaction–examiningRedirectView
Thoughbothredirectionandforwardingareusedtopresentadifferentwebpagethanthe
onerequested,thereisalittledifferencebetweenthem.Let’strytounderstandtheseby
examiningthem:
1. OpenourHomeControllerclassandaddonemorerequestmappingmethodas
follows:
@RequestMapping("/welcome/greeting")
publicStringgreeting(){
return"welcome";
}
2. Now,alterthereturnstatementoftheexistingwelcomerequestmappingmethod,
andsaveitasfollows:
return"forward:/welcome/greeting";
3. Now,runourapplicationandenterhttp://localhost:8080/webstore/.Youwillbe
abletoseeawelcomemessageonthewebpage.
4. Now,alterthereturnstatementoftheexistingwelcomerequestmappingmethod
againandsaveitasfollows:
return"redirect:/welcome/greeting";
5. Now,runourapplicationandenterhttp://localhost:8080/webstore/.Youwill
seeablankpagewithoutanywelcomemessage.
6. Finally,revertthereturnvalueofthewelcomemethodofHomeControllertothe
originalvalue,shownasfollows:
return"welcome";
Whatjusthappened?
So,whatwehavedemonstratedhereishowwecaninvoketheredirectviewfromthe
controllermethod.Instep1,wesimplycreatedarequestmappingmethodcalled
greetingforthewelcome/greetingrequestpath.Thismethodsimplyreturnsalogical
viewnameaswelcome.
Sincewereturnedthelogicalviewnameaswelcome,thewelcome.jspfilewillbe
renderedbyInternalResourceViewatruntime.Thewelcome.jspfileexpectstwomodel
attributes,namelygreetingandtagline,duringrendering.Instep2,wealteredthe
returnstatementoftheexitingrequestmappingmethodtoreturnaredirectedURL,as
follows:
@RequestMapping("/")
publicStringwelcome(Modelmodel){
model.addAttribute("greeting","WelcometoWebStore!");
model.addAttribute("tagline","Theoneandonlyamazingwebstore");
return"forward:/welcome/greeting";
}
Whatwehavedoneinstep2ismoreimportant;insteadofreturningalogicalviewname,
wesimplyreturntherequestpathvalueofthegreetinghandlermethodwiththe
forward:keywordprefixed.
ThemomentSpringMVCseesthis,itcanunderstandthatitisnotaregularlogicalview
name,soitwon’tsearchforanyviewfileunderthesrc/main/webapp/WEB-INF/views/
directory;rather,itwillconsiderthisrequestforittobeforwardedtoanotherrequest
mappingmethodbasedontherequestpathattachedaftertheforward:keyword.
Oneimportantthingtorememberhereisthattheforwardedrequestisstilltheactive
originalrequest,sowhatevervaluewehaveputinthemodelatthestartoftherequest
wouldstillbeavailable.ThisiswhywedidnotaddanyvaluetoModelinsidethe
greetingmethod.Wesimplyreturntheviewnameaswelcomeandthewelcome.jspfile
ontheassumptionthattherewillbemodelattributes,namelygreetingandtagline,
availableinthemodel.So,whenwefinallyrunourapplication,asmentionedinstep3,
eventhoughweissuedtherequesttotheURLhttp://localhost:8080/webstore/,the
RedirectViewwillforwardourrequestto
http://localhost:8080/webstore/welcome/greeting,andwewillabletoseethe
welcomemessageonthewebpage.
Againinstep4,wesimplychangedthereturnstatementofthewelcomemethodwiththe
redirect:prefix.Thistime,Springwillconsiderthisrequestasanewrequest,so
whatevervaluewehaveputinthemodel(insidethewelcomemethod)atthestartofthe
originalrequestwouldhavebeengone.Thisiswhyyousawanemptywelcomepagein
step6,sincethewelcome.jsppagecan’treadthegreetingandtaglinemodelattributes
fromthemodel.
So,basedonthisexercise,weunderstandthatRedirectViewwillgetintothepictureifwe
returnaredirectedURLwiththeappropriateprefixfromthecontrollermethod.
RedirectViewwillkeeptheoriginalrequestorspawnanewrequestbasedonredirection
orforwarding.
Popquiz–redirectview
Considerthefollowingcustomercontroller:
@Controller("/customers")
publicclassCustomerController{
@RequestMapping("/list")
publicStringlist(Modelmodel){
return"customers";
}
@RequestMapping("/process")
publicStringprocess(Modelmodel){
//return
}
}
Q1.IfIwanttogetredirectedtothelistmethodfromprocess,howshouldIformthe
returnstatementwithintheprocessmethod?
1. return"redirect:list";.
2. return"redirect:/list";.
3. return"redirect:customers/list";.
4. return"redirect:/customers/list";.
Servingstaticresources
Sofar,wehaveseenthateveryrequestgoesthroughthecontrollerandreturnsa
correspondingviewfilefortherequest;mostofthetime,theseviewfilescontaindynamic
content.Bydynamiccontent,Imeanthemodelvaluesthataredynamicallypopulatedin
theviewfileduringtherequestprocessing.Forexample,iftheviewfileisoftheJSPtype,
thenwepopulatemodelvaluesintheJSPfileusingtheJSPexpressionnotation,${}.
However,whatifwehavesomestaticcontentthatwewanttoservetotheclient?For
example,consideranimagethatisstaticcontent;wedon’twanttogothroughcontrollers
inordertoserve(fetch)animageasthereisnothingtoprocessorupdateanyvaluesinthe
model.Wesimplyneedtoreturntherequestedimage.
Let’ssaywehaveadirectory(/resources/images/)thatcontainssomeproductimages,
andwewanttoservetheseimagesuponrequest.Forexample,iftherequestedURLis
http://localhost:8080/webstore/resource/images/P1234.png,thenwewouldliketo
servetheimagewiththeP1234.pngname.Similarly,iftherequestedURLis
http://localhost:8080/webstore/resource/images/P1236.png,thenanimagewith
thenameP1236.pngneedstobeserved.
Timeforaction–servingstaticresources
Let’sseehowwecanservestaticimageswithSpringMVC:
1. Placesomeimagesunderthesrc/main/webapp/resources/images/directory;I
haveusedthreeproductimages,namelyP1234.png,P1235.png,andP1236.png.
2. Addthefollowingtaginourwebapplicationcontext’sconfiguration
DispatcherServlet-context.xmlfile:
<mvc:resourceslocation="/resources/"mapping="/resource/**"/>
3. Now,runourapplicationandenter
http://localhost:8080/webstore/resource/images/P1234.png(changethe
imagenameintheURLbasedontheimagesyouplacedinstep1).
4. Youarenowabletoviewtheimageyourequestedinthebrowser.
Whatjusthappened?
Whatjusthappenedwassimple;instep1,weplacedsomeimagefilesunderthe
src/main/webapp/resources/images/directory.Instep2,wejustaddedthe
<mvc:resources>taginthewebapplicationcontextconfigurationtotellSpringwhere
theseimagefilesarelocatedinourprojectsothatspringcanservethosefilesupon
request.Considerthefollowingcodesnippet:
<mvc:resourceslocation="/resources/"mapping="/resource/**"/>
Thelocationattributeofthe<mvc:resources>tagdefinesthebasedirectorylocationof
staticresourcesthatyouwanttoserve.Inourcase,wewanttoserveallimagesthatare
availableunderthesrc/main/webapp/resources/images/directory;youmaywonder
whywehavegivenonly/resources/asthelocationvalueinsteadof
src/main/webapp/resources/images/.Thisisbecauseweconsidertheresources
directoryasthebasedirectoryforallresources,wecanhavemultiplesubdirectoriesunder
resourcesdirectorytoputourimagesandotherstaticresourcefiles
Thesecondattribute,mapping,justindicatestherequestpaththatneedstobemappedto
thisresourcedirectory.Inourcase,wehaveassigned/resources/**asthemapping
value.So,ifanywebrequeststartswiththe/resourcerequestpath,thenitwillbe
mappedtotheresourcesdirectory,andthe/**symbolindicatestherecursivelookfor
anyresourcefilesunderneaththebaseresourcedirectory.
Thisiswhy,ifyounoticeinstep3,weformedtheURLas
http://localhost:8080/webstore/resource/images/P1234.png.So,whileservingthis
webrequest,SpringMVCwillconsider/resource/images/P1234.pngastherequest
path.So,itwilltrytomap/resourcetotheresourcebasedirectory,resources.Fromthis
directory,itwilltrytolookfortheremainingpathoftheURL,whichis
/images/P1234.png.Sincewehavetheimagesdirectoryundertheresourcesdirectory,
Springcaneasilylocatetheimagefilefromtheimagesdirectory.
Asamatteroffact,behindthescreen,SpringMVCuses
org.springframework.web.servlet.resource.ResourceHttpRequestHandlertoserve
theresourcesthatareconfiguredbythe<mvc:resources>tag.So,inourapplication,if
anyrequestcomeswiththerequestpath’s/resourceprefixinitsURL,thenSpringwill
lookintothelocationdirectorythatisconfiguredinthe<mvc:resources>tagandwill
returntherequestedfiletothebrowser.Remember,Springallowsyoutohostnotonly
images,butalsoanytypeofstaticfiles,suchasPDFs,Worddocuments,Excelsheets,and
sooninthisfashion.
Itisgoodthatweareabletoserveproductimageswithoutaddinganyextrarequest
mappingmethodsinthecontroller.
Popquiz–staticview
Considerthefollowingresourceconfiguration:
<mvc:resourceslocation="/pdf/"mapping="/resources/**"/>
Q1.Underthepdfdirectory,ifIhaveasubdirectorysuchasproduct/manuals/,which
containsa.pdffilecalledmanual-P1234.pdf,howcanIformtherequestpathtoaccess
that.pdffile?
1. /pdf/product/manuals/manual-P1234.pdf.
2. /resources/pdf/product/manuals/manual-P1234.pdf.
3. /product/manuals/manual-P1234.pdf.
4. /resource/pdf/product/manuals/manual-P1234.pdf.
Timeforaction–addingimagestothe
productdetailpage
Let’sextendthistechniquetoshowproductimagesinourproductlistingpageandinthe
productdetailpage.Performthefollowingsteps:
1. Openproducts.jsp;youcanfindproducts.jspunderthe/src/main/webapp/WEB-
INF/views/directoryinyourproject.Now,addthefollowing<img>tagafterthe
<divclass="thumbnail">tag:
<imgsrc="<c:urlvalue="/resource/images/${product.productId}.png">
</c:url>"alt="image"style="width:100%"/>
2. Similarly,openproduct.jspandaddthefollowing<img>tagafterthe<div
class="row">tag:
<divclass="col-md-5">
<imgsrc="<c:urlvalue="/resource/images/${product.productId}.png">
</c:url>"alt="image"style="width:100%"/>
</div>
3. Now,runourapplicationandenterhttp://localhost:8080/webstore/products.
Youwillbeabletoseetheproductlistpagewitheveryproductthathasaproduct
image,asshowninthefollowingfigure:
Productlistingswiththeimageattached
4. Now,clickontheDetailsbuttonofanyproduct,andyouwillbeabletoseethe
correspondingviewoftheproductdetailswiththeimageattachedtothedetailspage,
asfollows:
Theproductdetailpagewiththeimageattached
Whatjusthappened?
Whatwehavedoneissimple.Welearnedhowwecanservestaticresourcesandhowwe
canhostproductimages.Duringthisexercise,welearnedthatinourapplication,ifany
requestcomeswiththerequestpath’s/resourceprefix,itwouldgetmappedtothebase
resourcedirectory,andanyremainingURLpathwouldleadtothestaticfile.
Weleveragedthisfact,andformedtheimage’ssrcURLaccordingly;noticethesrc
attributeofthe<img>tagweaddedinstep1:
<imgsrc="<c:urlvalue="/resource/images/${product.productId}.png">
</c:url>"alt="image"style="width:100%"/>
Thesrcattributevaluethatweareforminginthepreceding<img>taghasanexpression
languagenotationtofetchtheproductID;aftergettingtheproductID,wesimply
concatenateittotheexistingvaluetoformavalidrequestpath,shownasfollows:
/resource/images/${product.productId}.png
Forexample,iftheproductIDisP1234,thenwewouldgetanimagerequestURLas
/resource/images/P1234.png,whichisnothingbutoneoftheimagefilenamesthatwe
havealreadyputupinthe/resources/imagesdirectory.So,Springcaneasilyreturnthe
imagefilethatweshowedusingthe<img>taginsteps1and2.
Themultipartrequestinaction
Intheprecedingexercise,welearnedhowwecanincorporatethestaticviewtoshow
productimagesintheproducts’detailspage.Wesimplyputsomeimagesinadirectoryin
theserverandperformedaconfiguration,andSpringMVCwasabletopickupthesefiles
duringtherenderingofthepagethathadtheproductdetails.Whatifweautomatethis
process?Imeaninsteadofputtingtheseimages,whatifweareabletouploadimagesto
theimagedirectory?
Howcanwedothis?Therecomesthemultipartrequest.Themultipartrequestisatypeof
HTTPrequestthatsendsthefileanddatatotheserver.SpringMVChasgoodsupportfor
amultipartrequest.Let’ssaywewanttouploadsomefilestotheserver.Toaccomplish
this,wewillhavetoformamultipartrequest.
Timeforaction–addingimagestothe
productpage
Let’saddtheimageuploadfacilitytoouraddproductspage:
1. Addabeandefinitioninourwebapplication’scontextconfigurationfile
(DispatcherServlet-context.xml)forCommonsMultipartResolver,asfollows:
<beanid="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolv
er">
<propertyname="maxUploadSize"value="10240000"/>
</bean>
2. Openpom.xml;youcanfindpom.xmlundertheprojectrootdirectoryitself.
3. Youwillbeabletoseesometabsatthebottomofthepom.xmlfile.Selectthe
DependenciestabandclickontheAddbuttonoftheDependenciessection.
4. ASelectDependencywindowwillappear;enterGroupIdascommons-fileupload,
ArtifactIdascommons-fileupload,Versionas1.2.2;selectScopeascompile;and
clickontheOKbutton.
5. Similarly,addonemoreGroupIddependencyasorg.apache.commons,ArtifactId
ascommons-io,Versionas1.3.2;selectScopeascompile;clickontheOKbutton;
andsavethepom.xmlfile.
6. Openourproduct’sdomainclass(Product.java)andaddareferenceto
org.springframework.web.multipart.MultipartFilewiththecorresponding
settersandgettersasfollows:
privateMultipartFileproductImage;
7. OpenaddProduct.jsp;youcanfindaddProduct.jspunderthe
/src/main/webapp/WEB-INF/views/directoryinyourproject.Addthefollowingset
oftagsafterthe<form:inputid="condition">taggroup:
<divclass="form-group">
<labelclass="control-labelcol-lg-2"for="productImage">
<spring:messagecode="addProdcut.form.productImage.label"/></label>
<divclass="col-lg-10">
<form:inputid="productImage"path="productImage"type="file"
class="form:input-large"/>
</div>
</div>
8. Addanentryinourmessagebundlesource(messages.properties)fortheproduct’s
imagelabel,asfollows:
addProdcut.form.productImage.label=ProductImagefile
9. Now,settheenctypeattributetomultipart/form-dataintheformtagasfollows
andsaveaddProduct.jsp:
<form:formmodelAttribute="newProduct"class="form-
horizontal"enctype="multipart/form-data">
10. OpenourProductController.javafileandmodifytheprocessAddNewProductForm
method’ssignaturebyaddinganextramethodparameteroftheHttpServletRequest
type(javax.servlet.http.HttpServletRequest);sobasically,your
processAddNewProductFormmethodsignatureshouldlooklikethefollowingcode
snippet:
publicStringprocessAddNewProductForm(@ModelAttribute("newProduct")
ProductnewProduct,BindingResultresult,HttpServletRequestrequest){
11. AddthefollowingcodesnippetinsidetheprocessAddNewProductFormmethodjust
beforeproductService.addProduct(newProduct);:
MultipartFileproductImage=productToBeAdded.getProductImage();
StringrootDirectory
=request.getSession().getServletContext().getRealPath("/");
if(productImage!=null&&!productImage.isEmpty()){
try{
productImage.transferTo(newFile(rootDirectory+"resources\\images\\"+pro
ductToBeAdded.getProductId()+".png"));
}catch(Exceptione){
thrownewRuntimeException("ProductImagesavingfailed",e);
}
}
12. WithintheinitialiseBindermethod,addtheproductImagefieldtothewhitelisting
setasfollows:
binder.setAllowedFields("productId","name","unitPrice","description","m
anufacturer","category","unitsInStock","productImage");
13. Now,runourapplicationandentertheURL
http://localhost:8080/webstore/products/add.Youwillbeabletoseeouradd
productspagewithanextrainputfieldtochooseafiletoupload.Justfillevery
informationasusualandmoreimportantly,chooseanimagefileofyourchoiceto
addanewimagefile,andclickontheAddbutton.Youwillthenbeabletoseethat
theimagehasbeenaddedtotheproductspageandtheproductdetailspage,asshown
inthefollowingscreenshot:
Addproductpagewithimageselectionoption
Whatjusthappened?
Spring’sCommonsMultipartResolver
(org.springframework.web.multipart.commons.CommonsMultipartResolver)class
determineswhetherthegivenrequestcontainsmultipartcontentornotandparsesthe
givenHTTPrequestintomultipartfilesandparameters.Thisiswhyweinitiatedthisclass
withinourservletcontextinstep1.ThroughthemaxUploadSizeproperty,wehaveseta
maximumof10240000bytesastheallowedfilesizetobeuploaded:
<bean
id="multipartResolver"class="org.springframework.web.multipart.commons.Comm
onsMultipartResolver">
<propertyname="maxUploadSize"value="10240000"/>
</bean>
Fromsteps2to5,weaddedsomeoftheorg.apache.commonslibrariesasourmaven
dependency.ThisisbecauseSpringusestheselibrariesinternallytosupportthefile
uploadingfeature.
Sincetheimagethatwewereuploadingbelongstoaproduct,itisbettertokeepthat
imageaspartoftheproductinformation;thisiswhyinstep6,weaddedareferenceto
MultipartFileinourdomainclass(Product.java)andaddedcorrespondingsettersand
getters.ThisMultipartFilereferenceholdstheactualproductimagefilethatwewere
uploading.
Wewanttoincorporatetheimageuploadingfacilityinouraddproductspage;thisiswhy,
intheaddProduct.jspviewfile,weaddedafileinputtagtochoosethedesiredimage,
shownasfollows:
<divclass="form-group">
<labelclass="control-labelcol-lg-2"for="productImage"><spring:message
code="addProdcut.form.productImage.label"/>
</label>
<divclass="col-lg-10">
<form:inputid="productImage"path="productImage"type="file"
class="form:input-large"/>
</div>
</div>
Intheprecedingsetoftag,theimportantoneisthe<form:input>tag.Ithasthetype
attributeasfilesothatitcanhavetheChooseFilebuttontodisplaythefilechooser
window.Asusual,wewantthisformfieldtobeboundwiththedomainobjectfield;thisis
whywehavesetthepathattributeasproductImage.Ifyouremembercorrectly,this
pathnameisnothingbutthesameMultipartFilereferencenamethatweaddedinstep6.
Asusual,wewanttoexternalizethelabelmessageforthisfileinputtagaswell,andthat’s
whyweaddedthe<spring:message>tag,andinstep8,weaddedthecorresponding
messageentryinthemessagesourcefile(messages.properties).
Sinceouraddproductformisnowcapableofsendingimagefilesaswellaspartofthe
request,weneedtoencodetherequestasamultipartrequest.Thisiswhy,instep9,we
addedtheenctypeattributetothe<form:form>tagandsetitsvalueasmultipart/form-
data.Theenctypeattributeindicateshowtheformdatashouldbeencodedwhenweare
submittingittotheserver.
Wewantedtosavetheimagefileintheserverunderthelocation’sresources/images
directory;thisdirectorystructurewouldbeavailabledirectlyundertherootdirectoryof
ourwebapplicationatruntime.So,inordertogettherootdirectoryofourweb
application,weneedHttpServletRequest.Seethefollowingcodesnippet:
StringrootDirectory=
request.getSession().getServletContext().getRealPath("/");
Thisiswhyweaddedanextramethodparametercalledrequestofthe
HttpServletRequesttypetoourprocessAddNewProductFormmethodinstep10.
Remember,SpringwillfillthisrequestparameterwiththeactualHTTPrequest.
Instep11,wesimplyreadtheimagefilefromthedomainobjectandwroteitintoanew
filewiththeproductIDasthename,asshowninthefollowingcodesnippet:
MultipartFileproductImage=productToBeAdded.getProductImage();
StringrootDirectory=
request.getSession().getServletContext().getRealPath("/");
if(productImage!=null&&!productImage.isEmpty()){
try{
productImage.transferTo(new
File(rootDirectory+"resources\\images\\"+
productToBeAdded.getProductId()+".png"));
}catch(Exceptione){
thrownewRuntimeException("ProductImagesavingfailed",e);
}
}
Remember,wepurposelysavetheimageswiththeproductIDnamebecausewehave
alreadydesignedourproducts(products.jsp)pageanddetail(product.jsp)page
accordinglyinordertodisplaytherightimagebasedontheproductID.
Asafinalstep,weaddedthenewlyintroducedproductImagefiletothewhitelistingsetin
thebinderconfigurationwithintheinitialiseBindermethod.
Now,ifyourunourapplicationandenter
http://localhost:8080/webstore/products/add,youwillbeabletoseeouradd
productspagewithanextrainputfieldtochoosethefiletoupload.
Haveagohero–uploadingproductusermanuals
totheserver
Itisnicethatwewereabletouploadtheproductimagetotheserverwhileaddinganew
product.Whydon’tyouextendthisfacilitytouploadaPDFfiletoserver?Forexample,
considerthateveryproducthasausermanualandyouwanttouploadtheseusermanuals
aswellwhileaddingaproduct.
HerearesomeofthethingsyoucandotouploadPDFfiles:
Createadirectorywiththepdfnameunderthesrc/main/webapp/resources/
directoryinyourproject.
AddonemoreMultipartFilereferenceinyourproductdomainclass
(Product.java)toholdthePDFfileandchangeProduct.javaaccordingly.
ExtendaddProduct.jsp.
ExtendProductController.javaaccordingly;don’tforgettoaddthenewlyadded
fieldtothewhitelist.
Sofinally,youwillbeabletoaccessthePDFunder
http://localhost:8080/webstore/resource/pdf/P1237.pdfifthenewlyadded
productidisP1237.Goodluck!
UsingContentNegotiatingViewResolver
Contentnegotiationisamechanismthatmakesitpossibletoserveadifferent
representationofthesameresource.Forexample,sofarwehavedisplayedourproduct
detailpageinaJSPrepresentation.Whatifwewanttorepresentthesamecontentinan
XMLformat,andsimilarly,whatifwewantthesamecontentinaJSONformat?There
comesSpringMVC’sContentNegotiatingViewResolver
(org.springframework.web.servlet.view.ContentNegotiatingViewResolver)tohelp
us.TheXMLandJSONformatsarepopulardatainterchangeformatsthatareusedinweb
servicecommunicationsheavily.So,usingContentNegotiatingViewResolver,wecan
incorporatemanyviewssuchasMappingJacksonJsonView(forJSON)and
MarshallingView(forXML)torepresentthesameproductinformationasthe
XML/JSONformat.
Timeforaction–configuring
ContentNegotiatingViewResolver
ContentNegotiatingViewResolverdoesnotresolveviewsitselfbutdelegatesthemto
otherviewresolversbasedontherequest.Now,let’saddthecontentnegotiationcapability
toourapplication:
1. Openpom.xml;youcanfindpom.xmlundertheprojectrootdirectoryitself.
2. Youwillbeabletoseesometabsatthebottomofpom.xmlfile.Selectthe
DependenciestabandclickontheAddbuttonoftheDependenciessection.
3. ASelectDependencywindowwillappear;enterGroupIdas
org.springframework,ArtifactIdasspring-oxm,Versionas4.0.3.RELEASE;
selectScopeascompile;andthenclickontheOKbutton.
4. Similarly,addonemoredependencyGroupIdasorg.codehaus.jackson,Artifact
Idasjackson-mapper-asl,Versionas1.9.10,andselectScopeascompile.Then,
clickontheOKbuttonandsavepom.xml.
5. AddthebeanconfigurationforContentNegotiatingViewResolverinourweb
application’scontextconfigurationfile,DispatcherServlet-context.xml,as
follows:
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResol
ver">
<propertyname="defaultViews">
<list>
<refbean="jsonView"/>
<refbean="xmlView"/>
</list>
</property>
</bean>
6. Now,addthebeanconfigurationfortheJSONviewasfollows:
<beanid="jsonView"
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView
">
<propertyname="prettyPrint"value="true"/>
</bean>
7. Finally,addthebeanconfigurationfortheXMLviewasfollows:
<beanid="xmlView"
class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<beanclass="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<propertyname="classesToBeBound">
<list>
<value>com.packt.webstore.domain.Product</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
8. Openourproductdomainclass(Product.java),andaddthe@XmlRootElement
annotationatthetopoftheclass.
9. Similarly,addthe@XmlTransientannotationatthetopofthegetProductImage()
methodandaddanother@JsonIgnoreannotationontopoftheproudctImagefield.
10. Now,runourapplicationandenter
http://localhost:8080/webstore/products/product?id=P1234.Youwillnowbe
abletoviewthedetailpageoftheproductwiththeP1234ID.
11. NowchangetheURLwiththe.xmlextension
(http://localhost:8080/webstore/products/product.xml?id=P1234).Youwill
beabletoseethesamecontentintheXMLformat,asshowninthefollowing
screenshot:
TheproductdetailpagethatshowstheproductinformationintheXMLformat
12. Similarly,thistimechangetheURLwiththe.jsonextension
(http://localhost:8080/webstore/products/product.json?id=P1234).Youwill
beabletoseetheJSONrepresentationofthatcontentasshowninthefollowing
screenshot:
TheproductdetailpagethatshowstheproductinformationintheJSONformat
Whatjusthappened?
SincewewantanXMLrepresentationforourmodeldatatoconvertourmodelobjects
intoXML,weneedSpring’sobject/XMLmappingsupport.Thisiswhyweaddedthe
dependencyforspring-oxm.jarinsteps1to3.Thespring-oxmnotationwillhelpus
convertanXMLdocumenttoandfromaJavaobject.
Similarly,toconvertmodelobjectsintoJSON,SpringMVCwillusejackson-mapper-
asl.jar,soweneedthatJARinourprojectaswell.Instep4,wejustaddedthe
dependencyconfigurationforthatjar.
Ifyouremember,wehavealreadydefinedInternalResourceViewResolverinourweb
applicationcontextasourviewresolvertoresolveJSP-basedviews.However,thistime,
wewantaviewresolvertoresolveXMLandJSONviews.Thisiswhy,instep6and7,we
configuredMappingJacksonJsonView(forJSON)andMarshallingView(forXML)inour
webapplicationcontext.
AsIalreadymentioned,ContentNegotiatingViewResolverdoesnotresolveviewsitself.
Instead,itdelegatestootherviewsbasedontherequest,soweneedtointroduceother
viewstoContentNegotiatingViewResolver.Wedidthatinstep5throughthe
defaultViewspropertyinContentNegotiatingViewResolver.Notethatinthe
ContentNegotiatingViewResolverbeanconfiguration,wejustaddedthebeanreference
fortheJSONviewandXMLviewunderthedefaultViewsproperty:
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
>
<propertyname="defaultViews">
<list>
<refbean="jsonView"/>
<refbean="xmlView"/>
</list>
</property>
</bean>
WeconfiguredbeanreferencesforjsonViewandxmlViewinside
ContentNegotiatingViewResolver.
ThexmlViewbeanconfiguration,especially,hasoneimportantpropertycalled
classesToBeBound,whichliststhedomainobjectsthatneedsXMLconversionduringthe
requestprocessing.SinceourproductdomainobjectneedstheXMLconversion,weadded
com.packt.webstore.domain.ProductinthelistofclassesToBeBound,shownas
follows:
<beanid="xmlView"
class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<beanclass="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<propertyname="classesToBeBound">
<list>
<value>
com.packt.webstore.domain.Product
</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
InordertoconverttoXML,weneedtogiveonemorehinttoMarshallingViewto
identifytherootXMLelementintheProductdomainobject.Thisiswhy,instep8,we
annotatedourclasswiththe@XmlRootElementannotation
(javax.xml.bind.annotation.XmlRootElement).
Instep9,weaddedthe@XmlTransientannotation
(javax.xml.bind.annotation.XmlTransient)ontopofthegetProductImage()method
andaddedanotherannotation,@JsonIgnore
(org.codehaus.jackson.annotate.JsonIgnore),ontopoftheproductImagefield.This
isbecausewedon’twanttorepresenttheproductimageaspartoftheXMLvieworJSON
view.Sincebothformatsarepurelytext-basedrepresentation,itisnotpossibleto
representimagesintexts.
Instep10,wesimplyaccessedourproductdetailpageinaregularwaybyfiringtheweb
requesthttp://localhost:8080/webstore/products/product?id=P1234fromthe
browser,andwewillbeabletoseethenormalJSPview,asexpected.
Instep11,wejustchangedtheURLslightlybyaddinga.xmlextensiontothe
http://localhost:8080/webstore/products/product.xml?id=P1234requestpath.
Thistime,wewillbeabletoseethesameproductinformationintheXMLformat.
Similarly,fortheJSONview,wechangedtheextensionbyadding.jsontothe
http://localhost:8080/webstore/products/product.json?id=P1234path,andwe
willbeabletoseetheJSONrepresentationofthesameproductinformation.
Workingwiththehandlerexception
resolver
SpringMVCprovidesseveralapproachestoexceptionhandling.InSpring,oneofthe
mainexceptionhandlingconstructsistheHandlerExceptionResolverinterface
(org.springframework.web.servlet.HandlerExceptionResolver).Anyobjectsthat
implementthisinterfacecanresolveexceptionsthatarethrownduringcontrollermapping
orexecution.TheHandlerExceptionResolverimplementersaretypicallyregisteredas
beansinthewebapplicationcontext.
SpringMVCcreatestwosuchHandlerExceptionResolverimplementationsbydefaultto
facilitateexceptionhandling:
ResponseStatusExceptionResolveriscreatedtosupportthe@ResponseStatus
annotation
ExceptionHandlerExceptionResolveriscreatedtosupportthe@ExceptionHandler
annotation
Timeforaction–addingtheresponse
statusexception
First,wewilllookatthe@ResponseStatusannotation
(org.springframework.web.bind.annotation.ResponseStatus).InChapter3,Control
YourStorewithControllers,wecreatedarequestmappingmethodtodisplayproductsby
categoryundertheURItemplate,
http://localhost:8080/webstore/products/{category}.Ifnoproductswerefound
underthegivencategory,wewouldshowanemptywebpage,whichisnotcorrect
semantically.WeshouldshowanHTTPstatuserrortoindicatethatnoproductsexist
underthegivencategory.Let’sseehowwecandothatwiththehelpofthe
@ResponseStatusannotation:
1. CreateaclasscalledNoProductsFoundUnderCategoryExceptionunderthe
com.packt.webstore.exceptionpackageinthesourcefolder,src/main/java.Now
addthefollowingcodeintoit:
packagecom.packt.webstore.exception;
importorg.springframework.http.HttpStatus;
importorg.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="Noproductsfound
underthiscategory")
publicclassNoProductsFoundUnderCategoryExceptionextends
RuntimeException{
privatestaticfinallongserialVersionUID=3935230281455340039L;
}
2. Now,openourProductControllerclassandmodifythegetProductsByCategory
methodasfollows:
@RequestMapping("/{category}")
publicStringgetProductsByCategory(Model
model,@PathVariable("category")Stringcategory){
List<Product>products
=productService.getProductsByCategory(category);
if(products==null||products.isEmpty()){
thrownewNoProductsFoundUnderCategoryException();
}
model.addAttribute("products",products);
return"products";
}
3. Nowrunourapplicationandenter
http://localhost:8080/webstore/products/HeadPhones.YouwillseeanHTTP
statuserrorthatsaysNoproductsfoundunderthiscategory,shownasfollows:
TheproductcategorypagethatshowsHTTPStatus404for“Noproductsfound
underthiscategory”
Whatjusthappened?
Instep1,wejustcreatedaruntimeexceptioncalled
NoProductsFoundUnderCategoryException