Spring MVC SLIDES - 1
Spring MVC SLIDES - 1
Alef Arendsen
Erwin Vervaet
Overall Presentation Goal
Practical session on
Spring Web MVC and
Spring Web Flow
Speakers
Alef Arendsen
Principal Consultant at Interface21
Spring Core Developer
Author, Professional Development with
Spring
Erwin Vervaet
Consultant at Ervacon
Extensive experience in J2SE and J2EE
Founder of the Spring Web Flow project
Agenda
Spring and Spring MVC
The Sample Application
Data Binding
Spring Web Flow
Redirect-After-Post
File Upload
Spring MVC - Topics
Positioning of Spring MVC in the
application architecture
Spring MVC general concepts
Request-processing flow
Spring MVC Introduction
Request-driven MVC framework
Some similarities with Struts
Tight integration with other parts of
Spring
Basis for other HTTP-based functionality
in Spring
Can be used in combination
with other MVC frameworks
Web MVC layer
(Spring MVC, Spring Web Flow
JSF, Struts, Tapestry, …)
Service objects
The result:
Controller – handling requests,
interacting with the business layer and
preparing the model and the view
View – rendering the response to the
user based on the Model
Model – the contract between the View
and the Controller
Request-driven MVC – Controller
(or: everything you need to know about Spring MVC – 1/5)
request DispatcherServlet
Model Controller
User
response View
Some code - the Controller
}
Some code - the ModelAndView
public class ModelAndView {
...
}
Agenda
Spring and Spring MVC
The Sample Application
Data Binding
Spring Web Flow
Redirect-After-Post
File Upload
Sample: Pimp My Shirt
Compose new shirts
Select long or short sleeve
Enter graphical or text print
Select color
Rate previously created shirts
Delete previously created shirts
DEMO
composeShirt flow
&
RateShirtsController
<<ShirtService>>
<servlet>
<servlet-name>pimpmyshirt</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
Defines name of
<servlet-mapping> ApplicationContext
<servlet-name>pimpmyshirt</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Layout of a typical Spring application
Pimp my Shirt – layout
pimpmyshirt-servlet.xml
ShirtServiceImpl
ShirtServiceImpl
ShirtDaoImpl
applicationContext.xml
Implementing a Controller
Implement Controller interface…
or implement AbstractController…
or implement another controller
MultiActionController
SimpleFormController
…
RateShirtsController
<bean name=“/index.html”
class=“...RateShirtsController”>
<property name=“shirtService”
ref=“shirtService”/>
</bean>
RateShirtsController
In reality uses MultiActionController
Multiple related actions grouped
together in one class
Selection handler method done
by MethodNameResolver
(e.g. using an HTTP parameter value)
Each action method must
return ModelAndView
DEMO
Agenda
Spring and Spring MVC
The Sample Application
Data Binding
Spring Web Flow
Redirect-After-Post
File Upload
Data binding
Binding request parameters to objects
Domain objects
Data transfer objects
…
Primitives as well as complex objects
Uses java.beans.PropertyEditors
<input type=“text”
name=“shirtText”/>
<input type=“text”
name=“date” value=“2005-12-13 14:30”/>
Used in SimpleFormController
formView
submit
errors
Process
submitted data
success
successView
Binding and validation process
1. formBackingObject()
• Create object to which
HTTP parameters will be bound
2. bindAndValidate()
• initBinder()
• Prepare WebDataBinder to convert
complex types using PropertyEditors
• Bind using WebDataBinder
• Validation using Validators
3. processSubmitAction() either one of
• doSubmitAction()
• onSubmit()
• …
PropertyEditors
Convert Strings to complex objects
Convert complex objects to Strings
Standard JavaBeans infrastructure
<input type=“text”
name=“${status.expression}”
value=“${status.value}”/>
<c:if test=“${status.error}”>
<span class=“error”>
${status.errorMessage}
Corresponds
</span>
to shirtText property
</c:if>
in ShirtFbo object
</spring:bind>
Agenda
Spring and Spring MVC
The Sample Application
Data Binding
Spring Web Flow
Redirect-After-Post
File Upload
Spring Web Flow
In a nutshell
An advanced controller for Web MVC
targeting use cases demanding controlled
navigation
Integrates with several frameworks
• Spring Web MVC & Spring Portlet MVC
• Struts
• JSF
•…
Typical Example
A wizard to compose a shirt
Spans several steps
Some steps require user input
Some steps are decision points
Some steps perform logic (calculations, …)
Navigation is controlled
Classic Implementation
What would this look like with Spring MVC?
Use AbstractWizardFormController …
Manage session scoped form backing
object to hold wizard data
Controller (and View) for each step
Expose each Controller at a request URL
Views submit to particular to particular
URL:
a controller handling one step in the flow
Result
Store form
backing
/page1.html
object
view1
Controller 1 HTTP
Session
/step2.html
Update
form
view2
backing
Controller 2
object
/step3.html Business
processTx Service
view3
Controller 3
Problems with this approach
Request centric
No concept of a conversation or flow
The flow is chopped up in individual
controllers
Brittle dependency on request URLs
Manual state management
Loosely controller navigation
…
Consequences
Custom code to handle problems
Hard to maintain
Hard to reuse
A key abstraction is missing: the flow!
A web conversation: longer than a single
request but shorter than a user’s session
Refactored with Spring Web Flow
view1
store
next Flow
view2 Execution
Storage
load
submit
view3
“Compose Shirt”
Flow
Spring Web Flow – Concepts (1)
Setup
1: Enter Basic Graphical
Reference
Shirt Params Print?
Data
no yes
3: Shirt
End Create Shirt
Preview
An Action State
<action-state id="referenceData">
<action bean="composeShirtAction"/>
<transition on="success" to="step1"/>
</action-state>
A Decision State
<decision-state id="printSwitch">
<if test="${flowScope.shirtFbo.graphical}"
then="step2Graphical"
else="step2Text"/>
</decision-state>
Flow Definition XML (2)
Flow Scope
Request Scope
Setup
1: Enter Basic
Reference End
Shirt Params
Data
Deploying the flow
Add a FlowController to your application
<bean id="flowController"
name="/flow.html"
class="org.springframework.webflow.mvc.FlowController"
/>
Participating in a flow
<a href=“<c:url value=“flow.html?_eventId=cancel
&_flowExecutionId=${flowExecutionId}”/>”>Cancel</a>
Don’t overuse!
Targeted at controlled navigations!
Side by side with Spring MVC controllers
Be careful with browser back button
Consider continuations
Guard against double submit
(with back button)
Consider TransactionSynchronizer
Advanced data binding
WebDataBindernot usable in all situations
Manual data binding still possible
Using HttpServletRequest
Using convenient RequestUtils and
WebUtils
If things became nasty,
just use manual data binding
Agenda
Spring and Spring MVC
The Sample Application
Data Binding
Spring Web Flow
Redirect-After-Post
File Upload
Redirect-After-Post
Most important HTTP request methods
POST: submit user input to server
GET: retrieve data from server
(idempotent)
Really Redirect-After-Submit
Sometimes GET has side effects;
e.g. because it’s used to submit data
Best practice: only use HTTP POST for
submits
Double submit problem
What if the server returns data (a page)
in response to a submit (POST) request?
Submit Data
Server
Submit Data
Browser Server
Get Result
Result Page
Be careful with caching
Don’t cache submit result pages
<bean id="flowController" name="/flow.html"
class="org.springframework.webflow.mvc.FlowController">
<property name="cacheSeconds" value="0"/>
</bean>
Redirecting in Spring MVC, SWF
Directly in controller
if (request instanceof MultipartHttpServletRequest) {
MultipartFile print = (Multipartfile)
((MultipartHttpServletRequest)request)
.getFile(“shirt.print”);
...
}
Using ByteArrayMultipartFileEditor
binder.registerCustomEditor(byte[].class,
new ByteArrayMultipartFileEditor());
Handling Uploaded Data (2)
Using a custom PropertyEditor
Bind multiple properties of the uploaded file to a
domain layer object
binder.registerCustomEditor(Print.class,
new PropertyEditorSupport() {
public void setValue(Object value) {
if (value instanceof MultipartFile) {
MultipartFile multipartFile = (MultipartFile) value;
ImagePrint print = new ImagePrint();
try {
print.setImage(multipartFile.getBytes());
print.setContentType(multipartFile.getContentType());
print.setFilename(multipartFile.getOriginalFilename());
}
catch (IOException ex) { ... }
super.setValue(print);
}
...