© Balaji Varanasi and Maxim Bartkov 2022
B. Varanasi, M. BartkovSpring RESThttps://doi.org/10.1007/978-1-4842-7477-4_2

2. Spring Web MVC Primer

Balaji Varanasi1   and Maxim Bartkov2
(1)
Salt Lake City, UT, USA
(2)
Kharkov, Ukraine
 
In this chapter, we will discuss the following:
  • Spring and its features

  • The Model View Controller pattern

  • Spring Web MVC and its components

The Java ecosystem is filled with frameworks such as Jersey and RESTEasy, which allow you to develop REST applications. Spring Web MVC is one such popular web framework that simplifies web and REST application development. We begin this chapter with an overview of the Spring Framework and take a deep dive into Spring Web MVC and its components.

Note

This book doesn’t give a comprehensive overview of Spring and Spring Web MVC. Refer to Pro Spring and Pro Spring MVC and WebFlux (both published by Apress) for detailed treatment of these concepts.

Spring Overview

The Spring Framework has become the de facto standard for building Java/Java EE-based enterprise applications. Originally written by Rod Johnson in 2002, the Spring Framework is one of the suites of projects owned and maintained by Pivotal Software Inc. (http://spring.io). Among many other things, the Spring Framework provides a dependency injection model1 that reduces plumbing code for application development, supports aspect-oriented programming (AOP) for implementing crosscutting concerns, and makes it easy to integrate with other frameworks and technologies. The Spring Framework is made up of different modules that offer services such as data access, instrumentation, messaging, testing, and web integration. The different Spring Framework modules and their groupings are shown in Figure 2-1.
../images/332520_2_En_2_Chapter/332520_2_En_2_Fig1_HTML.png
Figure 2-1

Spring Framework modules

As a developer, you are not forced to use everything that the Spring Framework has to offer. The modularity of the Spring Framework allows you to pick and choose the modules based on your application needs. In this book, we will be focusing on the web module for developing REST services. Additionally, we will be using a few other Spring portfolio projects such as Spring Data, Spring Security, and Spring Boot. These projects are built on top of the infrastructure provided by the Spring Framework modules and are intended to simplify data access, authentication/authorization, and Spring application creation.

Developing Spring-based applications requires a thorough understanding of two core concepts—dependency injection and aspect-oriented programming.

Dependency Injection

At the heart of the Spring Framework lies dependency injection (DI). As the name suggests, dependency injection allows dependencies to be injected into components that need them. This relieves those components from having to create or locate their dependencies, allowing them to be loosely coupled.

To better understand DI, consider the scenario of purchasing a product in an online retail store. Completing a purchase is typically implemented using a component such as an OrderService. The OrderService itself would interact with an OrderRepository that would create order details in a database and a NotificationComponent that would send out the order confirmation to the customer. In a traditional implementation, the OrderService creates (typically in its constructor) instances of OrderRepository and NotificationComponent and uses them. Even though there is nothing wrong with this approach, it can lead to hard-to-maintain, hard-to-test, and highly coupled code.

DI, by contrast, allows us to take a different approach when dealing with dependencies. With DI, you let an external process such as Spring create dependencies, manage dependencies, and inject those dependencies into the objects that need them. So, with DI, Spring would create the OrderRepository and NotificationComponent and then hand over those dependencies to the OrderService. This decouples OrderService from having to deal with OrderRepository/NotificationComponent creation, making it easier to test. It allows each component to evolve independently, making development and maintenance easier. Also, it makes it easier to swap these dependencies with different implementations or use these components in a different context.

Aspect-Oriented Programming

Aspect-oriented programming (AOP) is a programming model that implements crosscutting logic or concerns. Logging, transactions, metrics, and security are some examples of concerns that span (crosscut) different parts of an application. These concerns don't deal with business logic and are often duplicated across the application. AOP provides a standardized mechanism called an aspect for encapsulating such concerns in a single location. The aspects are then weaved into other objects so that the crosscutting logic is automatically applied across the entire application.

Spring provides a pure Java-based AOP implementation through its Spring AOP module. Spring AOP does not require any special compilation nor changes to the class loader hierarchy. Instead, Spring AOP uses proxies for weaving aspects into Spring beans at runtime. Figure 2-2 provides a representation of this behavior. When a method on the target bean gets called, the proxy intercepts the call. It then applies the aspect logic and invokes the target bean method.
../images/332520_2_En_2_Chapter/332520_2_En_2_Fig2_HTML.png
Figure 2-2

Spring AOP proxy

Spring provides two-proxy implementations—JDK dynamic proxy and CGLIB proxy. If the target bean implements an interface, Spring will use JDK dynamic proxy to create the AOP proxy. If the class doesn't implement an interface, Spring uses CGLIB to create a proxy.

You can read more about JDK dynamic proxy in the official documentation: https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html

Spring Web MVC Overview

Spring Web MVC, part of the Spring Framework’s web module, is a popular technology for building web-based applications. It is based on the model-view-controller architecture and provides a rich set of annotations and components. Over the years, the framework has evolved; it currently provides a rich set of configuration annotations and features such as flexible view resolution and powerful data binding.

Model View Controller Pattern

The Model View Controller, or MVC, is an architectural pattern for building decoupled web applications. This pattern decomposes the UI layer into the following three components:
  • Model—The model represents data or state. In a web-based banking application, information representing accounts, transactions, and statements are examples of the model.

  • View—Provides a visual representation of the model. This is what the user interacts with by providing inputs and viewing the output. In our banking application, web pages showing accounts and transactions are examples of views.

  • Controller—The controller is responsible for handling user actions such as button clicks. It then interacts with services or repositories to prepare the model and hands the prepared model over to an appropriate view.

Each component has specific responsibility. The interaction between them is shown in Figure 2-3. The interaction begins with the Controller preparing the model and selecting an appropriate view to be rendered. The View uses the data from the model for rendering. Further interactions with the View are sent to the Controller, which starts the process all over again.
../images/332520_2_En_2_Chapter/332520_2_En_2_Fig3_HTML.jpg
Figure 2-3

Model View Controller interaction

Spring Web MVC Architecture

Spring’s Web MVC implementation revolves around the DispatcherServlet—an implementation of the FrontController Pattern2 that acts as an entry point for handling requests. Spring Web MVC’s architecture is shown in Figure 2-4.
../images/332520_2_En_2_Chapter/332520_2_En_2_Fig4_HTML.png
Figure 2-4

Spring Web MVC's architecture

The different components in Figure 2-4 and their interactions include the following:
  1. 1.

    The interaction begins with the DispatcherServlet receiving the request from the client.

     
  2. 2.

    DispatcherServlet queries one or more HandlerMapping to figure out a Handler that can service the request. A Handler is a generic way of addressing a Controller and other HTTP-based endpoints that Spring Web MVC supports.

     
  3. 3.

    The HandlerMapping component uses the request path to determine the right Handler and passes it to the DispatcherServlet. The HandlerMapping also determines a list of Interceptors that need to get executed before (Pre-) and after (Post-) Handler execution.

     
  4. 4.

    The DispatcherServlet then executes the Pre-Process Interceptors if any are appropriate and passes the control to the Handler.

     
  5. 5.

    The Handler interacts with any Service(s) needed and prepares the model.

     
  6. 6.

    The Handler also determines the name of the view that needs to get rendered in the output and sends it to DispatcherServlet. The Post-Process Interceptors then get executed.

     
  7. 7.

    This is followed by the DispatcherServlet passing the logical view name to a ViewResolver, which determines and passes the actual View implementation.

     
  8. 8.

    The DispatcherServlet then passes the control and model to the View, which generates response. This ViewResolver and View abstraction allow the DispatcherServlet to be decoupled from a particular View implementation.

     
  9. 9.

    The DispatcherServlet returns the generated response over to the client.

     

Spring Web MVC Components

In the previous section, you were introduced to Spring Web MVC components such as HandlerMapping and ViewResolver. In this section, we will take a deeper look at those as well as additional Spring Web MVC components.

Note

In this book, we will be using Java configuration for creating Spring beans. Contrary to XML-based configuration, Java configuration provides compile-time safety, flexibility, and added power/control.

Controller

Controllers in Spring Web MVC are declared using the stereotype org.springframework.stereotype.Controller. A stereotype in Spring designates roles or responsibilities of a class or an interface. Listing 2-1 shows a basic controller.
@Controller
public class HomeController {
        @GetMapping("/home.html")
        public String showHomePage() {
                return "home";
        }
}
Listing 2-1

HomeController Implementation

The @Controller annotation designates the HomeController class as a MVC controller. The @GetMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.GET). @GetMapping annotation maps web requests to handler classes and handler methods. In this case, the @GetMapping indicates that when a request for home.html is made, the showHomePage method should get executed. The showHomePage method has a tiny implementation and simply returns the logical view name home. This controller did not prepare any model in this example.

