An introduction to the basics of the Spring MVC Web framework. The concepts of front controller, controller (handler), model and view are introduced. The whole processing pipeline is discussed, with an in-depth description of the HandlerMapping and ViewResolver strategy interfaces. The alternative representations of the Model (Map, Model and ModelMap) are presented.
5. MVC pa'ern
Applica'ons should have three major parts
• The part that manages the data
• The part that creates screens and reports
• The part that handles interac3ons between the user and the
other subsystems
12. Applica'on state
"If we think of an applica1on as being computerized behavior that
achieves a goal, we can describe an applica1on protocol as the set of
legal interac1ons necessary to realize that behavior. Applica3on state
is a snapshot of an execu1on of such an applica1on protocol."
(J. Webber et al.)
17. Stateless protocol
HTTP is stateless: we can implement the MVC design pa3ern on
top of the Java Servlet Pla;orm
18. Stateless protocol
The Java Servlet Pla-orm provides a session context to help track
users in the applica8on
19. Pull protocol
HTTP is a pull protocol: we can increase the Controller
responsibility
20. Pull protocol
Namely, the Controller will be responsible for:
• State changes
• State queries
• Change no2fica2ons
21. MVC 2 pa(ern
The resul)ng pa.ern is some)mes called MVC 2 or Web MVC
+-------------+ +---------------+ +----------------+
| +----------> +---------> |
| View | | Controller | | Domain model |
| <----------+ <---------+ |
+-------------+ +---------------+ +----------------+
22. MVC 2 pa(ern
Any state query or change no0fica0on must pass through the
Controller
+-------------+ +---------------+ +----------------+
| +----------> +---------> |
| View | | Controller | | Domain model |
| <----------+ <---------+ |
+-------------+ +---------------+ +----------------+
23. MVC 2 pa(ern
The View renders data passed by the Controller rather than data
returned directly from the Model
+-------------+ +---------------+ +----------------+
| +----------> +---------> |
| View | | Controller | | Domain model |
| <----------+ <---------+ |
+-------------+ +---------------+ +----------------+
24. MVC 2 pa(ern
We refer to such data as the view model
+-------------+ +---------------+ +----------------+
| +--------------> +---------> |
| View | | Controller | | Domain model |
| <--View model--+ <---------+ |
+-------------+ +---------------+ +----------------+
25. Compe&ng architectures
Note that MVC 2 proposes a compe&ng architecture to the one
we presented during the last lecture
┌──────┐┌────────────────────────────────┐
│ D ││ │
│ o ││ Presentation layer │
│ m ││ │
│ a │└────────────────────────────────┘
│ i │┌────────────────────────────────┐
│ n ││ │
│ ││ Service layer │
│ m ││ │
│ o │└────────────────────────────────┘
│ d │┌────────────────────────────────┐
│ e ││ │
│ l ││ Persistence layer │
│ ││ │
└──────┘└────────────────────────────────┘
26. Compe&ng architectures
We reconcile the two architectures by limi3ng the scope of the
MVC 2 pa;ern to the sole presenta(on layer
┌──────┐┌────────────────────────────────┐
│ D ││ │
│ o ││ Presentation layer │
│ m ││ │
│ a │└────────────────────────────────┘
│ i │┌────────────────────────────────┐
│ n ││ │
│ ││ Service layer │
│ m ││ │
│ o │└────────────────────────────────┘
│ d │┌────────────────────────────────┐
│ e ││ │
│ l ││ Persistence layer │
│ ││ │
└──────┘└────────────────────────────────┘
27. Compe&ng architectures
The controller will defer actual computa3on to the service layer
+-------------+ +---------------+ +----------------+
| +--------------> +---------> |
| View | | Controller | | Service layer |
| <--View model--+ <---------+ |
+-------------+ +---------------+ +----------------+
28. What is Spring MVC?
Spring MVC is an open source MVC 2 framework for developing
Web applica3ons
29. Spring MVC
Spring MVC eases Web applica2on development
• It provides a ready-to-use MVC 2 Controller
• It promotes Spring’s loosely coupled techniques in the
presenta>on layer of an applica>on
46. Se#ng up Spring MVC
Se#ng up Spring MVC is a two-step process
1. Loading DispatcherServlet in the Web container
2. Instan5a5ng a DI container and the related beans
48. Loading the front controller
We need to tell the Web container to load DispatcherServlet
49. Loading the front controller
To this end, we provide the Web container with a web.xml
deployment descriptor file
+ Web components
| (e.g., servlets)
|
+-------v--------+
| |
| Web |
web.xml +--------> container |
| |
+-------+--------+
|
|
v Web app
50. Loading the front controller
Recall that the web.xml file describes how to deploy the Web
components of an applica6on
+ Web components
| (e.g., servlets)
|
+-------v--------+
| |
| Web |
web.xml +--------> container |
| |
+-------+--------+
|
|
v Web app
54. Instan&a&ng beans
Such a container hosts beans belonging to the presenta(on layer
• Controllers
• Special beans
55. Controllers
Controllers are wri'en by the developer and provide access to the
service layer of the applica5on
@Controller
public class AccountController {
...
}
60. Special beans
For instance:
• HandlerMapping maps HTTP requests to handlers
• ViewResolver maps logical name to views
• HandlerExceptionResolver maps excep:ons to handlers
61. Special bean interfaces
Each special bean is expressed as an interface
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale);
}
63. Special bean advantages
Indeed, we can tailor each stage of the pipeline just by changing its
concrete implementa9on
public class VelocityViewResolver implements ViewResolver { ... }
public class XmlViewResolver implements ViewResolver { ... }
public class InternalResourceViewResolver implements ViewResolver { ... }
64. Default special beans
For each special bean, the DispatcherServlet maintains a list
of implementa)ons to be used by default
65. Default special beans
For instance, InternalResourceViewResolver is instan-ated
by default
public class InternalResourceViewResolver implements ViewResolver { ... }
66. Default special beans
If not stated otherwise, DispatcherServlet will use default
implementa4ons throughout the en4re processing pipeline
67. Web beans
Controllers and special beans are the Web beans of our applica3on
+---------------------------------------------------+
| |
| |
| |
| +------------+ +------------+ |
| | +------------+ | +------------+ |
| | | +-------------+ | | +-------------+ |
| | | | | | | | | |
| +-+ | Controllers | +-+ | Special | |
| +-+ | +-+ beans | |
| +-------------+ +-------------+ |
| |
| |
| |
+---------------------------------------------------+
Spring DI Container
68. WebApplicationContext
The container we obtain is of type WebApplicationContext
+---------------------------------------------------+
| |
| |
| |
| +------------+ +------------+ |
| | +------------+ | +------------+ |
| | | +-------------+ | | +-------------+ |
| | | | | | | | | |
| +-+ | Controllers | +-+ | Special | |
| +-+ | +-+ beans | |
| +-------------+ +-------------+ |
| |
| |
| |
+---------------------------------------------------+
Spring DI Container
70. Configura)on metadata
As with any DI container, WebApplicationContext has to be
provided with some configura)on metadata
+ Objects
|
|
|
|
+---------v---------+
| |
| Spring |
+-------------> Container |
| |
Configuration +---------+---------+
metadata |
(e.g., XML file) |
|
v Fully configured
system
71. frontcontroller-servlet.xml
In par'cular, the container looks in WEB-INF/ for an XML file
named frontcontroller-servlet.xml1
1
More generally, the servletName in servletName-servlet.xml must be the same as the <servlet-name>
specified in web.xml
72. frontcontroller-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation=”...">
<!-- CONFIGURE CONTROLLERS AND SPECIAL BEANS HERE -->
</beans>
76. Controllers
Controllers are responsible for responding to the ac#ons the user
takes on the user interface
• Submi'ng a form
• Clicking on a link
• Accessing a page
77. Types of controllers
There exist two op-ons for wri-ng controllers
• Using the @Controller annota-on
• Implemen-ng the Controller interface2
2
Do not use this approach. The only reason why the Controller interface is s5ll available is to preserve
compa5bility with legacy code
78. Annotated controllers
To write annotated controllers we need to use the @Controller
annota/on
@Controller
public class GreetingController {
...
}
79. Annotated controllers
The @Controller annota)on indicates that a par)cular class
serves the role of a controller
@Controller
public class GreetingController {
...
}
80. Auto-detec)ng controllers
A simple addi+on to frontcontroller-servlet.xml allows
the container to auto-detect annotated controllers
<context:component-scan
base-package="it.polimi.awt.springmvc.web"/>
81. Auto-detec)ng controllers
The <context:component-scan> element recursively scans a
package, looking for classes to be automa8cally registered as beans
<context:component-scan
base-package="it.polimi.awt.springmvc.web"/>
82. @RequestMapping
The @RequestMapping annota)on is used to map URLs to
handler methods
@Controller
public class GreetingController {
@RequestMapping("/greeting")
public String getGreeting() { ... }
}
84. Unique endpoints
Think of handler methods as unique endpoints, with mappings
derived from the related @RequestMapping annota6ons
@RequestMapping("/greeting")
public String getGreeting() {...}
85. Annotated controllers upsides
When you open a controller, you can see which URLs the controller
is mapped to
@RequestMapping("/greeting")
public String getGreeting() {...}
86. Annotated controllers downsides
The mapping is sca$ered throughout the codebase, making it
harder to see which URL is mapped to which handler method
@RequestMapping("/greeting")
public String getGreeting() {...}
87. Mapping URLs to handlers
In order to interpret the mappings defined in the controller class,
Spring MVC needs a HandlerMapping implementa8on
@RequestMapping("/greeting")
public String getGreeting() {...}
88. Determining which handler to call
The HandlerMapping interface provides the abstrac2on for
mapping requests to handlers3
public interface HandlerMapping {
public HandlerExecutionChain getHandler(HttpServletRequest request);
}
3
The getHandler method returns a HandlerExecutionChain only because of the need of accoun4ng for
possible interceptors. Conceptually, we can s4ll assume the method to return a handler
89. Determining which handler to call
Each HandlerMapping implementa-on is able to inspect the
request and come up with an appropriate handler
+----------------+
| |
| HandlerMapping |
| |
+---^------+-----+
| |
HTTP | | Handler
request | |
| |
+--+------v----+ +----------------+
| | | |
| Dispatcher +-------------> Handler |
+-------------> Servlet | | (Controller) |
| <-------------+ |
HTTP +--------------+ +----------------+
request
90. Determining which handler to call
DispatcherServlet consults one or more HandlerMapping
implementa.ons to know which handler to call
+----------------+
| |
| HandlerMapping |
| |
+---^------+-----+
| |
HTTP | | Handler
request | |
| |
+--+------v----+ +----------------+
| | | |
| Dispatcher +-------------> Handler |
+-------------> Servlet | | (Controller) |
| <-------------+ |
HTTP +--------------+ +----------------+
request
91. Determining which handler to call
If no handler is found, a 404 HTTP response is sent back to the
client
+----------------+
| |
| HandlerMapping |
| |
+---^------+-----+
| |
HTTP | | Handler
request | |
| |
+--+------v----+ +----------------+
| | | |
| Dispatcher +-------------> Handler |
+-------------> Servlet | | (Controller) |
| <-------------+ |
HTTP +--------------+ +----------------+
request
93. Overriding HandlerMapping
As of Spring 3, the suggested implementa.on became
RequestMappingHandlerMapping
public class DefaultAnnotationHandlerMapping implements HandlerMapping { ... }
public class RequestMappingHandlerMapping implements HandlerMapping { ... }
95. Overriding HandlerMapping
To start using RequestMappingHandlerMapping, it is sufficient
to alter frontcontroller-servlet.xml so as to add
<mvc:annotation-driven/>
96. Responsibili*es
We already know that the Web layer has two responsibili-es
1. Glueing together the service layer and the Web
2. Taking care of the naviga;on logic
97. Responsibili*es
Let us now see how to implement such responsibili2es in prac(ce
1. Glueing together the service layer and the Web
2. Taking care of the naviga;on logic
99. Interac(ng with the service layer
A"er being invoked, the controller processes the HTTP request
and interacts with the service layer
+---------------+ +----------------+
| +---------> |
| Controller | | Service layer |
| <---------+ |
+---------------+ +----------------+
100. GreetingService
For instance, we may assume that in our fic3onal applica3on, the
user can request a random gree3ng
public interface GreetingService {
public String getRandomGreeting();
}
101. GreetingService implementa*on
public class NaiveGreetingService implements GreetingService {
private List<String> greetings;
public NaiveGreetingService() {
this.greetings = Arrays.asList(
"Hello sweetie",
"Valar morghulis",
"Tiger tiger"
);
}
@Override
public String getRandomGreeting() {
int index = ThreadLocalRandom.current().nextInt(greetings.size());
return greetings.get(index);
}
}
102. Execu&ng the use case
Within the handler method, we first execute the related use case
@Controller
public class GreetingController {
private GreetingService service;
@RequestMapping("/greeting")
public String getGreeting() {
// execute the related use case
String greeting = service.getRandomGreeting();
...
}
}
103. Execu&ng the use case
Such an invoca+on may result in an arbitrarily complex object
@Controller
public class GreetingController {
private GreetingService service;
@RequestMapping("/greeting")
public String getGreeting() {
// execute the related use case
String greeting = service.getRandomGreeting();
...
}
}
104. Execu&ng the use case
However, in this case we end up with a simple String
@Controller
public class GreetingController {
private GreetingService service;
@RequestMapping("/greeting")
public String getGreeting() {
// execute the related use case
String greeting = service.getRandomGreeting();
...
}
}
105. Model
Once we collected the use case outcome, the next step is to build a
model object
+--------------+ +--------------------+ +----------------+
| | +----------+ | | +----------+ | |
| View <--+ Model +--+ Front Controller <--+ Model +--+ Controller |
| | +----------+ | | +----------+ | |
+--------------+ +--------------------+ +----------------+
106. Model
This model object will contain the frac%on of the outcome that
holds some significance for the next view to render
+--------------+ +--------------------+ +----------------+
| | +----------+ | | +----------+ | |
| View <--+ Model +--+ Front Controller <--+ Model +--+ Controller |
| | +----------+ | | +----------+ | |
+--------------+ +--------------------+ +----------------+
107. Model
The view will use the model to build up the dynamic parts of the
user interface
+--------------+ +--------------------+ +----------------+
| | +----------+ | | +----------+ | |
| View <--+ Model +--+ Front Controller <--+ Model +--+ Controller |
| | +----------+ | | +----------+ | |
+--------------+ +--------------------+ +----------------+
108. Model
The model is the collec+on of named objects needed by the view
in order to render itself
+---------------+--------------+
| key1 | value1 |
+------------------------------+
| key2 | value2 |
+------------------------------+
| key3 | value3 |
+---------------+--------------+
109. Model a(ributes
These named objects are usually called model a.ributes
+---------------+--------------+
| key1 | value1 |
+------------------------------+
| key2 | value2 |
+------------------------------+
| key3 | value3 |
+---------------+--------------+
110. Model implementa-ons
Several data structures can be used for represen2ng a model
• Any implementa-on of java.util.Map
• Any implementa-on of the Model interface provided by Spring
• A ModelMap object provided by Spring
111. Model as an input argument
Regardless the specific implementa2on, it is common prac2ce to let
the framework provide the model directly as an input argument
public String getGreeting(Map<String, Object> model) {...}
public String getGreeting(Model model) {...}
public String getGreeting(ModelMap model) {...}
112. Model as a Map
The only requirement when using java.util.Map to store model
a4ributes is that the key has to be of type String
@RequestMapping("/greeting")
public String getGreeting(Map<String, Object> model) {
String greeting = service.getRandomGreeting();
model.put("greeting", greeting);
...
}
113. Model as a Map
The value part of the map can be freely decided. Because of its
generality, Object is a popular choice
@RequestMapping("/greeting")
public String getGreeting(Map<String, Object> model) {
String greeting = service.getRandomGreeting();
model.put("greeting", greeting);
...
}
114. Model as a Model
The downside of using a Map is that we have to decide the name of
each model a)ribute we want to store
// deciding names... boring (and difficult!)
model.put("greeting", greeting);
115. Model as a Model
The Model interface provides convenient methods for popula4ng
the Model, such as addAttribute()
@RequestMapping("/special-deals")
public String getSpecialDeals(Model model) {
List<SpecialDeal> specialDeals = service.getSpecialDeals();
model.addAttribute(specialDeals);
...
}
116. Model as a Model
addAttribute() is the same as Map’s put(), except that it
automa&cally generates the name for the model a5ribute
@RequestMapping("/special-deals")
public String getSpecialDeals(Model model) {
List<SpecialDeal> specialDeals = service.getSpecialDeals();
model.addAttribute(specialDeals);
...
}
117. Naming strategy
• Scalar objects take the uncapitalized, unqualified class name
• Collec.on objects take the uncapitalized, unqualified class name
of the first object in the collec:on, with List appended to the
name
119. You can s)ll decide the names
If needed, it is s+ll possible to decide the name of each model
a4ribute
@RequestMapping("/fullname")
public String getFullname(Model model) {
// as both "Jon" and "Snow" are of type
// String, it is better to explicitly name them
model.addAttribute("name", "Jon");
model.addAttribute("surname", "Snow");
...
}
120. Model as ModelMap
The ModelMap class is a Map, but it provides some addi4onal
conveniences w.r.t. the plain Map interface
@RequestMapping("/fullname")
public String getFullname(Model model) {
// chained calls are handy!
model.addAttribute("name", "Jon")
.addAttribute("surname", "Snow");
...
}
121. Model as ModelMap
Namely:
• It adheres to the same naming conven2on as Model
• The addAttribute() method checks if the values are null
• It supports chained calls of the addAttribute() method
123. Choosing the next view
When processing is complete, the controller is responsible for
choosing what view the user sees next
+--------------+ +--------------------+ +----------------+
| | +----------+ | | +----------+ | |
| View <--+ Model +--+ Front Controller <--+ Model +--+ Controller |
| | +----------+ | | +----------+ | |
+--------------+ +--------------------+ +----------------+
124. The View renders the model
Views are responsible for rendering the objects in the Model
+--------------+ +--------------------+ +----------------+
| | +----------+ | | +----------+ | |
| View <--+ Model +--+ Front Controller <--+ Model +--+ Controller |
| | +----------+ | | +----------+ | |
+--------------+ +--------------------+ +----------------+
125. The View interface
Spring MVC does not make any assump)on on the specific view
technology
• JavaServer Pages
• JavaServer Faces
• Velocity
126. The View interface
To this end, views are modeled according to the View interface
public interface View {
void render(Map<String,?> model,
HttpServletRequest request,
HttpServletResponse response)
String getContentType();
}
127. The View interface
As with many Spring’s interfaces, the View interface is really simple
public interface View {
void render(Map<String,?> model,
HttpServletRequest request,
HttpServletResponse response)
String getContentType();
}
128. The render() method
The View will render the output star2ng from
• The Model
• The Servlet’s request and response objects
129. The render() method
The render() method returns void to denote that it is the
arriving point of the en0re request processing pipeline7
void render(Map<String,?> model,
HttpServletRequest request,
HttpServletResponse response)
7
Here, we are not considering possible calls to HandlerInterceptor.afterCompletion()
130. The render() method
Hence, it is the responsibility of the View not only to render the
content, but also to actually return it to the client
void render(Map<String,?> model,
HttpServletRequest request,
HttpServletResponse response)
131. Wri$ng a View implementa$on
Given the interface, we can easily write a View implementa4on
public class GreetingView implements View {
@Override
public void render(Map<String, ?> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
PrintWriter out = response.getWriter();
out.print(model.get("greeting"));
out.flush();
out.close();
}
@Override
String getContentType() { ... }
}
133. View technology independence
Spring MVC makes a special effort to keep the controller unaware
of the adopted view technology
@RequestMapping("/greeting")
public String getGreeting(Model model) {
...
return "greeting";
}
134. View technology independence
Such a decoupling is achieved by referring to views by their
logical name
@RequestMapping("/greeting")
public String getGreeting(Model model) {
...
return "greeting";
}
135. View technology independence
In order to enforce decoupling, we let the controller knows nothing
of the view but its logical name
@RequestMapping("/greeting")
public String getGreeting(Model model) {
...
return "greeting";
}
136. ViewResolver
The special bean ViewResolver will later resolve the view’s
logical name to an actual view instance
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale);
}
137. ViewResolver
Implementa)ons of ViewResolver are able to provide the
mapping between logical names and View objects
+-----------------+
| |
| ViewResolver |
| |
+---+-----------^-+
| |
+---v----+ |
| View | |
+---+----+ |
| |
+-----v-----------+---+ +----------------+
| | | |
| DispatcherServlet <-------+ Controller |
| | | |
+---------------------+ +----------------+
139. Controller testability
Note that this allows controllers to be easily unit-tested6
public class GreetingControllerTest {
@Test
public void getGreetingTest() {
GreetingController controller = new GreetingController(...);
Map<String, Object> model = new HashMap<>();
String viewName = controller.getGreeting(model);
assertTrue(viewName.equals("greeting"));
assertTrue(model.get("expression") != null);
}
}
6
Snippet adapted from h0p://stackoverflow.com/a/9363053/1849221
142. InternalResourceViewResolver
We need to configure InternalResourceViewResolver in
frontcontroller-servlet.xml
<bean class="org.springframework.web.
servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
143. Hiding JSP pages
It is good prac-ce to place all view pages under the /WEB-INF
directory
144. Hiding JSP pages
This protects the pages from being retrieved independently of the
Controllers that configure them
146. References
• Stackoverflow, Design pa5erns for Web applica:ons
• SpringSource, Spring Framework Reference
• SpringSource, Java-doc APIs
• Stackoverflow, ModelMap usage in Spring
147. References
• S. Ladd, K. Donald, Expert Spring MVC and Web flows, Apress
PublicaBons
• M. Deinum, K. Serneels, Pro Spring MVC: with Web flows,
Apress PublicaBons
• Craig Walls, Spring in AcBon (3rd EdiBon), Manning PublicaBons
• T. N. Husted et al., Struts 1 In AcBon, Manning PublicaBons
148. References
• Clarence Ho, Rob Harrop, Pro Spring 3, Apress Publica8ons
• Rod Johnson et al., Professional Java Development with the
Spring Framework, Wiley Publishing