Spring MVC
Request Lifecycle
- HTTP Request reaches Spring’s
DispatcherServlet
. DispatcherServlet
uses one or more handler mappings to figure out the controller to the handle the request- Controller calls the service classes to perform a business function. Optionally returns a business model class.
- Controller returns a logical view name
DispatcherServlet
consults a view resolver to map the logical view name to a specific view implementation, which couild be JSP, Apache Tiles, Thymeleaf, etc.- View uses the model data to render the output.
DispatcherServlet
sends the output as HTTP response back to client.
Components
2 ways to enable Spring MVC
- Add
<mvc:annotation-driven>
element in XML (which XML) - Add
@EnableWebMvc
annotation in configuration class
1 2 3 4 5 6 |
|
Application Contexts
Spring web applications have 2 application contexts.
DispatcherServlet
loads beans containing web components such as controllers, view resolvers, handler mappings, etc.ContextLoaderListener
loads the other beans in the application that are typically used by service and data access layers.
DispatcherServlet
DispatcherServlet
is theFront Controller
for Spring MVC. This is the single servlet that delegates responsibility to other controllers to perform the actual processing.- When
DispatcherServlet
starts up, it creates a Spring application context and loads it with beans declared in- the configuration file or
- the classes given via
getServletConfigClasses()
method inAbstractAnnotationConfigDispatcherServletInitializer
4 ways to configure DispatcherServlet
1) Via web.xml
with XML-based context files
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
If you’d rather specify the location of the DispatcherServlet
configuration file,
you can set a contextConfigLocation
initialization parameter on the servlet as follows.
1 2 3 4 5 6 7 8 9 |
|
2) Via web.xml
with Java-based context classes annotated with @Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
3) Implement WebApplicationInitializer
(From Spring 3.0, container automatically detects an implementation of this interface)
1 2 3 4 5 6 7 8 9 10 11 |
|
4) Extend abstract class AbstractAnnotationConfigDispatcherServletInitializer
(which implements WebApplicationInitializer
and invoked by SpringServletContainerInitializer
). It creates both a DispatcherServlet
and a ContextLoaderListener
. (works only in containers supporting Servlet 3.0)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
ContextLoaderListener
- Spring web applications have 2 application contexts.
DispatcherServlet
loads beans containing web components such as controllers, view resolvers, handler mappings, etc.ContextLoaderListener
loads the other beans in the application that are typically used by service and data access layers.
- loads the beans for the application (not web related beans)
Controller
- Just classes annotated with
@Controller
and methods annotated with@RequestMapping
1 2 3 4 5 6 7 |
|
- Various input types to request handler method in controller
- No input parameter
Model
java.util.Map
- Parameters annotated with
@RequestParam
- Parameters annotated with
@PathVariable
- POJO populated with form parameters
Errors
- used for form validation
- Various output/return types from request handler method in controller
void
String
- logical view name (“car”)
- physical view name (
/WEB-INF/jsp/car.jsp
) - special view names (
redirect:/cars/7
) - ModelAndView
Model
Map
ModelMap
View
@ResponseBody
@ModelAttribute
2 ways to configure a Controller
- Add
@Component
to a class - Add
@Controller
to a class. This is just a stereotype annotation and serves only for component-scanning.
Request Mapping
@RequestMapping
can be applied at class-level and method-level.@RequestMapping
takes an array paths as input as well.
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 |
|
User input
There are 3 ways to get user input into a controller’s handler method:
1) Query parameters
1 2 3 4 5 6 7 |
|
2) Path variables
1 2 3 4 5 |
|
3) Form parameters
Spring MVC binds the form parameters to the fields in the POJO by field name.
1 2 3 4 5 |
|
When handling a
POST
request, it’s usually a good idea to send a redirect after thePOST
has completed processing so that a browser refresh won’t accidentally submit the form a second time
Model
org.springframework.ui.Model
is nothing but a Map implementation
1 2 3 4 5 6 |
|
View Resolver
- Presenting here some of the commonly used view resolvers
- Default view resolver is
BeanNameViewResolver
1 2 3 |
|
BeanNameViewResolver
Resolves views as beans in the Spring application context whose ID is the same as the view name. The bean has to implement View
interface.
InternalResourceViewResolver
In the below example, when controller a string say “blah”, the below resolver will resolve it to /WEB-INF/views/blah.jsp
1 2 3 4 5 6 7 8 |
|
InternalResourceViewResolver
also recognizes the following special prefixes
redirect:
- e.g.,return "redirect:/spitter/" + spitter.getUsername();
forward:
- e.g.,return "forward:/spitter/" + spitter.getUsername();
View
1 2 3 4 |
|
Web MVC Application Layer
Layering leads to separation of concerns, easy testability, clean architecture.
User Interface (Presentation)
- presents the application to the user rendering the response as requested by the client. e.g., HTML, XML, PDF, etc.
- In Spring it is represented by a generic interface ‘View’ and it has no dependencies on a particular view technology like JSP, Velocity, Tiles, etc.
Web (Presentation)
- Thin layer; no business logic;
- In Spring it is represented by ‘Controller’ interface or classes with @Controller annotation.
- Responsible for (1) page navigation logic via Spring Web Flow, etc. (2) integrating service layer and HTTP.
- Converts HTTP request into service layer calls, and then transforms result from server into response for the user interface.
- Contains cookies, HTTP headers, HTTP sessions; responsible to manage these consistently and transparently.
- 2 types of web layer implementations:
- (a) request-repsonse frameworks. e.g., Struts and Spring MVC. They operate on ServletRequest and ServletResponse objects and is not hiddent from the users.
- (b) component-based frameworks. e.g., JSF, Tapestry. Hides the Servlet API from the user and offers a component-based programming model.
Service layer
- only business logic (transactional boundary, security, etc.).
- No persistence or presentation logic.
- Coarse-API layer - funciton should represent a single unit of work that either succeeds of fails. User can user different clients (web, web service, desktop app, JMS) but the business logic is same.
- Services should be stateless and a good practice to keep it Singleton.
- Keeping the service layer clean also allows us to reuse the same services for different channels. For example, it enables us to add a web service or JMS-driven solution
Data Access
Interface-based layer abstracts persistence framework (JDO, JDBC, JPA, etc.) No business logic.
Domain
(cuts across all layers) - Domain class names are nouns. Contains both state and behavior. In anemic domain model, it holds only state and no behavior.
Communication should be top-to-bottom except Domain layer. Data access shouldn’t access Service layer. Circular dependencies is a sign of bad design. A rule of thumb is that, if a layer has too many dependencies with other layers, we might want to introduce another layer that incorporates all the dependencies. On the other hand, if we see a single layer throughout different layers, we might want to reconsider this layer and make it an aspect of the application (Spring AOP).
Definitions
ModelMap
View
- Out-of-the-box - JSP, JSTL, Tiles, Velocity, FreeMaker, etc.
- Special - redirect: and forward:
ModelAndView
An aggregator/container class which holds both a ModelMap and a View instance.
Controller
- RequestMapping is defined both at class-level and method-level
- Types of mapping requests
- by path -
@RequestMapping("/welcome")
- by HTTP method -
@RequestMapping("/welcome", method=RequestMethod.GET)
- by presence/value of query parameter -
@RequestMapping("find=ByMake", "form")
- by presence/value of request header -
@RequestMapping("/welcome", header="accept=text/*")
- by path -
- Types of controller method return types (http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html)
- void
- String
- logical view name (“car”)
- physical view name (
/WEB-INF/jsp/car.jsp
) - special view names (
redirect:/cars/7
) - ModelAndView
- Model / Map / ModelMap
- View
@ResponseBody
/@ModelAttribute
1 2 3 |
|
1 2 3 4 5 6 7 8 |
|
- Additional annotations at controller level
@ModelAttribute
- method parameter level - maps a model attribute to the specific method parameter
- method level - provides the reference data to the model
@SessionAttributes
- class level: list the names or types of model attributes which should be stroed in the session.@RequestHeader
- method parameter level - maps request header parameters to method parameters@CookieValue
- method parameter level - gets the JSESSIONID of the cookie@RequestBody
/@ResponseBody
FormController
- Override onSubmit() method to provide custom handling function
- Override formBackingObject() method to provide default values when the form is first rendered.
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 |
|
MultiActionController
- allows multiple request types to be handled by the same class. For example, when mapped *Foo.htm to FooController which extends MultiActionController.
- Based on the request type, different methods on implementing class will be invoked; which method to invoke is decided by MethodNameResolver implementation which can be injected.
InternalPathMethodNameResolver
- maps file name in url to method name. e.g, requesting addFoo.htm and deleteFoo.htm would invoke methods addFoo() and deleteFoo() respectively.PropertiesMethodNameResolver
- maps url to method name based on a pre-configured map as follows.
1 2 3 4 5 6 7 8 9 |
|
ParameterNameMethodNameResolver
- maps a request parameter value to method name. e.g.,/Foo.html?action=add
invokes method add() after configuring ‘action’ as the parameter to look for.
1 2 3 |
|
HandlerMapping
- Maps incoming request to a handler/controller
- Default mapping classes
BeanNameUrlHandlerMapping
- maps url to bean name in app context xml.DefaultAnnotationHandlerMapping
- maps url to classes with@Controller
and@RequestMapping
annotations.
SimpleUrlHandlerMapping
- maps url to a bean name in the app context.
HandlerInterceptor
- intercepting requests before handed over to handlers to implement special functions like security, monitoring, etc.
- This interface defines three methods:
preHandle()
is called before the actual handler is executed;postHandle()
- is called after the handler is executed;afterProcessing()
- is called after the complete request has finished.
HandlerExecutionChain
…
HandlerAdapter
is the glue between the dispatcher servlet and the handler. It removes the actual execution logic from the dispatcher servlet, which makes the dispatcher servlet infinitely extensible. It executes the Handler identified from the HandlerMapping. It takes a Handler as input and returns ModelAndView. If there is no view in the returned ModelAndView, RequestToViewNameTranslator is consulted to generate a view name based on the incoming request.
Resolvers
ViewResolver
UrlBasedViewResolver - controller returns “cars”, resolver resolves it to “/WEB-INF/jsp/cars.jsp”
1 2 3 4 5 |
|
HandlerExceptionResolver
DispatcherServlet Bootstrapping
4 ways to bootstrap a DispatcherServlet in a ServletContainer like Tomcat
web.xml
web-fragment.xml
(since Servlet 3.0 spec)javax.servlet.ServletContextInitializer
(since Servlet 3.0 spec)
1 2 3 4 5 6 7 8 |
|
- Spring’s WebApplicationInitializer
Spring has an in-built implementation of ServletContextInitializer which scans classpath for WebApplicationInitializer implementations and invokes onStartup()
on them.
1 2 3 4 5 6 7 8 |
|
By default, the dispatcher servlet loads a file named [servletname]-servlet.xml from the WEB-INF directory.
How to configure DispatcherServlet and ContextLoaderListener in Java?
In web, DispatcherServlet and ContextLoaderListener components bootstrap and configure an application context.
- Steps to create
DispatcherServlet
in Java- Spring 3.0 container automatically detects a FooWebAppInitializer class that extends WebApplicationInitializer.
- FooWebAppInitializer overrides onStartUp(ServletContext) method.
- Create new WebApplicationContext - new AnnotationConfigWebApplicationContext();
- Create new DispatcherServlet - new DispatcherServlet(webappContext);
- Add dispatcher servlet to ServletContext - servletContext.addServlet(“dispatcher”, dispatcherServlet);
- Steps to create
ContextLoaderListener
in Java- Spring 3.0 container automatically detects a FooWebAppInitializer class that extends WebApplicationInitializer.
- FooWebAppInitializer overrides onStartUp(ServletContext) method.
- Create new WebApplicationContext - new AnnotationConfigWebApplicationContext();
- Create new ContextLoaderListener - new ContextLoaderListener(webappContext);
- Add listener to ServletContext - servletContext.addListener(contextLoaderListener);
DispatcherServlet Request Processing Flow
- DispatcherServlet receives request.
- Before dispatching
- Determines and exposes java.util.Locale of the current request using LocaleResolver.
- Prepares and exposes current request in RequestContextHolder.
- Constructs FlashMap using FlashMapManager. Map contains attributes from previous request when a redirect is made.
- Request is checked if it is a multipart HTTP request. If so, request is wrapped in MultipartHttpServletRequest via MultipartResolver.
- Dispatching
- DispatcherServlet consults 1 or more HandlerMapping implementations to determine the handler to handle the request.
- Check HandlerMapping
- Check HandlerApapter
- Servlet tries to find a HandlerAdapter,
- If not found, ServletException is thrown.
- If found and if 1 or more HandlerInterceptor are defined, preHandle and postHandle methods in the interceptor are executed before and after the Handler execution.
- If a view is selected, DispatcherServlet checks if the view reference is a String or View.
- If a String is found, ViewResolvers are consulted to resolve it to a View implementation.
- If not resolved, ServletException is thrown.
- Handling Exceptions
- If an exception is thrown during request handling, DispatcherServlet consults HandlerExceptionResolver to handle thrown exceptions.
- It resolves an exception to a view to show to the user.
- If an exception is unresolved, it is rethrown and handled by the servlet container which throws HTTP 500 error.
- After dispatching
- DispatcherServlet uses the event mechanism in the Spring Framework to fire a RequestHandledEvent. ApplicationListener can be to receive and log these events.
# Bibliography
- Pro Spring MVC with WebFlows
- Spring in Action