Model

Spring provides the org.springframework.ui.Model interface that serves as holder for model attributes. Listing 2-2 shows the Model interface with the available methods. As the names suggest, the addAttribute and addAttributes methods can be used to add attributes to the model object.
public interface Model {
        Model addAttribute(String attributeName, Object attributeValue);
        Model addAttribute(Object attributeValue);
        Model addAllAttributes(Collection<?> attributeValues);
        Model addAllAttributes(Map<String, ?> attributes);
        Model mergeAttributes(Map<String, ?> attributes);
        boolean containsAttribute(String attributeName);
        Map<String, Object> asMap();
        Object getAttribute(String attributeName);
}
Listing 2-2

Model Interface

The easiest way for a controller to work with a model object is by declaring it as a method parameter. Listing 2-3 shows the showHomePage method with the Model parameter. In the method implementation, we are adding the currentDate attribute to the model object.
@GetMapping("/home.html")
public String showHomePage(Model model) {
        model.addAttribute("currentDate", new Date());
        return "home";
}
Listing 2-3

showHomePage with Model Attribute

The Spring Framework strives to decouple our applications from the framework’s classes. So, a popular approach for working with model objects is to use a java.util.Map instance as shown in Listing 2-4. Spring would use the passed-in Map parameter instance to enrich the model that gets exposed to the view.
@GetMapping("/home.html")
public String showHomePage(Map model) {
        model.put("currentDate", new Date());
        return "home";
}
Listing 2-4

