This recipe introduces the Spring MVC controller with its simplest implementation.
We will discover later on, and especially in Chapter 3, Working with Java Persistence and Entities, that Spring MVC is a great tool to build a REST API. Here, we will focus on how to create a controller that prints some content in the response.
Starting with this recipe, we will be using GIT to follow each iteration that has been made to develop the cloudstreetmarket
application. After the initial setup, you will appreciate how smoothly you can upgrade.
This recipe comes with two initial sections for installing and configuring GIT.
For Mac OS X, double-click on the downloaded dmg
file to extract the package on your hard drive. Navigate to the extracted directory and double-click on the pkg
file. Select all the default options, one step after an other, up to the Successful Installation screen. Close the screen.
For Windows, execute the downloaded program and follow the default options for every step up to these screens:
Let the installation finish and click on the Finish button.
For verification, open your terminal and enter the following command:
git –version
This command should display the installed version. The presented installation guidelines were associated with GIT 2.6.3
.
cd <home-directory>/workspace
.Replace <home-directory>
with your own home path.
git init
git remote add origin https://github.com/alex-bretet/cloudstreetmarket.com
git fetch
command.Add this perspective with the button if you don't have it yet.
cloudstreetmarket-parent
. You will observe BUILD SUCCESS
each time. cd <home-directory>/workspace
git pull origin v2.2.1
edu.zipcloud.cloudstreetmarket.portal.controllers.
InfoTagController
class has been created:@Controller @RequestMapping("/info") public class InfoTagController { @RequestMapping("/helloHandler") @ResponseBody public String helloController(){ return "hello"; } }
wars
are deployed in the Tomcat server. Start the Tomcat server and access the http://localhost:8080/portal/info/helloHandler
URL with your browser.cloudstreetmarket-webapp/src/main/webapp/WEB-INF/dispatcher-context.xml
file, the following bean definition is added:<bean id="webAppVersion" class="java.lang.String"> <constructor-arg value="1.0.0"/> </bean>
InfoTagController
class are also added:@Autowired private WebApplicationContext webAppContext; private final static LocalDateTime startDateTime = LocalDateTime.now(); private final static DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ofPattern("EEE, d MMM yyyy h:mm a"); @RequestMapping("/server") @ResponseBody public String infoTagServer(){ return new StringJoiner("<br>") .add("-------------------------------------") .add(" Server: "+ webAppContext.getServletContext().getServerInfo()) .add(" Start date: "+ startDateTime.format(DT_FORMATTER)) .add(" Version: " + webAppContext.getBean("webAppVersion")) .add("--------------------------------------") .toString(); }
http://localhost:8080/portal/info/server
URL with your browser.We are going to draft an overview of Spring MVC as a Framework. We will then review how a Controller is configured from the DispatcherServlet
, the controller-level annotations, and from the method-handler signatures.
Spring MVC implements two common design patterns: the front controller design pattern and the MVC design pattern.
A system designed as a Front controller exposes a single entry point for all incoming requests. In Java Web environments, this entry point is usually a servlet—a unique servlet that dispatches and delegates to other components.
Servlets are standards in the Java web. They are associated to predefined URL paths and are registered in deployment descriptors (the web.xml
files). Parsing deployment descriptors, the servlet-container (such as Apache Tomcat) identifies the declared servlets and their URL mapping. At runtime, the servlet-container intercepts every HTTP client request and creates a new Thread for each one of them. Those Threads will call the matching relevant servlets with Java-converted request and response objects.
The MVC design pattern is more of an architectural style. It describes the application as a whole. It encourages a clear separation of concerns between three different layers that the request thread has to pass through: the Model, the View, and the Controller—the Controller, the Model, and then the View to be accurate.
When a client request is intercepted by the servlet-container, it is routed to the DispatcherServlet
. The DispatcherServlet
sends the request to one Controller (one controller method-handler), which has a configuration matching the request state (if a match is found).
The Controller orchestrates the business logic, the model generation and ultimately chooses a View for the model and the response. In this perspective, the model represents a populated data structure handled by the controller and given to the view for visualization purposes.
But the three components (Model, View, and Controller) can also be visualized at a Macro scale as independent static layers. Each of these components is a layer and a placeholder for every individual constituent, part of the category. The Controller layer contains all the registered controllers as well as the Web Interceptors and converters; the Model generation layer (and Business logic layer) contains the business services and data access components. The View layer encloses the templates (JSPs for example) and other web client-side components.
The Spring MVC flow can be represented with the following diagram:
We previously mentioned that Spring MVC implements a front controller pattern. The entry point is the DispatcherServlet
. This DispatcherServlet
relies on a HandlerMapping
implementation. With different strategies and specificities, the HandlerMapping
resolves a Controller method-handler for the request.
Once the DispatcherServlet
has a Controller method-handler, it dispatches the request to it. The method-handler returns a View name (or directly the View itself) and also the populated model object to the DispatcherServlet
.
With a View name, the DispatcherServlet
asks a ViewResolver
implementation to find and select a View.
With the request, a View, and a Model, the DispatcherServlet
has everything to build the client response. The view is processed with all these elements and the response is finally returned to the servlet-container.
As explained, the DispatcherServlet
is quite a central piece in Spring MVC. It intercepts the client requests that target predefined URL paths for the application. It maps them to handlers that belong to business logic operators (Controllers, Interceptors, Filters, and so on). It also provides a set of tools, available as beans for solving recurring web development issues and techniques such as serving a centralized and modular View layer, handling internationalisation, themes, handing exceptions, and so on.
Before everything, the DispatcherServlet
is a servlet and is defined as such in the web.xml
file with a servlet configuration and its servlet-mapping. The code is as follows:
<servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
In our application, in the cloudstreetmarket-webapp, the DispatcherServlet
is named spring and covers the full context-path of the application: /*
.
We have already seen that each DispatcherServlet
has a restricted-scope WebApplicationContext
that inherits the beans from the root ApplicationContext
.
By default, for the WebApplicationContext
, Spring MVC looks in the /WEB-INF
directory for a configuration file named {servletName}-servlet.xml
. We have, however, overridden this default name and location through the initialization parameter contextConfigLocation
:
<servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
Still in the web.xml
, you can see that the root application context (classpath*:/META-INF/spring/*-config.xml
) starts with the ContextLoaderListener
:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
Spring MVC controllers are the place where client requests really start to be processed by the business-specific code. Since Spring 2.5, we have been able to use annotations on controllers so we don't have to explicitly declare them as beans in configuration. This makes their implementation much easier to extend and understand.
A @Controller
annotation tags a class as a Web controller. It remains a Spring Stereotype for presentation layers. The main purpose of defining a Spring Stereotype is to make a target type or method discoverable during the Spring classpath scanning which is activated by package with the following command:
<context:component-scan base-package="edu.zipcloud.cloudstreetmarket.portal"/>
There is not much custom logic related to this annotation. We could run a Controller with other Stereotype annotations (@Component
or @Service
) if we don't bother making the application a cleaner place.
The
@RequestMapping
annotations define handlers onto Controller classes and/or onto controller methods. These annotations are looked-up among stereotyped classes by the DispatcherServlet
. The main idea behind the @RequestMapping
annotations is to define a primary path mapping on the class-level and to narrow HTTP request methods, headers, parameters, and media-types on the methods.
To implement this narrowing, the @RequestMapping
annotation accepts comma-separated parameters within parentheses.
Consider the following example:
@RequestMapping(value="/server", method=RequestMethod.GET)
Available parameters for
@RequestMapping
are summarized in the following table:
Parameter and type |
Use/description (from JavaDoc) |
---|---|
|
Assign a name to the mapping. |
|
The path mapping URIs (for example, |
Path mapping URIs may contain placeholders (for example, | |
A path implements URI templates that give access to selected parts of a URL through patterns, variables, placeholders, and matrix variables (see section URI Templates). | |
At the method level, relative paths (for example, | |
|
GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. |
|
A sequence of |
Expressions can be negated using the | |
|
A sequence of |
Specifying only the header name (for example, | |
Negating a header name (for example, "!My-Header") is also supported (the specified header is not supposed to be present in the request). | |
Also supports media type wildcards ( | |
|
The consumable media types of the mapped request. |
Only mapped if the | |
Negating an expression (for example, | |
|
The producible media types of the mapped request. |
Only mapped if the | |
Negating an expression (for example, |
All these parameters can be used both at the type and method level. When used at the type level, all method-level parameters inherit the parent-level narrowing.
Several constituents make a Controller method-handler. Here's another example of such a handler with Spring MVC:
@RequestMapping(value="/index") public ModelAndView getRequestExample(ServletRequest request){ ModelAndView mav = new ModelAndView(); mav.setViewName("index"); mav.addObject("variable1", new ArrayList<String>()); return mav; }
We have just talked about how to use the @RequestMapping
annotation. With regard to the method signature, this annotation can only be placed before the return-type.
Declaring specific types of arguments for handler methods can get Spring to automatically inject in them references to external objects. Objects related to the request lifecycle, the session, or to the application configuration. With the benefit of being scoped for the method, those argument types are presented in the following table:
A set of native annotations for method-handler arguments has been designed. They must be seen as handles that configure the web behavior of controller methods in regard to incoming requests or the response yet to be built.
They identify abstractions for handy Spring MVC functions such as request parameter binding, URI path variable binding, injection-to-argument of request payloads, HTML form-parameter binding, and so on.
Supported annotation arguments |
Use/description |
Package |
---|---|---|
|
Injects an URI Template variable into an argument. |
|
|
Injects name-value pairs located in URI path segments into an argument. | |
|
Injects a specific request parameter into an argument. | |
|
Injects a specific request HTTP Header into an argument. | |
|
Allows direct access to the request payload injecting it into an argument. | |
|
Injects the content of a specific part (meta-data, file-data…) of a multipart/form-data encoded request into an argument of the matching type (MetaData, MultipartFile…) | |
|
Populates automatically an attribute of the Model using the URI template. This binding is operated before the method handler processing. |
These annotations have to be placed just before the method argument to be populated:
@RequestMapping(value="/index") public ModelAndView getRequestExample(@RequestParam("exP1") String exP1){ ModelAndView mav = new ModelAndView(); mav.setViewName("index"); mav.addObject("exP1", exP1); return mav; }
Spring MVC, with different possible controller method return Types, allows us to specify either the response sent back to the client or the necessary configuration for targeting or populating with variables an intermediate View layer. Depending upon what we want to do or the actual application state, we have the choice among the following:
In the InfoTagController.infoTagServer()
method-handler, we have used the @ResponseBody
annotation before the return Type. This annotation has been borrowed from the REST-specific tools. When you don't need to process a View, the @ResponseBody
directive will use the registered Spring converters to marshal the returned object into the expected format (XML, JSON, and so on). It will then write the marshalled content to the Response body (as the Response payload).
In the case of a String object with no more configurations, it is printed out as such in the Response body. We could have used the ResponseEntity<String>
return Type to achieve the same goal.