showHomePage with Map Attribute

View

Spring Web MVC supports a variety of view technologies such as JSP, Velocity, FreeMarker, and XSLT. Spring Web MVC uses the org.springframework.web.servlet.View interface to accomplish this. The View interface has two methods, as shown in Listing 2-5.
public interface View
{
        String getContentType();
        void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Listing 2-5

View Interface API

Concrete implementations of the View interface are responsible for rendering the response. This is accomplished by overriding the render method. The getContentType method returns the generated view's content type. Table 2-1 shows important View implementations that Spring Web MVC provides out of the box. You will notice that all of these implementations reside inside the org.springframework.web.servlet.view package.
Table 2-1

Spring Web MVC View Implementations

Class name

Description

org.springframework.web.servlet.view.json.MappingJackson2JsonView

View implementation that encodes model attributes and returns JSON

org.springframework.web.servlet.view.xslt.XsltView

View implementation that performs XSLT transformation and returns the response

org.springframework.web.servlet.view.InternalResourceView

View implementation that delegates the request to a JSP page inside the web application

org.springframework.web.servlet.view.tiles2.TilesView

View implementation that uses Apache Tiles configuration for Tile definition and rendering

org.springframework.web.servlet.view.JstlView

Specialized implementation of InternalResourceView that supports JSP pages using JSTL

org.springframework.web.servlet.view.RedirectView

View implementation that redirects to a different (absolute or relative) URL

Listing 2-6 shows the reimplementation of the HomeController that we looked at earlier. Here we are creating an instance of JstlView and setting the JSP page that we need to be rendered.
@Controller
public class HomeController {
        @RequestMapping("/home.html")
        public View showHomePage() {
                JstlView view = new JstlView();
                view.setUrl("/WEB-INF/pages/home.jsp");
                return view;
        }
}
Listing 2-6

HomeController View Implementation

Controller implementations typically don't deal with view instances. Instead, they return logical view names, as shown in Listing 2-1, and let view resolvers determine and create view instances. This decouples the controllers from tying to a specific view implementation and makes it easy to swap view implementations. Also, the controllers no longer need to know intricacies such as the location of the views.

@RequestParam

The @RequestParam annotation is used to bind Servlet request parameters to handler/controller method parameters. The request parameter values are automatically converted to the specified method parameter type using type conversion. Listing 2-7 shows two usages of @RequestParam. In the first usage, Spring looks for a request parameter named query and maps its value to the method parameter query. In the second usage, Spring looks for a request parameter named page, converts its value to an integer, and maps it to the pageNumber method parameter.
@GetMapping("/search.html")
public String search(@RequestParam String query, @RequestParam("page") int pageNumber) {
        model.put("currentDate", new Date());
        return "home";
}
Listing 2-7

RequestParam Usage

When a method parameter is annotated using @RequestParam, the specified request parameter must be available in the client request. If the parameter is missing, Spring will throw a MissingServletRequestParameterException exception. One way to address this is to set the required attribute to false, as shown in Listing 2-8. The other option is to use the defaultValue attribute to specify a default value.
@GetMapping("/search.html")
public String search(@RequestParam String query, @RequestParam(value="page", required=false) int pageNumber, @RequestParam(value="size", defaultValue="10") int pageSize) {
        model.put("currentDate", new Date());
        return "home";
}
Listing 2-8

Making a Request Parameter Not Required

@RequestMapping

As we learned in the “Controller” section, the @RequestMapping annotation is used to map a web request to a handler class or handler method. @RequestMapping provides several attributes that can be used to narrow down these mappings. Table 2-2 shows the different elements along with their descriptions.
Table 2-2

RequestMapping Elements

Element name

Description

Method

Restricts a mapping to a specific HTTP method such as GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE

Produces

Narrows mapping to media type that is produced by the method

Consumes

Narrows mapping to media type that the method consumes

Headers

Narrows mapping to the headers that should be present

Name

Allows you to assign a name to the mapping

Params

Restricts a mapping to the supplied parameter name and value

Value

Narrowing path for a specific handler method (if don’t have any elements, by default value is the main element)

Path

Narrowing path for a specific handler method (alias for value)

The default HTTP method mapped by @RequestMapping is GET. This behavior can be changed using the method element shown in Listing 2-9. Spring invokes the saveUser method only when a POST operation is performed. A GET request on saveUser will result in an exception thrown. Spring provides a handy RequestMethod enumeration with the list of HTTP methods available.
@RequestMapping(value="/saveuser.html", method=RequestMethod.POST)
public String saveUser(@RequestParam String username, @RequestParam String password) {
        // Save User logic
        return "success";
}
Listing 2-9

POST Method Example

@RequestMapping Shortcut Annotations

You can use “shortcut annotation” for @RequestMapping.

It looks more readable because you can use “shortcut annotation” instead of @RequestMapping.

All shortcut annotations inherit all elements from @RequestMapping, without method, because the method is already in the title of the annotation.

For example, @GetMapping is exactly the same as @RequestMapping(method = RequestMethod.GET).
Table 2-3

Shortcut Annotations for @RequestMapping

Annotation

Replacement

@GetMapping

@RequestMapping(method = RequestMethod.GET)

@PostMapping

@RequestMapping(method = RequestMethod.POST)

@PutMapping

@RequestMapping(method = RequestMethod.PUT)

@DeleteMapping

@RequestMapping(method = RequestMethod.DELETE)

@PatchMapping

@RequestMapping(method = RequestMethod.PATCH)

The produces element indicates the media type, such as JSON or XML or HTML, produced by the mapped method. The produces element can take a single media type or multiple media types as its value. Listing 2-10 shows the search method with the produces element added. The MediaType.TEXT_HTML value indicates that when a GET request is performed on search.html, the method returns an HTML response.
@GetMapping(value="/search.html", produces="MediaType.TEXT_HTML")
public String search(@RequestParam String query, @RequestParam(value="page", required=false) int pageNumber) {
        model.put("currentDate", new Date());
        return "home";
}
Listing 2-10

Produces Element Example

It is possible for the client to perform a GET request on /search.html but send an Accept header with value application/JSON. In that scenario, Spring will not invoke the search method. Instead, it will return a 404 error. The produces element provides a convenient way to restrict mappings to content types that the controller can serve. In the same fashion, the consumes element is used to indicate the media type that the annotated method consumes.

Accept and Content-Type Header

As discussed in Chapter 1, REST resources can have multiple representations. REST clients typically use the Accept and Content-Type headers to work with these representations.

REST clients use the Accept header to indicate the representations that they accept. The HTTP specification allows a client to send a prioritized list of different media types that it will accept as responses. On receiving the request, the server will send the representation with the highest priority. To understand this, consider the default Accept header for Firefox browser:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

The q parameter, also known as relative quality parameter, indicates the degree of preference and has values ranging from 0 to 1. From the string, we can infer that the HTML and XHTML will have a priority of 1 because they don't have an associated q value. The XML media type has priority 0.9, and the rest of the representations have a priority of 0.8. On receiving this request, the server would try to send an HTML/XHTML representation because it has the highest priority.

In a similar fashion, REST clients use the Content-Type header to indicate the media type of the request being sent to the server. This allows the server to properly interpret the request and parse the contents correctly. If the server is unable to parse the content, it will send a 415 Unsupported Media Type error status code.

Spring Web MVC allows flexible signatures for methods annotated with @RequestMapping. This includes variable method parameters and method return types. Table 2-4 lists the important arguments allowed. For a detailed list of allowed arguments, refer to Spring's Javadocs at http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html.
Table 2-4

Method Arguments and Descriptions

Method argument

Description

HttpServletRequest/HttpServletResponse

HTTP Servlet request and response objects. Allows raw access to client’s data, such as request parameters and headers.

HttpSession

Instance representing a user’s HTTP session.

Command object

A POJO or model object that Spring populates/binds with the user-submitted data. The command object can be annotated with @ModelAttribute.

BindingResult

Instance representing a command object’s validation and binding. This parameter must immediately precede the command object.

HttpEntity<?>

Instance representing an HTTP request. Each HttpEntity is composed of request body and a set of headers.

Principal

A java.security.Principal instance that represents the authenticated user.

The different return types supported in methods annotated with @RequestMapping are shown in Table 2-5.
Table 2-5

Return Types and Descriptions

Return type

Description

String

Represents the logical view name. Registered view resolvers are employed to resolve the physical view, and a response is generated.

View

Instance representing a view. In this case, no view resolution is performed and the view object is responsible for generating the response. Examples include JstlView, VelocityView, RedirectView, and so on.

HttpEntity<?>

Instance representing an HTTP response. Each HttpEntity is composed of response body and a set of headers.

HttpHeaders

Instance capturing the headers to be returned. Response will have an empty body.

Pojo

Java object that is considered to be a model attribute. A specialized RequestToViewNameTranslator is used to determine the appropriate logical view name.

Path Variables

The @RequestMapping annotation supports dynamic URIs via URI templates. As discussed in Chapter 1, URI templates are URIs with placeholders or variables. The @PathVariable annotation allows you to access and use these placeholders via method parameters. Listing 2-11 gives an example of @PathVariable. In this scenario, the getUser method is designed to serve user information associated with the path variable {username}. The client would perform a GET on the URL /users/jdoe to retrieve user information associated with username jdoe.
@RequestMapping("/users/{username}")
public User getUser(@PathVariable("username") String username) {
        User user = null;
        // Code to construct user object using username
        return user;
}
Listing 2-11

PathVariable Example

View Resolver

As discussed in the previous sections, a Spring Web MVC controller can return an org.springframework.web.servlet.View instance or a logical view name. When a logical view name is returned, a ViewResolver is employed to resolve the view to a View implementation. If this process fails for some reason, a javax.servlet.ServletException is thrown. The ViewResolver interface has a single method and is shown in Listing 2-12.
public interface ViewResolver
{
        View resolveViewName(String viewName, Locale locale) throws Exception;
}
Listing 2-12

ViewResolver Interface

Table 2-6 lists some of the ViewResolver implementations provided by Spring Web MVC.

As you might have noticed, the different view resolvers in Table 2-6 mimic the different types of views we looked at earlier. Listing 2-13 shows the code required for creating an InternalViewResolver.

Also Listing 2-13 shows @Bean annotation—in short, all methods defined by @Bean in a class annotated by @Configuration will return objects, which is controlled by Spring Framework, and with help we can define behavior for some objects and call the object everywhere using @Inject or @Autowired annotation if it is needed. Every object created by Spring Framework will be defined as a @Bean by default.
Table 2-6

ViewResolver Implementations and Descriptions

Return type

Description

BeanNameViewResolver

ViewResolver implementation that looks for a bean with an id that matches the logical view name in the ApplicationContext. If it doesn't find the bean in the ApplicationContext, a null is returned.

InternalResourceViewResolver

ViewResolver that looks for an internal resource that has the logical view name. The location of the internal resource is typically computed by prefixing and suffixing the logical name with path and extension information.

ContentNegotiatingViewResolver

ViewResolver that delegates the view resolution to other view resolvers. The choice of the view resolver is based on the requested media type, which itself is determined using an Accept header or file extension or URL parameter.

TilesViewResolver

ViewResolver that looks for a template in the Tiles configuration that matches the logical view name.

@Bean
public ViewResolver viewResolver() {
     InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
     viewResolver.setPrefix("/WEB-INF/jsp/");
     viewResolver.setSuffix(".jsp");
     return viewResolver;
}
Listing 2-13

InternalViewResolver Example

Exception Handler

Exceptions are part of any application, and Spring provides the HandlerExceptionResolver mechanism for handling those unexpected exceptions. The HandlerExceptionResolver abstraction is similar to the ViewResolver and is used to resolve exceptions to error views. Listing 2-14 shows the HandlerExceptionResolver API.
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex);
}
Listing 2-14

HandlerExceptionResolver API

Spring provides several out-of-the-box implementations of HandlerExceptionResolver, as shown in Table 2-7.
Table 2-7

HandlerExceptionResolver Implementations and Descriptions

Resolver implementation

Description

org.springframework.web.servlet.handler.SimpleMappingExceptionResolver

Exception resolver implementation that maps exception class names to view names.

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

Exception resolver implementation that translates standard Spring exceptions to HTTP status codes.

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

Custom exceptions in Spring applications can be annotated with @ResponseStatus, which takes a HTTP status code as its value. This exception resolver translates the exceptions to its mapped HTTP status codes.

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver

Exception resolver implementation that resolves exceptions using annotated @ExceptionHandler methods.

The SimpleMappingExceptionResolver has been around for a really long time. Spring 3 introduced a new way of handling exceptions using the @ExceptionHandler strategy. This provides a mechanism for handling errors in REST-based services where there is really no view to show but, rather, return data. Listing 2-15 shows a controller with an exception handler. Any methods that now throw a SQLException in the HomeController will get handled in the handleSQLException method. The handleSQLException simply creates a ResponseEntity instance and returns it. However, additional operations such as logging, returning additional diagnostic data, and so on can be performed.
@Controller
public class HomeController {
        @ExceptionHandler(SQLException.class)
        public ResponseEntity handleSQLException() {
ResponseEntity responseEntity = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
                return responseEntity;
        }
        @GetMapping("/stream")
        public void streamMovie(HttpServletResponse response) throws SQLException {
        }
}
Listing 2-15

ExceptionHandler Example

The @ExceptionHandler annotated methods can only handle exceptions that occur in the controller or its subclasses. So, if we need to handle SQL exceptions in other controllers, then we need to copy and paste the handleSQLException method in all of those controllers. This approach can pose severe limitations, as exception handling is truly a crosscutting concern and should be centralized.

To address this, Spring provides the @ControllerAdvice annotation. Methods in classes annotated with @ControllerAdvice get applied to all the @RequestMapping methods. Listing 2-16 shows the GlobalExceptionHandler with the handleSQLException method. As you can see, the GlobalExceptionHandler extends Spring's ResponseEntityExceptionHandler, which converts default Spring Web MVC exceptions to a ResponseEntity with HTTP status codes.
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
        @ExceptionHandler(SQLException.class)
        public ResponseEntity handleSQLException() {
ResponseEntity responseEntity = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
                return responseEntity;
        }
}
Listing 2-16

GlobalExceptionHandler Example

Interceptors

Spring Web MVC provides the notion of interceptors to implement concerns that crosscut across different handlers. Consider the scenario in which you want to prevent unauthenticated access to a set of controllers. An interceptor allows you to centralize this access logic without you having to copy and paste the code in every controller. As the name suggests, interceptors intercept a request; they do so at the following three points:
  • Before the controller gets executed. This allows the interceptor to decide if it needs to continue the execution chain or return with an exception or custom response.

  • After the controller gets executed but before the response is sent out. This allows the interceptor to provide any additional model objects to the view.

  • After the response is sent out allowing any resource cleanup.

Note

Spring Web MVC interceptors are similar to HTTP servlet filters. Both can be used to intercept a request and execute common concerns. However, there are a few differences between them that are worth noting. Filters have the capability to wrap or even swap the HttpServletRequest and HttpServletResponse objects. Interceptors can’t decorate or swap those objects. Interceptors are Spring-managed beans, and we can easily inject other spring beans in them. Filters are container-managed instances; they don't provide a straightforward mechanism for injecting Spring-managed beans.

Spring Web MVC provides the HandlerInterceptor interface for implementing interceptors. Listing 2-17 gives the HandlerInterceptor interface. As you can see, the three methods correspond to the three interceptor features that we just discussed.
public interface HandlerInterceptor{
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
}
Listing 2-17

HandlerInterceptor API

Listing 2-18 gives a simple interceptor implementation. As you can see, the SimpleInterceptor class extends HandlerInterceptorAdapter. The HandlerInterceptorAdapter is a convenient abstract class that implements the HandlerInterceptor interface and provides default implementations of its methods.
public class SimpleInterceptor extends HandlerInterceptorAdapter {
        private static final Logger logger = Logger.getLogger(SimpleInterceptor.class);
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("Inside the prehandle");
        return false;
    }
}
Listing 2-18

Spring Web MVC Interceptor Example

Interceptors can be registered in a Spring Web application using the InterceptorRegistry strategy. When using Java configuration, this is typically achieved by creating a configuration class that extends WebMvcConfigurerAdapter. Spring Web MVC’s WebMvcConfigurerAdapter class provides the addInterceptors method that can be used to access the InterceptorRegistry . Listing 2-19 shows the code registering two interceptors: LocalInterceptor that comes out of the box with Spring and our SimpleInterceptor.
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.apress.springrest.web" })
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new SimpleInterceptor()).addPathPatterns("/auth/**");
    }
}
Listing 2-19

Example Registering Interceptors

When an interceptor is added to the interceptor registry, the interceptor gets applied to all of the handler mappings. So, the LocaleChangeInterceptor in Listing 2-19 gets applied to all the handler mappings. However, it is also possible to restrict the interceptor to certain URLs. This is demonstrated in Listing 2-19 using the addPathPatterns method . Here we are indicating that the SimpleInterceptor should be applied to only the URLs that are under the auth path.

Summary

In this chapter, we have looked at the basics of the Spring Framework and different components of a Spring Web MVC. In the next chapter, we will bring things together and look at building our first RESTful application using Spring Boot.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.128.105