Movatterモバイル変換


[0]ホーム

URL:


22. Web MVC framework
Prev Part VI. The Web Next

22. Web MVC framework

22.1 Introduction to Spring Web MVC framework

The Spring Web model-view-controller (MVC) framework is designed around aDispatcherServlet that dispatches requests to handlers, with configurable handlermappings, view resolution, locale, time zone and theme resolution as well as support foruploading files. The default handler is based on the@Controller and@RequestMappingannotations, offering a wide range of flexible handling methods. With the introductionof Spring 3.0, the@Controller mechanism also allows you to create RESTful Web sitesand applications, through the@PathVariable annotation and other features.

"Open for extension…​"A key design principle in Spring Web MVC and in Spring in general is the "Open forextension, closed for modification" principle.

Some methods in the core classes of Spring Web MVC are markedfinal. As a developeryou cannot override these methods to supply your own behavior. This has not been donearbitrarily, but specifically with this principle in mind.

For an explanation of this principle, refer toExpert Spring Web MVC and Web Flow bySeth Ladd and others; specifically see the section "A Look At Design," on page 117 ofthe first edition. Alternatively, see

You cannot add advice to final methods when you use Spring MVC. For example, you cannotadd advice to theAbstractController.setSynchronizeOnSession() method. Refer toSection 11.6.1, “Understanding AOP proxies” for more information on AOP proxies and why you cannotadd advice to final methods.

In Spring Web MVC you can use any object as a command or form-backing object; you do notneed to implement a framework-specific interface or base class. Spring’s data binding ishighly flexible: for example, it treats type mismatches as validation errors that can beevaluated by the application, not as system errors. Thus you do not need to duplicate yourbusiness objects' properties as simple, untyped strings in your form objects simply tohandle invalid submissions, or to convert the Strings properly. Instead, it is oftenpreferable to bind directly to your business objects.

Spring’s view resolution is extremely flexible. AController is typically responsiblefor preparing a modelMap with data and selecting a view name but it can also writedirectly to the response stream and complete the request. View name resolution is highlyconfigurable through file extension or Accept header content type negotiation, throughbean names, a properties file, or even a customViewResolver implementation. The model(the M in MVC) is aMap interface, which allows for the complete abstraction of theview technology. You can integrate directly with template based rendering technologiessuch as JSP, Velocity and Freemarker, or directly generate XML, JSON, Atom, and manyother types of content. The modelMap is simply transformed into an appropriateformat, such as JSP request attributes, a Velocity template model.

22.1.1 Features of Spring Web MVC

Spring Web Flow

Spring Web Flow (SWF) aims to be the best solution for the management of web applicationpage flow.

SWF integrates with existing frameworks like Spring MVC and JSF, in both Servlet andPortlet environments. If you have a business process (or processes) that would benefitfrom a conversational model as opposed to a purely request model, then SWF may be thesolution.

SWF allows you to capture logical page flows as self-contained modules that are reusablein different situations, and as such is ideal for building web application modules thatguide the user through controlled navigations that drive business processes.

For more information about SWF, consult theSpring Web Flow website.

Spring’s web module includes many unique web support features:

  • Clear separation of roles. Each role — controller, validator, command object,form object, model object,DispatcherServlet, handler mapping, view resolver, and soon — can be fulfilled by a specialized object.
  • Powerful and straightforward configuration of both framework and application classesas JavaBeans. This configuration capability includes easy referencing acrosscontexts, such as from web controllers to business objects and validators.
  • Adaptability, non-intrusiveness, and flexibility. Define any controller methodsignature you need, possibly using one of the parameter annotations (such as@RequestParam, @RequestHeader, @PathVariable, and more) for a given scenario.
  • Reusable business code, no need for duplication. Use existing business objectsas command or form objects instead of mirroring them to extend a particular frameworkbase class.
  • Customizable binding and validation. Type mismatches as application-levelvalidation errors that keep the offending value, localized date and number binding,and so on instead of String-only form objects with manual parsing and conversion tobusiness objects.
  • Customizable handler mapping and view resolution. Handler mapping and viewresolution strategies range from simple URL-based configuration, to sophisticated,purpose-built resolution strategies. Spring is more flexible than web MVC frameworksthat mandate a particular technique.
  • Flexible model transfer. Model transfer with a name/valueMap supports easyintegration with any view technology.
  • Customizable locale, time zone and theme resolution, support for JSPs with or withoutSpring tag library, support for JSTL, support for Velocity without the need for extrabridges, and so on.
  • A simple yet powerful JSP tag library known as the Spring tag library that providessupport for features such as data binding and themes. The custom tags allow formaximum flexibility in terms of markup code. For information on the tag librarydescriptor, see the appendix entitledChapter 43,spring JSP Tag Library
  • A JSP form tag library, introduced in Spring 2.0, that makes writing forms in JSPpages much easier. For information on the tag library descriptor, see the appendixentitledChapter 44,spring-form JSP Tag Library
  • Beans whose lifecycle is scoped to the current HTTP request or HTTPSession.This is not a specific feature of Spring MVC itself, but rather of theWebApplicationContext container(s) that Spring MVC uses. These bean scopes aredescribed inSection 7.5.4, “Request, session, global session, application, and WebSocket scopes”

22.1.2 Pluggability of other MVC implementations

Non-Spring MVC implementations are preferable for some projects. Many teams expect toleverage their existing investment in skills and tools, for example with JSF.

If you do not want to use Spring’s Web MVC, but intend to leverage other solutions thatSpring offers, you can integrate the web MVC framework of your choice with Springeasily. Simply start up a Spring root application context through itsContextLoaderListener, and access it through itsServletContext attribute (orSpring’s respective helper method) from within any action object. No "plug-ins"are involved, so no dedicated integration is necessary. From the web layer’s point ofview, you simply use Spring as a library, with the root application context instance asthe entry point.

Your registered beans and Spring’s services can be at your fingertips even withoutSpring’s Web MVC. Spring does not compete with other web frameworks in this scenario.It simply addresses the many areas that the pure web MVC frameworks do not, from beanconfiguration to data access and transaction handling. So you can enrich yourapplication with a Spring middle tier and/or data access tier, even if you just wantto use, for example, the transaction abstraction with JDBC or Hibernate.

22.2 The DispatcherServlet

Spring’s web MVC framework is, like many other web MVC frameworks, request-driven,designed around a central Servlet that dispatches requests to controllers and offersother functionality that facilitates the development of web applications. Spring’sDispatcherServlet however, does more than just that. It is completely integrated withthe Spring IoC container and as such allows you to use every other feature that Springhas.

The request processing workflow of the Spring Web MVCDispatcherServlet is illustratedin the following diagram. The pattern-savvy reader will recognize that theDispatcherServlet is an expression of the "Front Controller" design pattern (this is apattern that Spring Web MVC shares with many other leading web frameworks).

Figure 22.1. The request processing workflow in Spring Web MVC (high level)

mvc

TheDispatcherServlet is an actualServlet (it inherits from theHttpServlet baseclass), and as such is declared in your web application. You need to map requests thatyou want theDispatcherServlet to handle, by using a URL mapping. Here is a standardJava EE Servlet configuration in a Servlet 3.0+ environment:

publicclass MyWebApplicationInitializerimplements WebApplicationInitializer {@Overridepublicvoid onStartup(ServletContext container) {        ServletRegistration.Dynamic registration = container.addServlet("example",new DispatcherServlet());        registration.setLoadOnStartup(1);        registration.addMapping("/example/*");    }}

In the preceding example, all requests starting with/example will be handled by theDispatcherServlet instance namedexample.

WebApplicationInitializer is an interface provided by Spring MVC that ensures yourcode-based configuration is detected and automatically used to initialize any Servlet 3container. An abstract base class implementation of this interface namedAbstractAnnotationConfigDispatcherServletInitializer makes it even easier to register theDispatcherServlet by simply specifying its servlet mapping and listing configurationclasses - it’s even the recommended way to set up your Spring MVC application.SeeCode-based Servlet container initialization for more details.

TheDispatcherServlet is an actualServlet (it inherits from theHttpServlet baseclass), and as such is declared in theweb.xml of your web application. You need tomap requests that you want theDispatcherServlet to handle, by using a URL mapping inthe sameweb.xml file. This is standard Java EE Servlet configuration; the followingexample shows such aDispatcherServlet declaration and mapping:

Below is theweb.xml equivalent of the above code based example:

<web-app><servlet><servlet-name>example</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>example</servlet-name><url-pattern>/example/*</url-pattern></servlet-mapping></web-app>

As detailed inSection 7.15, “Additional capabilities of the ApplicationContext”,ApplicationContext instances in Spring can bescoped. In the Web MVC framework, eachDispatcherServlet has its ownWebApplicationContext, which inherits all the beans already defined in the rootWebApplicationContext. The rootWebApplicationContext should contain all theinfrastructure beans that should be shared between your other contexts and Servletinstances. These inherited beans can be overridden in the servlet-specificscope, and you can define new scope-specific beans local to a given Servlet instance.

Figure 22.2. Typical context hierarchy in Spring Web MVC

mvc context hierarchy

Upon initialization of aDispatcherServlet, Spring MVC looks for a file named[servlet-name]-servlet.xml in theWEB-INF directory of your web application andcreates the beans defined there, overriding the definitions of any beans defined withthe same name in the global scope.

Consider the followingDispatcherServlet Servlet configuration (in theweb.xml file):

<web-app><servlet><servlet-name>golfing</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>golfing</servlet-name><url-pattern>/golfing/*</url-pattern></servlet-mapping></web-app>

With the above Servlet configuration in place, you will need to have a file called/WEB-INF/golfing-servlet.xml in your application; this file will contain all of yourSpring Web MVC-specific components (beans). You can change the exact location of thisconfiguration file through a Servlet initialization parameter (see below for details).

It is also possible to have just one root context for single DispatcherServlet scenarios.

Figure 22.3. Single root context in Spring Web MVC

mvc root context

This can be configured by setting an empty contextConfigLocation servlet init parameter,as shown below:

<web-app><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/root-context.xml</param-value></context-param><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value></param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/*</url-pattern></servlet-mapping><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener></web-app>

TheWebApplicationContext is an extension of the plainApplicationContext that hassome extra features necessary for web applications. It differs from a normalApplicationContext in that it is capable of resolving themes (seeSection 22.9, “Using themes”), and that it knows which Servlet it is associated with (by havinga link to theServletContext). TheWebApplicationContext is bound in theServletContext, and by using static methods on theRequestContextUtils class you canalways look up theWebApplicationContext if you need access to it.

Note that we can achieve the same with java-based configurations:

publicclass GolfingWebAppInitializerextends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {// GolfingAppConfig defines beans that would be in root-context.xmlreturnnew Class[] { GolfingAppConfig.class };    }@Overrideprotected Class<?>[] getServletConfigClasses() {// GolfingWebConfig defines beans that would be in golfing-servlet.xmlreturnnew Class[] { GolfingWebConfig.class };    }@Overrideprotected String[] getServletMappings() {returnnew String[] {"/golfing/*" };    }}

22.2.1 Special Bean Types In the WebApplicationContext

The SpringDispatcherServlet uses special beans to process requests and render theappropriate views. These beans are part of Spring MVC. You can choose which specialbeans to use by simply configuring one or more of them in theWebApplicationContext.However, you don’t need to do that initially since Spring MVC maintains a list ofdefault beans to use if you don’t configure any. More on that in the next section. Firstsee the table below listing the special bean types theDispatcherServlet relies on.

Table 22.1. Special bean types in the WebApplicationContext

Bean typeExplanation

HandlerMapping

Maps incoming requests to handlers and a list of pre- and post-processors (handler interceptors) based on some criteria the details of which vary byHandlerMapping implementation. The most popular implementation supports annotated controllers but other implementations exists as well.

HandlerAdapter

Helps theDispatcherServlet to invoke a handler mapped to a request regardless of the handler is actually invoked. For example, invoking an annotated controller requires resolving various annotations. Thus the main purpose of aHandlerAdapter is to shield theDispatcherServlet from such details.

HandlerExceptionResolver

Maps exceptions to views also allowing for more complex exception handling code.

ViewResolver

Resolves logical String-based view names to actualView types.

LocaleResolver &LocaleContextResolver

Resolves the locale a client is using and possibly their time zone, in order to be able to offer internationalized views

ThemeResolver

Resolves themes your web application can use, for example, to offer personalized layouts

MultipartResolver

Parses multi-part requests for example to support processing file uploads from HTML forms.

FlashMapManager

Stores and retrieves the "input" and the "output"FlashMap that can be used to pass attributes from one request to another, usually across a redirect.


22.2.2 Default DispatcherServlet Configuration

As mentioned in the previous section for each special bean theDispatcherServletmaintains a list of implementations to use by default. This information is kept in thefileDispatcherServlet.properties in the packageorg.springframework.web.servlet.

All special beans have some reasonable defaults of their own. Sooner or later thoughyou’ll need to customize one or more of the properties these beans provide. For exampleit’s quite common to configure anInternalResourceViewResolver settings itsprefixproperty to the parent location of view files.

Regardless of the details, the important concept to understand here is that onceyouconfigure a special bean such as anInternalResourceViewResolver in yourWebApplicationContext, you effectively override the list of default implementationsthat would have been used otherwise for that special bean type. For example if youconfigure anInternalResourceViewResolver, the default list ofViewResolverimplementations is ignored.

InSection 22.16, “Configuring Spring MVC” you’ll learn about other options for configuring Spring MVC includingMVC Java config and the MVC XML namespace both of which provide a simple starting pointand assume little knowledge of how Spring MVC works. Regardless of how you choose toconfigure your application, the concepts explained in this section are fundamentalshould be of help to you.

22.2.3 DispatcherServlet Processing Sequence

After you set up aDispatcherServlet, and a request comes in for that specificDispatcherServlet, theDispatcherServlet starts processing the request as follows:

  • TheWebApplicationContext is searched for and bound in the request as an attributethat the controller and other elements in the process can use. It is bound by defaultunder the keyDispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.
  • The locale resolver is bound to the request to enable elements in the process toresolve the locale to use when processing the request (rendering the view, preparingdata, and so on). If you do not need locale resolving, you do not need it.
  • The theme resolver is bound to the request to let elements such as views determinewhich theme to use. If you do not use themes, you can ignore it.
  • If you specify a multipart file resolver, the request is inspected for multiparts; ifmultiparts are found, the request is wrapped in aMultipartHttpServletRequest forfurther processing by other elements in the process. SeeSection 22.10, “Spring’s multipart (file upload) support” for furtherinformation about multipart handling.
  • An appropriate handler is searched for. If a handler is found, the execution chainassociated with the handler (preprocessors, postprocessors, and controllers) isexecuted in order to prepare a model or rendering.
  • If a model is returned, the view is rendered. If no model is returned, (may be due toa preprocessor or postprocessor intercepting the request, perhaps for securityreasons), no view is rendered, because the request could already have been fulfilled.

Handler exception resolvers that are declared in theWebApplicationContext pick upexceptions that are thrown during processing of the request. Using these exceptionresolvers allows you to define custom behaviors to address exceptions.

The SpringDispatcherServlet also supports the return of thelast-modification-date, as specified by the Servlet API. The process of determiningthe last modification date for a specific request is straightforward: theDispatcherServlet looks up an appropriate handler mapping and tests whether thehandler that is found implements theLastModified interface. If so, the value of thelong getLastModified(request) method of theLastModified interface is returned tothe client.

You can customize individualDispatcherServlet instances by adding Servletinitialization parameters (init-param elements) to the Servlet declaration in theweb.xml file. See the following table for the list of supported parameters.

Table 22.2. DispatcherServlet initialization parameters

ParameterExplanation

contextClass

Class that implementsWebApplicationContext, which instantiates the context used by this Servlet. By default, theXmlWebApplicationContext is used.

contextConfigLocation

String that is passed to the context instance (specified bycontextClass) to indicate where context(s) can be found. The string consists potentially of multiple strings (using a comma as a delimiter) to support multiple contexts. In case of multiple context locations with beans that are defined twice, the latest location takes precedence.

namespace

Namespace of theWebApplicationContext. Defaults to[servlet-name]-servlet.


22.3 Implementing Controllers

Controllers provide access to the application behavior that you typically define througha service interface. Controllers interpret user input and transform it into a model thatis represented to the user by the view. Spring implements a controller in a veryabstract way, which enables you to create a wide variety of controllers.

Spring 2.5 introduced an annotation-based programming model for MVC controllers thatuses annotations such as@RequestMapping,@RequestParam,@ModelAttribute, and soon. This annotation support is available for both Servlet MVC and Portlet MVC.Controllers implemented in this style do not have to extend specific base classes orimplement specific interfaces. Furthermore, they do not usually have direct dependencieson Servlet or Portlet APIs, although you can easily configure access to Servlet orPortlet facilities.

[Tip]Tip

Available in thespring-projects Org on Github,a number of web applications leverage the annotation support described in this sectionincludingMvcShowcase,MvcAjax,MvcBasic,PetClinic,PetCare,and others.

@Controllerpublicclass HelloWorldController {@RequestMapping("/helloWorld")public String helloWorld(Model model) {        model.addAttribute("message","Hello World!");return"helloWorld";    }}

As you can see, the@Controller and@RequestMapping annotations allow flexiblemethod names and signatures. In this particular example the method accepts aModel andreturns a view name as aString, but various other method parameters and return valuescan be used as explained later in this section.@Controller and@RequestMapping anda number of other annotations form the basis for the Spring MVC implementation. Thissection documents these annotations and how they are most commonly used in a Servletenvironment.

22.3.1 Defining a controller with @Controller

The@Controller annotation indicates that a particular class serves the role ofacontroller. Spring does not require you to extend any controller base class orreference the Servlet API. However, you can still reference Servlet-specific features ifyou need to.

The@Controller annotation acts as a stereotype for the annotated class, indicatingits role. The dispatcher scans such annotated classes for mapped methods and detects@RequestMapping annotations (see the next section).

You can define annotated controller beans explicitly, using a standard Spring beandefinition in the dispatcher’s context. However, the@Controller stereotype alsoallows for autodetection, aligned with Spring general support for detecting componentclasses in the classpath and auto-registering bean definitions for them.

To enable autodetection of such annotated controllers, you add component scanning toyour configuration. Use thespring-context schema as shown in the following XMLsnippet:

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context.xsd"><context:component-scanbase-package="org.springframework.samples.petclinic.web"/><!-- ... --></beans>

22.3.2 Mapping Requests With @RequestMapping

You use the@RequestMapping annotation to map URLs such as/appointments onto anentire class or a particular handler method. Typically the class-level annotation maps aspecific request path (or path pattern) onto a form controller, with additionalmethod-level annotations narrowing the primary mapping for a specific HTTP methodrequest method ("GET", "POST", etc.) or an HTTP request parameter condition.

The following example from thePetcare sample shows a controller in a Spring MVCapplication that uses this annotation:

@Controller@RequestMapping("/appointments")publicclass AppointmentsController {privatefinal AppointmentBook appointmentBook;@Autowiredpublic AppointmentsController(AppointmentBook appointmentBook) {this.appointmentBook = appointmentBook;    }@RequestMapping(method = RequestMethod.GET)public Map<String, Appointment> get() {return appointmentBook.getAppointmentsForToday();    }@RequestMapping(path = "/{day}", method = RequestMethod.GET)public Map<String, Appointment> getForDay(@PathVariable@DateTimeFormat(iso=ISO.DATE) Date day, Model model) {return appointmentBook.getAppointmentsForDay(day);    }@RequestMapping(path = "/new", method = RequestMethod.GET)public AppointmentForm getNewForm() {returnnew AppointmentForm();    }@RequestMapping(method = RequestMethod.POST)public String add(@Valid AppointmentForm appointment, BindingResult result) {if (result.hasErrors()) {return"appointments/new";        }        appointmentBook.addAppointment(appointment);return"redirect:/appointments";    }}

In the above example,@RequestMapping is used in a number of places. The first usage ison the type (class) level, which indicates that all handler methods in this controllerare relative to the/appointments path. Theget() method has a further@RequestMapping refinement: it only acceptsGET requests, meaning that an HTTPGET for/appointments invokes this method. Theadd() has a similar refinement, and thegetNewForm() combines the definition of HTTP method and path into one, so thatGETrequests forappointments/new are handled by that method.

ThegetForDay() method shows another usage of@RequestMapping: URI templates. (Seethe section called “URI Template Patterns”).

A@RequestMapping on the class level is not required. Without it, all paths are simplyabsolute, and not relative. The following example from thePetClinic sampleapplication shows a multi-action controller using@RequestMapping:

@Controllerpublicclass ClinicController {privatefinal Clinic clinic;@Autowiredpublic ClinicController(Clinic clinic) {this.clinic = clinic;    }@RequestMapping("/")publicvoid welcomeHandler() {    }@RequestMapping("/vets")public ModelMap vetsHandler() {returnnew ModelMap(this.clinic.getVets());    }}

The above example does not specifyGET vs.PUT,POST, and so forth, because@RequestMapping maps all HTTP methods by default. Use@RequestMapping(method=GET) or@GetMapping to narrow the mapping.

Composed @RequestMapping Variants

Spring Framework 4.3 introduces the following method-levelcomposed variants of the@RequestMapping annotation that help to simplify mappings for common HTTP methods andbetter express the semantics of the annotated handler method. For example, a@GetMapping can be read as aGET@RequestMapping.

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

The following example shows a modified version of theAppointmentsController from theprevious section that has been simplified withcomposed@RequestMapping annotations.

@Controller@RequestMapping("/appointments")publicclass AppointmentsController {privatefinal AppointmentBook appointmentBook;@Autowiredpublic AppointmentsController(AppointmentBook appointmentBook) {this.appointmentBook = appointmentBook;    }@GetMappingpublic Map<String, Appointment> get() {return appointmentBook.getAppointmentsForToday();    }@GetMapping("/{day}")public Map<String, Appointment> getForDay(@PathVariable@DateTimeFormat(iso=ISO.DATE) Date day, Model model) {return appointmentBook.getAppointmentsForDay(day);    }@GetMapping("/new")public AppointmentForm getNewForm() {returnnew AppointmentForm();    }@PostMappingpublic String add(@Valid AppointmentForm appointment, BindingResult result) {if (result.hasErrors()) {return"appointments/new";        }        appointmentBook.addAppointment(appointment);return"redirect:/appointments";    }}

@Controller and AOP Proxying

In some cases a controller may need to be decorated with an AOP proxy at runtime.One example is if you choose to have@Transactional annotations directly on thecontroller. When this is the case, for controllers specifically, we recommendusing class-based proxying. This is typically the default choice with controllers.However if a controller must implement an interface that is not a Spring Contextcallback (e.g.InitializingBean,*Aware, etc), you may need to explicitlyconfigure class-based proxying. For example with<tx:annotation-driven/>,change to<tx:annotation-driven proxy-target-class="true"/>.

New Support Classes for @RequestMapping methods in Spring MVC 3.1

Spring 3.1 introduced a new set of support classes for@RequestMapping methods calledRequestMappingHandlerMapping andRequestMappingHandlerAdapter respectively. They arerecommended for use and even required to take advantage of new features in Spring MVC3.1 and going forward. The new support classes are enabled by default by the MVCnamespace and the MVC Java config but must be configured explicitly if using neither.This section describes a few important differences between the old and the new supportclasses.

Prior to Spring 3.1, type and method-level request mappings were examined in twoseparate stages — a controller was selected first by theDefaultAnnotationHandlerMapping and the actual method to invoke was narrowed downsecond by theAnnotationMethodHandlerAdapter.

With the new support classes in Spring 3.1, theRequestMappingHandlerMapping is theonly place where a decision is made about which method should process the request. Thinkof controller methods as a collection of unique endpoints with mappings for each methodderived from type and method-level@RequestMapping information.

This enables some new possibilities. For once aHandlerInterceptor or aHandlerExceptionResolver can now expect the Object-based handler to be aHandlerMethod, which allows them to examine the exact method, its parameters andassociated annotations. The processing for a URL no longer needs to be split acrossdifferent controllers.

There are also several things no longer possible:

  • Select a controller first with aSimpleUrlHandlerMapping orBeanNameUrlHandlerMapping and then narrow the method based on@RequestMappingannotations.
  • Rely on method names as a fall-back mechanism to disambiguate between two@RequestMapping methods that don’t have an explicit path mapping URL path butotherwise match equally, e.g. by HTTP method. In the new support classes@RequestMapping methods have to be mapped uniquely.
  • Have a single default method (without an explicit path mapping) with which requestsare processed if no other controller method matches more concretely. In the newsupport classes if a matching method is not found a 404 error is raised.

The above features are still supported with the existing support classes. However totake advantage of new Spring MVC 3.1 features you’ll need to use the new support classes.

URI Template Patterns

URI templates can be used for convenient access to selected parts of a URL in a@RequestMapping method.

A URI Template is a URI-like string, containing one or more variable names. When yousubstitute values for these variables, the template becomes a URI. Theproposed RFC for URI Templates defineshow a URI is parameterized. For example, the URI Templatehttp://www.example.com/users/{userId} contains the variableuserId. Assigning thevaluefred to the variable yieldshttp://www.example.com/users/fred.

In Spring MVC you can use the@PathVariable annotation on a method argument to bind itto the value of a URI template variable:

@GetMapping("/owners/{ownerId}")public String findOwner(@PathVariable String ownerId, Model model) {    Owner owner = ownerService.findOwner(ownerId);    model.addAttribute("owner", owner);return"displayOwner";}

The URI Template "/owners/{ownerId}`" specifies the variable name `ownerId. When thecontroller handles this request, the value ofownerId is set to the value found in theappropriate part of the URI. For example, when a request comes in for/owners/fred,the value ofownerId isfred.

[Tip]Tip

To process the @PathVariable annotation, Spring MVC needs to find the matching URItemplate variable by name. You can specify it in the annotation:

@GetMapping("/owners/{ownerId}")public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {// implementation omitted}

Or if the URI template variable name matches the method argument name you can omit thatdetail. As long as your code is compiled with debugging information or the-parameterscompiler flag on Java 8, Spring MVC will match the method argument name to the URItemplate variable name:

@GetMapping("/owners/{ownerId}")public String findOwner(@PathVariable String ownerId, Model model) {// implementation omitted}

A method can have any number of@PathVariable annotations:

@GetMapping("/owners/{ownerId}/pets/{petId}")public String findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {    Owner owner = ownerService.findOwner(ownerId);    Pet pet = owner.getPet(petId);    model.addAttribute("pet", pet);return"displayPet";}

When a@PathVariable annotation is used on aMap<String, String> argument, the mapis populated with all URI template variables.

A URI template can be assembled from type and method level@RequestMappingannotations. As a result thefindPet() method can be invoked with a URL such as/owners/42/pets/21.

@Controller@RequestMapping("/owners/{ownerId}")publicclass RelativePathUriTemplateController {    @RequestMapping("/pets/{petId}")publicvoid findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {// implementation omitted    }}

A@PathVariable argument can be ofany simple type such asint,long,Date, etc.Spring automatically converts to the appropriate type or throws aTypeMismatchException if it fails to do so. You can also register support for parsingadditional data types. Seethe section called “Method Parameters And Type Conversion” andthe section called “Customizing WebDataBinder initialization”.

URI Template Patterns with Regular Expressions

Sometimes you need more precision in defining URI template variables. Consider the URL"/spring-web/spring-web-3.0.5.jar". How do you break it down into multiple parts?

The@RequestMapping annotation supports the use of regular expressions in URI templatevariables. The syntax is{varName:regex} where the first part defines the variablename and the second - the regular expression. For example:

@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")publicvoid handle(@PathVariable String version,@PathVariable String extension) {// ...}

Path Patterns

In addition to URI templates, the@RequestMapping annotation and allcomposed@RequestMapping variants also support Ant-style path patterns (for example,/myPath/*.do). A combination of URI template variables and Ant-style globs isalso supported (e.g./owners/*/pets/{petId}).

Path Pattern Comparison

When a URL matches multiple patterns, a sort is used to find the most specific match.

A pattern with a lower count of URI variables and wild cards is considered more specific.For example/hotels/{hotel}/* has 1 URI variable and 1 wild card and is consideredmore specific than/hotels/{hotel}/** which as 1 URI variable and 2 wild cards.

If two patterns have the same count, the one that is longer is considered more specific.For example/foo/bar* is longer and considered more specific than/foo/*.

When two patterns have the same count and length, the pattern with fewer wild cards is considered more specific.For example/hotels/{hotel} is more specific than/hotels/*.

There are also some additional special rules:

  • Thedefault mapping pattern/** is less specific than any other pattern.For example/api/{a}/{b}/{c} is more specific.
  • Aprefix pattern such as/public/** is less specific than any other pattern that doesn’t contain double wildcards.For example/public/path3/{a}/{b}/{c} is more specific.

For the full details seeAntPatternComparator inAntPathMatcher. Note that the PathMatchercan be customized (seeSection 22.16.11, “Path Matching” in the section on configuring Spring MVC).

Path Patterns with Placeholders

Patterns in@RequestMapping annotations support${…​} placeholders against localproperties and/or system properties and environment variables. This may be useful incases where the path a controller is mapped to may need to be customized throughconfiguration. For more information on placeholders, see the javadocs of thePropertyPlaceholderConfigurer class.

Suffix Pattern Matching

By default Spring MVC performs".*" suffix pattern matching so that acontroller mapped to/person is also implicitly mapped to/person.*.This makes it easy to request different representations of a resource through theURL path (e.g./person.pdf,/person.xml).

Suffix pattern matching can be turned off or restricted to a set of path extensionsexplicitly registered for content negotiation purposes. This is generallyrecommended to minimize ambiguity with common request mappings such as/person/{id} where a dot might not represent a file extension, e.g./person/[email protected] vs/person/[email protected]. Furthermore as explainedin the note below suffix pattern matching as well as content negotiation may beused in some circumstances to attempt malicious attacks and there are goodreasons to restrict them meaningfully.

SeeSection 22.16.11, “Path Matching” for suffix pattern matching configuration andalsoSection 22.16.6, “Content Negotiation” for content negotiation configuration.

Suffix Pattern Matching and RFD

Reflected file download (RFD) attack was first described in apaper by Trustwavein 2014. The attack is similar to XSS in that it relies on input(e.g. query parameter, URI variable) being reflected in the response.However instead of inserting JavaScript into HTML, an RFD attack relies on thebrowser switching to perform a download and treating the response as an executablescript if double-clicked based on the file extension (e.g. .bat, .cmd).

In Spring MVC@ResponseBody andResponseEntity methods are at risk becausethey can render different content types which clients can request includingvia URL path extensions. Note however that neither disabling suffix pattern matchingnor disabling the use of path extensions for content negotiation purposes aloneare effective at preventing RFD attacks.

For comprehensive protection against RFD, prior to rendering the response bodySpring MVC adds aContent-Disposition:inline;filename=f.txt header tosuggest a fixed and safe download file filename. This is done only if the URLpath contains a file extension that is neither whitelisted nor explicitlyregistered for content negotiation purposes. However it may potentially haveside effects when URLs are typed directly into a browser.

Many common path extensions are whitelisted bydefault. Furthermore REST API calls are typically not meant to be used as URLsdirectly in browsers. Nevertheless applications that use customHttpMessageConverter implementations can explicitly register file extensionsfor content negotiation and the Content-Disposition header will not be addedfor such extensions. SeeSection 22.16.6, “Content Negotiation”.

[Note]Note

This was originally introduced as part of work forCVE-2015-5211.Below are additional recommendations from the report:

  • Encode rather than escape JSON responses. This is also an OWASP XSS recommendation.For an example of how to do that with Spring seespring-jackson-owasp.
  • Configure suffix pattern matching to be turned off or restricted to explicitlyregistered suffixes only.
  • Configure content negotiation with the properties "useJaf" and "ignoreUnknownPathExtensions"set to false which would result in a 406 response for URLs with unknown extensions.Note however that this may not be an option if URLs are naturally expected to havea dot towards the end.
  • AddX-Content-Type-Options: nosniff header to responses. Spring Security 4 doesthis by default.

Matrix Variables

The URI specificationRFC 3986 definesthe possibility of including name-value pairs within path segments. There is no specificterm used in the spec. The general "URI path parameters" could be applied although themore unique"Matrix URIs", originatingfrom an old post by Tim Berners-Lee, is also frequently used and fairly well known.Within Spring MVC these are referred to as matrix variables.

Matrix variables can appear in any path segment, each matrix variable separated with a";" (semicolon). For example:"/cars;color=red;year=2012". Multiple values may beeither "," (comma) separated"color=red,green,blue" or the variable name may berepeated"color=red;color=green;color=blue".

If a URL is expected to contain matrix variables, the request mapping pattern mustrepresent them with a URI template. This ensures the request can be matched correctlyregardless of whether matrix variables are present or not and in what order they areprovided.

Below is an example of extracting the matrix variable "q":

// GET /pets/42;q=11;r=22@GetMapping("/pets/{petId}")publicvoid findPet(@PathVariable String petId,@MatrixVariableint q) {// petId == 42// q == 11}

Since all path segments may contain matrix variables, in some cases you need to be morespecific to identify where the variable is expected to be:

// GET /owners/42;q=11/pets/21;q=22@GetMapping("/owners/{ownerId}/pets/{petId}")publicvoid findPet(@MatrixVariable(name="q", pathVar="ownerId")int q1,@MatrixVariable(name="q", pathVar="petId")int q2) {// q1 == 11// q2 == 22}

A matrix variable may be defined as optional and a default value specified:

// GET /pets/42@GetMapping("/pets/{petId}")publicvoid findPet(@MatrixVariable(required=false, defaultValue="1")int q) {// q == 1}

All matrix variables may be obtained in a Map:

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23@GetMapping("/owners/{ownerId}/pets/{petId}")publicvoid findPet(@MatrixVariable MultiValueMap<String, String> matrixVars,@MatrixVariable(pathVar="petId"") MultiValueMap<String, String> petMatrixVars) {// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]// petMatrixVars: ["q" : 11, "s" : 23]}

Note that to enable the use of matrix variables, you must set theremoveSemicolonContent property ofRequestMappingHandlerMapping tofalse. Bydefault it is set totrue.

[Tip]Tip

The MVC Java config and the MVC namespace both provide options for enabling the use ofmatrix variables.

If you are using Java config, TheAdvanced Customizationswith MVC Java Config section describes how theRequestMappingHandlerMapping canbe customized.

In the MVC namespace, the<mvc:annotation-driven> element has anenable-matrix-variables attribute that should be set totrue. By default it is settofalse.

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc.xsd"><mvc:annotation-drivenenable-matrix-variables="true"/></beans>

Consumable Media Types

You can narrow the primary mapping by specifying a list of consumable media types. Therequest will be matched only if theContent-Type request header matches the specifiedmedia type. For example:

@PostMapping(path ="/pets",consumes = "application/json")publicvoid addPet(@RequestBody Pet pet, Model model) {// implementation omitted}

Consumable media type expressions can also be negated as in!text/plain to match toall requests other than those withContent-Type oftext/plain. Also considerusing constants provided inMediaType such asAPPLICATION_JSON_VALUE andAPPLICATION_JSON_UTF8_VALUE.

[Tip]Tip

Theconsumes condition is supported on the type and on the method level. Unlike mostother conditions, when used at the type level, method-level consumable types overriderather than extend type-level consumable types.

Producible Media Types

You can narrow the primary mapping by specifying a list of producible media types. Therequest will be matched only if theAccept request header matches one of thesevalues. Furthermore, use of theproduces condition ensures the actual content typeused to generate the response respects the media types specified in theproducescondition. For example:

@GetMapping(path ="/pets/{petId}",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic Pet getPet(@PathVariable String petId, Model model) {// implementation omitted}
[Note]Note

Be aware that the media type specified in theproduces condition can also optionallyspecify a character set. For example, in the code snippet above we specify the same mediatype than the default one configured inMappingJackson2HttpMessageConverter, includingtheUTF-8 charset.

Just like withconsumes, producible media type expressions can be negated as in!text/plain to match to all requests other than those with anAccept headervalue oftext/plain. Also consider using constants provided inMediaType suchasAPPLICATION_JSON_VALUE andAPPLICATION_JSON_UTF8_VALUE.

[Tip]Tip

Theproduces condition is supported on the type and on the method level. Unlike mostother conditions, when used at the type level, method-level producible types overriderather than extend type-level producible types.

Request Parameters and Header Values

You can narrow request matching through request parameter conditions such as"myParam","!myParam", or"myParam=myValue". The first two test for requestparameter presence/absence and the third for a specific parameter value. Here is anexample with a request parameter value condition:

@Controller@RequestMapping("/owners/{ownerId}")publicclass RelativePathUriTemplateController {    @GetMapping(path ="/pets/{petId}",params = "myParam=myValue")publicvoid findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {// implementation omitted    }}

The same can be done to test for request header presence/absence or to match based on aspecific request header value:

@Controller@RequestMapping("/owners/{ownerId}")publicclass RelativePathUriTemplateController {    @GetMapping(path ="/pets",headers = "myHeader=myValue")publicvoid findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {// implementation omitted    }}
[Tip]Tip

Although you can match toContent-Type andAccept header values using media typewild cards (for example"content-type=text/*" will match to"text/plain" and"text/html"), it is recommended to use theconsumes andproduces conditionsrespectively instead. They are intended specifically for that purpose.

HTTP HEAD and HTTP OPTIONS

@RequestMapping methods mapped to "GET" are also implicitly mapped to "HEAD",i.e. there is no need to have "HEAD" explicitly declared. An HTTP HEAD requestis processed as if it were an HTTP GET except instead of writing the body onlythe number of bytes are counted and the "Content-Length" header set.

@RequestMapping methods have built-in support for HTTP OPTIONS. By default anHTTP OPTIONS request is handled by setting the "Allow" response header to theHTTP methods explicitly declared on all@RequestMapping methods with matchingURL patterns. When no HTTP methods are explicitly declared the "Allow" headeris set to "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS". Ideally always declare theHTTP method(s) that an@RequestMapping method is intended to handle, or alternativelyuse one of the dedicatedcomposed@RequestMapping variants (seethe section called “Composed @RequestMapping Variants”).

Although not necessary an@RequestMapping method can be mapped to and handleeither HTTP HEAD or HTTP OPTIONS, or both.

22.3.3 Defining @RequestMapping handler methods

@RequestMapping handler methods can have very flexible signatures. The supportedmethod arguments and return values are described in the following section. Mostarguments can be used in arbitrary order with the only exception beingBindingResultarguments. This is described in the next section.

[Note]Note

Spring 3.1 introduced a new set of support classes for@RequestMapping methods calledRequestMappingHandlerMapping andRequestMappingHandlerAdapter respectively. They arerecommended for use and even required to take advantage of new features in Spring MVC3.1 and going forward. The new support classes are enabled by default from the MVCnamespace and with use of the MVC Java config but must be configured explicitly if usingneither.

Supported method argument types

The following are the supported method arguments:

  • Request or response objects (Servlet API). Choose any specific request or responsetype, for exampleServletRequest orHttpServletRequest.
  • Session object (Servlet API): of typeHttpSession. An argument of this type enforcesthe presence of a corresponding session. As a consequence, such an argument is nevernull.
[Note]Note

Session access may not be thread-safe, in particular in a Servlet environment. Considersetting theRequestMappingHandlerAdapter's "synchronizeOnSession" flag to "true" ifmultiple requests are allowed to access a session concurrently.

  • org.springframework.web.context.request.WebRequest ororg.springframework.web.context.request.NativeWebRequest. Allows for genericrequest parameter access as well as request/session attribute access, without tiesto the native Servlet/Portlet API.
  • java.util.Locale for the current request locale, determined by the most specificlocale resolver available, in effect, the configuredLocaleResolver /LocaleContextResolver in an MVC environment.
  • java.util.TimeZone (Java 6+) /java.time.ZoneId (on Java 8) for the time zoneassociated with the current request, as determined by aLocaleContextResolver.
  • java.io.InputStream /java.io.Reader for access to the request’s content.This value is the raw InputStream/Reader as exposed by the Servlet API.
  • java.io.OutputStream /java.io.Writer for generating the response’s content.This value is the raw OutputStream/Writer as exposed by the Servlet API.
  • org.springframework.http.HttpMethod for the HTTP request method.
  • java.security.Principal containing the currently authenticated user.
  • @PathVariable annotated parameters for access to URI template variables. Seethe section called “URI Template Patterns”.
  • @MatrixVariable annotated parameters for access to name-value pairs located inURI path segments. Seethe section called “Matrix Variables”.
  • @RequestParam annotated parameters for access to specific Servlet requestparameters. Parameter values are converted to the declared method argument type.Seethe section called “Binding request parameters to method parameters with @RequestParam”.
  • @RequestHeader annotated parameters for access to specific Servlet request HTTPheaders. Parameter values are converted to the declared method argument type.Seethe section called “Mapping request header attributes with the @RequestHeader annotation”.
  • @RequestBody annotated parameters for access to the HTTP request body. Parametervalues are converted to the declared method argument type usingHttpMessageConverters. Seethe section called “Mapping the request body with the @RequestBody annotation”.
  • @RequestPart annotated parameters for access to the content of a"multipart/form-data" request part. SeeSection 22.10.5, “Handling a file upload request from programmatic clients” andSection 22.10, “Spring’s multipart (file upload) support”.
  • @SessionAttribute annotated parameters for access to existing, permanentsession attributes (e.g. user authentication object) as opposed to modelattributes temporarily stored in the session as part of a controller workflowvia@SessionAttributes.
  • @RequestAttribute annotated parameters for access to request attributes.
  • HttpEntity<?> parameters for access to the Servlet request HTTP headers andcontents. The request stream will be converted to the entity body usingHttpMessageConverters. Seethe section called “Using HttpEntity”.
  • java.util.Map /org.springframework.ui.Model /org.springframework.ui.ModelMapfor enriching the implicit model that is exposed to the web view.
  • org.springframework.web.servlet.mvc.support.RedirectAttributes to specify the exactset of attributes to use in case of a redirect and also to add flash attributes(attributes stored temporarily on the server-side to make them available to therequest after the redirect). Seethe section called “Passing Data To the Redirect Target” andSection 22.6, “Using flash attributes”.
  • Command or form objects to bind request parameters to bean properties (via setters)or directly to fields, with customizable type conversion, depending on@InitBindermethods and/or the HandlerAdapter configuration. See thewebBindingInitializerproperty onRequestMappingHandlerAdapter. Such command objects along with theirvalidation results will be exposed as model attributes by default, using the commandclass name - e.g. model attribute "orderAddress" for a command object of type"some.package.OrderAddress". TheModelAttribute annotation can be used on a methodargument to customize the model attribute name used.
  • org.springframework.validation.Errors /org.springframework.validation.BindingResult validation results for a precedingcommand or form object (the immediately preceding method argument).
  • org.springframework.web.bind.support.SessionStatus status handle for marking formprocessing as complete, which triggers the cleanup of session attributes that havebeen indicated by the@SessionAttributes annotation at the handler type level.
  • org.springframework.web.util.UriComponentsBuilder a builder for preparing a URLrelative to the current request’s host, port, scheme, context path, and the literalpart of the servlet mapping.

TheErrors orBindingResult parameters have to follow the model object that is beingbound immediately as the method signature might have more than one model object andSpring will create a separateBindingResult instance for each of them so the followingsample won’t work:

Invalid ordering of BindingResult and @ModelAttribute. 

@PostMappingpublic String processSubmit(@ModelAttribute("pet") Pet pet, Model model,BindingResult result) { ... }

Note, that there is aModel parameter in betweenPet andBindingResult. To getthis working you have to reorder the parameters as follows:

@PostMappingpublic String processSubmit(@ModelAttribute("pet") Pet pet,BindingResult result, Model model) { ... }
[Note]Note

JDK 1.8’sjava.util.Optional is supported as a method parameter type with annotationsthat have arequired attribute (e.g.@RequestParam,@RequestHeader, etc. The useofjava.util.Optional in those cases is equivalent to havingrequired=false.

Supported method return types

The following are the supported return types:

  • AModelAndView object, with the model implicitly enriched with command objects andthe results of@ModelAttribute annotated reference data accessor methods.
  • AModel object, with the view name implicitly determined through aRequestToViewNameTranslator and the model implicitly enriched with command objectsand the results of@ModelAttribute annotated reference data accessor methods.
  • AMap object for exposing a model, with the view name implicitly determined throughaRequestToViewNameTranslator and the model implicitly enriched with command objectsand the results of@ModelAttribute annotated reference data accessor methods.
  • AView object, with the model implicitly determined through command objects and@ModelAttribute annotated reference data accessor methods. The handler method mayalso programmatically enrich the model by declaring aModel argument (see above).
  • AString value that is interpreted as the logical view name, with the modelimplicitly determined through command objects and@ModelAttribute annotatedreference data accessor methods. The handler method may also programmatically enrichthe model by declaring aModel argument (see above).
  • void if the method handles the response itself (by writing the response contentdirectly, declaring an argument of typeServletResponse /HttpServletResponse forthat purpose) or if the view name is supposed to be implicitly determined through aRequestToViewNameTranslator (not declaring a response argument in the handler methodsignature).
  • If the method is annotated with@ResponseBody, the return type is written to theresponse HTTP body. The return value will be converted to the declared method argumenttype usingHttpMessageConverters. Seethe section called “Mapping the response body with the @ResponseBody annotation”.
  • AnHttpEntity<?> orResponseEntity<?> object to provide access to the Servletresponse HTTP headers and contents. The entity body will be converted to the responsestream usingHttpMessageConverters. Seethe section called “Using HttpEntity”.
  • AnHttpHeaders object to return a response with no body.
  • ACallable<?> can be returned when the application wants to produce the return valueasynchronously in a thread managed by Spring MVC.
  • ADeferredResult<?> can be returned when the application wants to produce the returnvalue from a thread of its own choosing.
  • AListenableFuture<?> orCompletableFuture<?>/CompletionStage<?> can be returnedwhen the application wants to produce the value from a thread pool submission.
  • AResponseBodyEmitter can be returned to write multiple objects to the responseasynchronously; also supported as the body within aResponseEntity.
  • AnSseEmitter can be returned to write Server-Sent Events to the responseasynchronously; also supported as the body within aResponseEntity.
  • AStreamingResponseBody can be returned to write to the response OutputStreamasynchronously; also supported as the body within aResponseEntity.
  • Any other return type is considered to be a single model attribute to be exposed tothe view, using the attribute name specified through@ModelAttribute at the methodlevel (or the default attribute name based on the return type class name). The modelis implicitly enriched with command objects and the results of@ModelAttributeannotated reference data accessor methods.

Binding request parameters to method parameters with @RequestParam

Use the@RequestParam annotation to bind request parameters to a method parameter inyour controller.

The following code snippet shows the usage:

@Controller@RequestMapping("/pets")@SessionAttributes("pet")publicclass EditPetForm {// ...@GetMappingpublic String setupForm(@RequestParam("petId") int petId, ModelMap model) {        Pet pet =this.clinic.loadPet(petId);        model.addAttribute("pet", pet);return"petForm";    }// ...}

Parameters using this annotation are required by default, but you can specify that aparameter is optional by setting@RequestParam'srequired attribute tofalse(e.g.,@RequestParam(name="id", required=false)).

Type conversion is applied automatically if the target method parameter type is notString. Seethe section called “Method Parameters And Type Conversion”.

When an@RequestParam annotation is used on aMap<String, String> orMultiValueMap<String, String> argument, the map is populated with all requestparameters.

Mapping the request body with the @RequestBody annotation

The@RequestBody method parameter annotation indicates that a method parameter shouldbe bound to the value of the HTTP request body. For example:

@PutMapping("/something")publicvoid handle(@RequestBody String body, Writer writer)throws IOException {    writer.write(body);}

You convert the request body to the method argument by using anHttpMessageConverter.HttpMessageConverter is responsible for converting from the HTTP request message to anobject and converting from an object to the HTTP response body. TheRequestMappingHandlerAdapter supports the@RequestBody annotation with the followingdefaultHttpMessageConverters:

  • ByteArrayHttpMessageConverter converts byte arrays.
  • StringHttpMessageConverter converts strings.
  • FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
  • SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.

For more information on these converters, seeMessageConverters. Also note that if using the MVC namespace or the MVC Java config, a widerrange of message converters are registered by default. SeeSection 22.16.1, “Enabling the MVC Java Config or the MVC XML Namespace” for more information.

If you intend to read and write XML, you will need to configure theMarshallingHttpMessageConverter with a specificMarshaller and anUnmarshallerimplementation from theorg.springframework.oxm package. The example below shows howto do that directly in your configuration but if your application is configured throughthe MVC namespace or the MVC Java config seeSection 22.16.1, “Enabling the MVC Java Config or the MVC XML Namespace” instead.

<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><propertyname="messageConverters"><util:listid="beanList"><refbean="stringHttpMessageConverter"/><refbean="marshallingHttpMessageConverter"/></util:list></property</bean><beanid="stringHttpMessageConverter"class="org.springframework.http.converter.StringHttpMessageConverter"/><beanid="marshallingHttpMessageConverter"class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"><propertyname="marshaller"ref="castorMarshaller"/><propertyname="unmarshaller"ref="castorMarshaller"/></bean><beanid="castorMarshaller"class="org.springframework.oxm.castor.CastorMarshaller"/>

An@RequestBody method parameter can be annotated with@Valid, in which case it willbe validated using the configuredValidator instance. When using the MVC namespace orthe MVC Java config, a JSR-303 validator is configured automatically assuming a JSR-303implementation is available on the classpath.

Just like with@ModelAttribute parameters, anErrors argument can be used to examinethe errors. If such an argument is not declared, aMethodArgumentNotValidExceptionwill be raised. The exception is handled in theDefaultHandlerExceptionResolver, whichsends a400 error back to the client.

[Note]Note

Also seeSection 22.16.1, “Enabling the MVC Java Config or the MVC XML Namespace” forinformation on configuring message converters and a validator through the MVC namespaceor the MVC Java config.

Mapping the response body with the @ResponseBody annotation

The@ResponseBody annotation is similar to@RequestBody. This annotation can be placedon a method and indicates that the return type should be written straight to the HTTPresponse body (and not placed in a Model, or interpreted as a view name). For example:

@GetMapping("/something")@ResponseBodypublic String helloWorld() {return"Hello World";}

The above example will result in the textHello World being written to the HTTPresponse stream.

As with@RequestBody, Spring converts the returned object to a response body by usinganHttpMessageConverter. For more information on these converters, see the previoussection andMessage Converters.

Creating REST Controllers with the @RestController annotation

It’s a very common use case to have Controllers implement a REST API, thus serving onlyJSON, XML or custom MediaType content. For convenience, instead of annotating all your@RequestMapping methods with@ResponseBody, you can annotate your controller Classwith@RestController.

@RestControlleris a stereotype annotation that combines@ResponseBody and@Controller. More thanthat, it gives more meaning to your Controller and also may carry additional semanticsin future releases of the framework.

As with regular@Controllers, a@RestController may be assisted by@ControllerAdvice or@RestControllerAdvice beans. See thethe section called “Advising controllers with @ControllerAdvice and @RestControllerAdvice”section for more details.

Using HttpEntity

TheHttpEntity is similar to@RequestBody and@ResponseBody. Besides gettingaccess to the request and response body,HttpEntity (and the response-specificsubclassResponseEntity) also allows access to the request and response headers, likeso:

@RequestMapping("/something")public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity)throws UnsupportedEncodingException {    String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));byte[] requestBody = requestEntity.getBody();// do something with request header and body    HttpHeaders responseHeaders =new HttpHeaders();    responseHeaders.set("MyResponseHeader","MyValue");returnnew ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);}

The above example gets the value of theMyRequestHeader request header, and reads thebody as a byte array. It adds theMyResponseHeader to the response, writesHelloWorld to the response stream, and sets the response status code to 201 (Created).

As with@RequestBody and@ResponseBody, Spring usesHttpMessageConverter toconvert from and to the request and response streams. For more information on theseconverters, see the previous section andMessage Converters.

Using @ModelAttribute on a method

The@ModelAttribute annotation can be used on methods or on method arguments. Thissection explains its usage on methods while the next section explains its usage onmethod arguments.

An@ModelAttribute on a method indicates the purpose of that method is to add one ormore model attributes. Such methods support the same argument types as@RequestMappingmethods but cannot be mapped directly to requests. Instead@ModelAttribute methods ina controller are invoked before@RequestMapping methods, within the same controller. Acouple of examples:

// Add one attribute// The return value of the method is added to the model under the name "account"// You can customize the name via @ModelAttribute("myAccount")@ModelAttributepublic Account addAccount(@RequestParam String number) {return accountManager.findAccount(number);}// Add multiple attributes@ModelAttributepublicvoid populateModel(@RequestParam String number, Model model) {    model.addAttribute(accountManager.findAccount(number));// add more ...}

@ModelAttribute methods are used to populate the model with commonly needed attributesfor example to fill a drop-down with states or with pet types, or to retrieve a commandobject like Account in order to use it to represent the data on an HTML form. The lattercase is further discussed in the next section.

Note the two styles of@ModelAttribute methods. In the first, the method adds anattribute implicitly by returning it. In the second, the method accepts aModel andadds any number of model attributes to it. You can choose between the two stylesdepending on your needs.

A controller can have any number of@ModelAttribute methods. All such methods areinvoked before@RequestMapping methods of the same controller.

@ModelAttribute methods can also be defined in an@ControllerAdvice-annotated classand such methods apply to many controllers. See thethe section called “Advising controllers with @ControllerAdvice and @RestControllerAdvice” sectionfor more details.

[Tip]Tip

What happens when a model attribute name is not explicitly specified? In such cases adefault name is assigned to the model attribute based on its type. For example if themethod returns an object of typeAccount, the default name used is "account". You canchange that through the value of the@ModelAttribute annotation. If adding attributesdirectly to theModel, use the appropriate overloadedaddAttribute(..) method -i.e., with or without an attribute name.

The@ModelAttribute annotation can be used on@RequestMapping methods as well. Inthat case the return value of the@RequestMapping method is interpreted as a modelattribute rather than as a view name. The view name is then derived based on view nameconventions instead, much like for methods returningvoid — seeSection 22.13.3, “Default view name”.

Using @ModelAttribute on a method argument

As explained in the previous section@ModelAttribute can be used on methods or onmethod arguments. This section explains its usage on method arguments.

An@ModelAttribute on a method argument indicates the argument should be retrievedfrom the model. If not present in the model, the argument should be instantiated firstand then added to the model. Once present in the model, the argument’s fields should bepopulated from all request parameters that have matching names. This is known as databinding in Spring MVC, a very useful mechanism that saves you from having to parse eachform field individually.

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")public String processSubmit(@ModelAttribute Pet pet) { }

Given the above example where can the Pet instance come from? There are several options:

An@ModelAttribute method is a common way to retrieve an attribute from thedatabase, which may optionally be stored between requests through the use of@SessionAttributes. In some cases it may be convenient to retrieve the attribute byusing an URI template variable and a type converter. Here is an example:

@PutMapping("/accounts/{account}")public String save(@ModelAttribute("account") Account account) {// ...}

In this example the name of the model attribute (i.e. "account") matches the name of aURI template variable. If you registerConverter<String, Account> that can turn theString account value into anAccount instance, then the above example will workwithout the need for an@ModelAttribute method.

The next step is data binding. TheWebDataBinder class matches request parameter names — including query string parameters and form fields — to model attribute fields byname. Matching fields are populated after type conversion (from String to the targetfield type) has been applied where necessary. Data binding and validation are covered inChapter 9,Validation, Data Binding, and Type Conversion. Customizing the data binding process for a controller level is coveredinthe section called “Customizing WebDataBinder initialization”.

As a result of data binding there may be errors such as missing required fields or typeconversion errors. To check for such errors add aBindingResult argument immediatelyfollowing the@ModelAttribute argument:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {if (result.hasErrors()) {return"petForm";    }// ...}

With aBindingResult you can check if errors were found in which case it’s common torender the same form where the errors can be shown with the help of Spring’s<errors>form tag.

Note that in some cases it may be useful to gain access to an attribute in themodel without data binding. For such cases you may inject theModel into thecontroller or alternatively use thebinding flag on the annotation:

@ModelAttributepublic AccountForm setUpForm() {returnnew AccountForm();}@ModelAttributepublic Account findAccount(@PathVariable String accountId) {return accountRepository.findOne(accountId);}@PostMapping("update")public String update(@Valid AccountUpdateForm form, BindingResult result,@ModelAttribute(binding=false) Account account) {// ...}

In addition to data binding you can also invoke validation using your own customvalidator passing the sameBindingResult that was used to record data binding errors.That allows for data binding and validation errors to be accumulated in one place andsubsequently reported back to the user:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {new PetValidator().validate(pet, result);if (result.hasErrors()) {return"petForm";    }// ...}

Or you can have validation invoked automatically by adding the JSR-303@Validannotation:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {if (result.hasErrors()) {return"petForm";    }// ...}

SeeSection 9.8, “Spring Validation” andChapter 9,Validation, Data Binding, and Type Conversion for details on how to configure anduse validation.

Using @SessionAttributes to store model attributes in the HTTP session between requests

The type-level@SessionAttributes annotation declares session attributes used by aspecific handler. This will typically list the names of model attributes or types ofmodel attributes which should be transparently stored in the session or someconversational storage, serving as form-backing beans between subsequent requests.

The following code snippet shows the usage of this annotation, specifying the modelattribute name:

@Controller@RequestMapping("/editPet.do")@SessionAttributes("pet")publicclass EditPetForm {// ...}

Using @SessionAttribute to access pre-existing global session attributes

If you need access to pre-existing session attributes that are managed globally,i.e. outside the controller (e.g. by a filter), and may or may not be presentuse the@SessionAttribute annotation on a method parameter:

@RequestMapping("/")public String handle(@SessionAttribute User user) {// ...}

For use cases that require adding or removing session attributes consider injectingorg.springframework.web.context.request.WebRequest orjavax.servlet.http.HttpSession into the controller method.

For temporary storage of model attributes in the session as part of a controllerworkflow consider usingSessionAttributes as described inthe section called “Using @SessionAttributes to store model attributes in the HTTP session between requests”.

Using @RequestAttribute to access request attributes

Similar to@SessionAttribute the@RequestAttribute annotation can be used toaccess pre-existing request attributes created by a filter or interceptor:

@RequestMapping("/")public String handle(@RequestAttribute Client client) {// ...}

Working with "application/x-www-form-urlencoded" data

The previous sections covered use of@ModelAttribute to support form submissionrequests from browser clients. The same annotation is recommended for use with requestsfrom non-browser clients as well. However there is one notable difference when it comesto working with HTTP PUT requests. Browsers can submit form data via HTTP GET or HTTPPOST. Non-browser clients can also submit forms via HTTP PUT. This presents a challengebecause the Servlet specification requires theServletRequest.getParameter*() familyof methods to support form field access only for HTTP POST, not for HTTP PUT.

To support HTTP PUT and PATCH requests, thespring-web module provides the filterHttpPutFormContentFilter, which can be configured inweb.xml:

<filter><filter-name>httpPutFormFilter</filter-name><filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class></filter><filter-mapping><filter-name>httpPutFormFilter</filter-name><servlet-name>dispatcherServlet</servlet-name></filter-mapping><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet>

The above filter intercepts HTTP PUT and PATCH requests with content typeapplication/x-www-form-urlencoded, reads the form data from the body of the request,and wraps theServletRequest in order to make the form data available through theServletRequest.getParameter*() family of methods.

[Note]Note

AsHttpPutFormContentFilter consumes the body of the request, it should not beconfigured for PUT or PATCH URLs that rely on other converters forapplication/x-www-form-urlencoded. This includes@RequestBody MultiValueMap<String,String> andHttpEntity<MultiValueMap<String, String>>.

Mapping cookie values with the @CookieValue annotation

The@CookieValue annotation allows a method parameter to be bound to the value of anHTTP cookie.

Let us consider that the following cookie has been received with an http request:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

The following code sample demonstrates how to get the value of theJSESSIONID cookie:

@RequestMapping("/displayHeaderInfo.do")publicvoid displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) {//...}

Type conversion is applied automatically if the target method parameter type is notString. Seethe section called “Method Parameters And Type Conversion”.

This annotation is supported for annotated handler methods in Servlet and Portletenvironments.

Mapping request header attributes with the @RequestHeader annotation

The@RequestHeader annotation allows a method parameter to be bound to a request header.

Here is a sample request header:

Host                    localhost:8080Accept                  text/html,application/xhtml+xml,application/xml;q=0.9Accept-Language         fr,en-gb;q=0.7,en;q=0.3Accept-Encoding         gzip,deflateAccept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive              300

The following code sample demonstrates how to get the value of theAccept-Encoding andKeep-Alive headers:

@RequestMapping("/displayHeaderInfo.do")publicvoid displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,@RequestHeader("Keep-Alive")long keepAlive) {//...}

Type conversion is applied automatically if the method parameter is notString. Seethe section called “Method Parameters And Type Conversion”.

When an@RequestHeader annotation is used on aMap<String, String>,MultiValueMap<String, String>, orHttpHeaders argument, the map is populatedwith all header values.

[Tip]Tip

Built-in support is available for converting a comma-separated string into anarray/collection of strings or other types known to the type conversion system. Forexample a method parameter annotated with@RequestHeader("Accept") may be of typeString but alsoString[] orList<String>.

This annotation is supported for annotated handler methods in Servlet and Portletenvironments.

Method Parameters And Type Conversion

String-based values extracted from the request including request parameters, pathvariables, request headers, and cookie values may need to be converted to the targettype of the method parameter or field (e.g., binding a request parameter to a field inan@ModelAttribute parameter) they’re bound to. If the target type is notString,Spring automatically converts to the appropriate type. All simple types such as int,long, Date, etc. are supported. You can further customize the conversion process throughaWebDataBinder (seethe section called “Customizing WebDataBinder initialization”) or by registeringFormatters withtheFormattingConversionService (seeSection 9.6, “Spring Field Formatting”).

Customizing WebDataBinder initialization

To customize request parameter binding with PropertyEditors through Spring’sWebDataBinder, you can use@InitBinder-annotated methods within your controller,@InitBinder methods within an@ControllerAdvice class, or provide a customWebBindingInitializer. See thethe section called “Advising controllers with @ControllerAdvice and @RestControllerAdvice” section for more details.

Customizing data binding with @InitBinder

Annotating controller methods with@InitBinder allows you to configure web databinding directly within your controller class.@InitBinder identifies methods thatinitialize theWebDataBinder that will be used to populate command and form objectarguments of annotated handler methods.

Such init-binder methods support all arguments that@RequestMapping methods support,except for command/form objects and corresponding validation result objects. Init-bindermethods must not have a return value. Thus, they are usually declared asvoid.Typical arguments includeWebDataBinder in combination withWebRequest orjava.util.Locale, allowing code to register context-specific editors.

The following example demonstrates the use of@InitBinder to configure aCustomDateEditor for alljava.util.Date form properties.

@Controllerpublicclass MyFormController {@InitBinderprotectedvoid initBinder(WebDataBinder binder) {        SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd");        dateFormat.setLenient(false);        binder.registerCustomEditor(Date.class,new CustomDateEditor(dateFormat, false));    }// ...}

Alternatively, as of Spring 4.2, consider usingaddCustomFormatter to specifyFormatter implementations instead ofPropertyEditor instances. This isparticularly useful if you happen to have aFormatter-based setup in a sharedFormattingConversionService as well, with the same approach to be reused forcontroller-specific tweaking of the binding rules.

@Controllerpublicclass MyFormController {@InitBinderprotectedvoid initBinder(WebDataBinder binder) {        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));    }// ...}
Configuring a custom WebBindingInitializer

To externalize data binding initialization, you can provide a custom implementation oftheWebBindingInitializer interface, which you then enable by supplying a custom beanconfiguration for anAnnotationMethodHandlerAdapter, thus overriding the defaultconfiguration.

The following example from the PetClinic application shows a configuration using acustom implementation of theWebBindingInitializer interface,org.springframework.samples.petclinic.web.ClinicBindingInitializer, which configuresPropertyEditors required by several of the PetClinic controllers.

<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><propertyname="cacheSeconds"value="0"/><propertyname="webBindingInitializer"><beanclass="org.springframework.samples.petclinic.web.ClinicBindingInitializer"/></property></bean>

@InitBinder methods can also be defined in an@ControllerAdvice-annotated class inwhich case they apply to matching controllers. This provides an alternative to using aWebBindingInitializer. See thethe section called “Advising controllers with @ControllerAdvice and @RestControllerAdvice” section for more details.

Advising controllers with @ControllerAdvice and @RestControllerAdvice

The@ControllerAdvice annotation is a component annotation allowing implementationclasses to be auto-detected through classpath scanning. It is automatically enabled whenusing the MVC namespace or the MVC Java config.

Classes annotated with@ControllerAdvice can contain@ExceptionHandler,@InitBinder, and@ModelAttribute annotated methods, and these methods will apply to@RequestMapping methods across all controller hierarchies as opposed to the controllerhierarchy within which they are declared.

@RestControllerAdvice is an alternative where@ExceptionHandler methodsassume@ResponseBody semantics by default.

Both@ControllerAdvice and@RestControllerAdvice can target a subset of controllers:

// Target all Controllers annotated with @RestController@ControllerAdvice(annotations = RestController.class)publicclass AnnotationAdvice {}// Target all Controllers within specific packages@ControllerAdvice("org.example.controllers")publicclass BasePackageAdvice {}// Target all Controllers assignable to specific classes@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})publicclass AssignableTypesAdvice {}

Check out the@ControllerAdvicedocumentation for more details.

Jackson Serialization View Support

It can sometimes be useful to filter contextually the object that will be serialized to theHTTP response body. In order to provide such capability, Spring MVC has built-in support forrendering withJackson’s Serialization Views.

To use it with an@ResponseBody controller method or controller methods that returnResponseEntity, simply add the@JsonView annotation with a class argument specifyingthe view class or interface to be used:

@RestControllerpublicclass UserController {@GetMapping("/user")@JsonView(User.WithoutPasswordView.class)public User getUser() {returnnew User("eric","7!jd#h23");    }}publicclass User {publicinterface WithoutPasswordView {};publicinterface WithPasswordViewextends WithoutPasswordView {};private String username;private String password;public User() {    }public User(String username, String password) {this.username = username;this.password = password;    }@JsonView(WithoutPasswordView.class)public String getUsername() {returnthis.username;    }@JsonView(WithPasswordView.class)public String getPassword() {returnthis.password;    }}
[Note]Note

Note that despite@JsonView allowing for more than one class tobe specified, the use on a controller method is only supported withexactly one class argument. Consider the use of a composite interfaceif you need to enable multiple views.

For controllers relying on view resolution, simply add the serialization view classto the model:

@Controllerpublicclass UserControllerextends AbstractController {@GetMapping("/user")public String getUser(Model model) {        model.addAttribute("user",new User("eric","7!jd#h23"));        model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);return"userView";    }}

Jackson JSONP Support

In order to enableJSONP support for@ResponseBodyandResponseEntity methods, declare an@ControllerAdvice bean that extendsAbstractJsonpResponseBodyAdvice as shown below where the constructor argument indicatesthe JSONP query parameter name(s):

@ControllerAdvicepublicclass JsonpAdviceextends AbstractJsonpResponseBodyAdvice {public JsonpAdvice() {super("callback");    }}

For controllers relying on view resolution, JSONP is automatically enabled when therequest has a query parameter namedjsonp orcallback. Those names can becustomized throughjsonpParameterNames property.

22.3.4 Asynchronous Request Processing

Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead ofreturning a value, as usual, a controller method can now return ajava.util.concurrent.Callable and produce the return value from a Spring MVC managed thread.Meanwhile the main Servlet container thread is exited and released and allowed to process otherrequests. Spring MVC invokes theCallable in a separate thread with the help of aTaskExecutor and when theCallable returns, the request is dispatched back to theServlet container to resume processing using the value returned by theCallable. Hereis an example of such a controller method:

@PostMappingpublic Callable<String> processUpload(final MultipartFile file) {returnnew Callable<String>() {public String call()throws Exception {// ...return"someView";        }    };}

Another option is for the controller method to return an instance ofDeferredResult. In thiscase the return value will also be produced from any thread, i.e. one thatis not managed by Spring MVC. For example the result may be produced in response to someexternal event such as a JMS message, a scheduled task, and so on. Here is an exampleof such a controller method:

@RequestMapping("/quotes")@ResponseBodypublic DeferredResult<String> quotes() {    DeferredResult<String> deferredResult =new DeferredResult<String>();// Save the deferredResult somewhere..return deferredResult;}// In some other thread...deferredResult.setResult(data);

This may be difficult to understand without any knowledge of the Servlet 3.0asynchronous request processing features. It would certainly help to read upon that. Here are a few basic facts about the underlying mechanism:

  • AServletRequest can be put in asynchronous mode by callingrequest.startAsync().The main effect of doing so is that the Servlet, as well as any Filters, can exit butthe response will remain open to allow processing to complete later.
  • The call torequest.startAsync() returnsAsyncContext which can be used forfurther control over async processing. For example it provides the methoddispatch,that is similar to a forward from the Servlet API except it allows anapplication to resume request processing on a Servlet container thread.
  • TheServletRequest provides access to the currentDispatcherType that canbe used to distinguish between processing the initial request, an asyncdispatch, a forward, and other dispatcher types.

With the above in mind, the following is the sequence of events for async requestprocessing with aCallable:

  • Controller returns aCallable.
  • Spring MVC starts asynchronous processing and submits theCallable toaTaskExecutor for processing in a separate thread.
  • TheDispatcherServlet and all Filter’s exit the Servlet container threadbut the response remains open.
  • TheCallable produces a result and Spring MVC dispatches the request backto the Servlet container to resume processing.
  • TheDispatcherServlet is invoked again and processing resumes with theasynchronously produced result from theCallable.

The sequence forDeferredResult is very similar except it’s up to theapplication to produce the asynchronous result from any thread:

  • Controller returns aDeferredResult and saves it in some in-memoryqueue or list where it can be accessed.
  • Spring MVC starts async processing.
  • TheDispatcherServlet and all configured Filter’s exit the requestprocessing thread but the response remains open.
  • The application sets theDeferredResult from some thread and Spring MVCdispatches the request back to the Servlet container.
  • TheDispatcherServlet is invoked again and processing resumes with theasynchronously produced result.

For further background on the motivation for async request processing andwhen or why to use it please readthisblog post series.

Exception Handling for Async Requests

What happens if aCallable returned from a controller method raises anException while being executed? The short answer is the same as what happenswhen a controller method raises an exception. It goes through the regularexception handling mechanism. The longer explanation is that when aCallableraises an Exception Spring MVC dispatches to the Servlet container withtheException as the result and that leads to resume request processingwith theException instead of a controller method return value.When using aDeferredResult you have a choice whether to callsetResult orsetErrorResult with anException instance.

Intercepting Async Requests

AHandlerInterceptor can also implementAsyncHandlerInterceptor in orderto implement theafterConcurrentHandlingStarted callback, which is calledinstead ofpostHandle andafterCompletion when asynchronous processingstarts.

AHandlerInterceptor can also register aCallableProcessingInterceptoror aDeferredResultProcessingInterceptor in order to integrate moredeeply with the lifecycle of an asynchronous request and for examplehandle a timeout event. See the Javadoc ofAsyncHandlerInterceptorfor more details.

TheDeferredResult type also provides methods such asonTimeout(Runnable)andonCompletion(Runnable). See the Javadoc ofDeferredResult for moredetails.

When using aCallable you can wrap it with an instance ofWebAsyncTaskwhich also provides registration methods for timeout and completion.

HTTP Streaming

A controller method can useDeferredResult andCallable to produce itsreturn value asynchronously and that can be used to implement techniques such aslong pollingwhere the server can push an event to the client as soon as possible.

What if you wanted to push multiple events on a single HTTP response?This is a technique related to "Long Polling" that is known as "HTTP Streaming".Spring MVC makes this possible through theResponseBodyEmitter return valuetype which can be used to send multiple Objects, instead of one as is normallythe case with@ResponseBody, where each Object sent is written to theresponse with anHttpMessageConverter.

Here is an example of that:

@RequestMapping("/events")public ResponseBodyEmitter handle() {    ResponseBodyEmitter emitter =new ResponseBodyEmitter();// Save the emitter somewhere..return emitter;}// In some other threademitter.send("Hello once");// and again later onemitter.send("Hello again");// and done at some pointemitter.complete();

Note thatResponseBodyEmitter can also be used as the body in aResponseEntity in order to customize the status and headers ofthe response.

HTTP Streaming With Server-Sent Events

SseEmitter is a subclass ofResponseBodyEmitter providing support forServer-Sent Events.Server-sent events is a just another variation on the same "HTTP Streaming"technique except events pushed from the server are formatted according tothe W3C Server-Sent Events specification.

Server-Sent Events can be used for their intended purpose, that is to pushevents from the server to clients. It is quite easy to do in Spring MVC andrequires simply returning a value of typeSseEmitter.

Note however that Internet Explorer does not support Server-Sent Events andthat for more advanced web application messaging scenarios such as online games,collaboration, financial applicatinos, and others it’s better to considerSpring’s WebSocket support that includes SockJS-style WebSocket emulationfalling back to a very wide range of browsers (including Internet Explorer)and also higher-level messaging patterns for interacting with clients througha publish-subscribe model within a more messaging-centric architecture.For further background on this seethe following blog post.

HTTP Streaming Directly To The OutputStream

ResponseBodyEmitter allows sending events by writing Objects to theresponse through anHttpMessageConverter. This is probably the most commoncase, for example when writing JSON data. However sometimes it is useful tobypass message conversion and write directly to the responseOutputStreamfor example for a file download. This can be done with the help of theStreamingResponseBody return value type.

Here is an example of that:

@RequestMapping("/download")public StreamingResponseBody handle() {returnnew StreamingResponseBody() {@Overridepublicvoid writeTo(OutputStream outputStream)throws IOException {// write...        }    };}

Note thatStreamingResponseBody can also be used as the body in aResponseEntity in order to customize the status and headers ofthe response.

Configuring Asynchronous Request Processing

Servlet Container Configuration

For applications configured with aweb.xml be sure to update to version 3.0:

<web-appxmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0">    ...</web-app>

Asynchronous support must be enabled on theDispatcherServlet through the<async-supported>true</async-supported> sub-element inweb.xml. AdditionallyanyFilter that participates in asyncrequest processing must be configuredto support the ASYNC dispatcher type. It should be safe to enable the ASYNCdispatcher type for all filters provided with the Spring Framework since theyusually extendOncePerRequestFilter and that has runtime checks for whetherthe filter needs to be involved in async dispatches or not.

Below is some example web.xml configuration:

<web-appxmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="            http://java.sun.com/xml/ns/javaee            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><filter><filter-name>Spring OpenEntityManagerInViewFilter</filter-name><filter-class>org.springframework.~.OpenEntityManagerInViewFilter</filter-class><async-supported>true</async-supported></filter><filter-mapping><filter-name>Spring OpenEntityManagerInViewFilter</filter-name><url-pattern>/*</url-pattern><dispatcher>REQUEST</dispatcher><dispatcher>ASYNC</dispatcher></filter-mapping></web-app>

If using Servlet 3, Java based configuration for example viaWebApplicationInitializer,you’ll also need to set the "asyncSupported" flag as well as the ASYNC dispatcher typejust like withweb.xml. To simplify all this configuration, consider extendingAbstractDispatcherServletInitializer, or betterAbstractAnnotationConfigDispatcherServletInitializer which automaticallyset those options and make it very easy to registerFilter instances.

Spring MVC Configuration

The MVC Java config and the MVC namespace provide options for configuringasynchronous request processing.WebMvcConfigurer has the methodconfigureAsyncSupport while<mvc:annotation-driven> has an<async-support> sub-element.

Those allow you to configure the default timeout value to use for async requests, whichif not set depends on the underlying Servlet container (e.g. 10 seconds on Tomcat). Youcan also configure anAsyncTaskExecutor to use for executingCallable instancesreturned from controller methods. It is highly recommended to configure this propertysince by default Spring MVC usesSimpleAsyncTaskExecutor. The MVC Java config and theMVC namespace also allow you to registerCallableProcessingInterceptor andDeferredResultProcessingInterceptor instances.

If you need to override the default timeout value for a specificDeferredResult, youcan do so by using the appropriate class constructor. Similarly, for aCallable, youcan wrap it in aWebAsyncTask and use the appropriate class constructor to customizethe timeout value. The class constructor ofWebAsyncTask also allows providing anAsyncTaskExecutor.

22.3.5 Testing Controllers

Thespring-test module offers first class support for testing annotated controllers.SeeSection 15.6, “Spring MVC Test Framework”.

22.4 Handler mappings

In previous versions of Spring, users were required to define one or moreHandlerMapping beans in the web application context to map incoming web requests toappropriate handlers. With the introduction of annotated controllers, you generallydon’t need to do that because theRequestMappingHandlerMapping automatically looks for@RequestMapping annotations on all@Controller beans. However, do keep in mind thatallHandlerMapping classes extending fromAbstractHandlerMapping have the followingproperties that you can use to customize their behavior:

  • interceptors List of interceptors to use.HandlerInterceptors are discussed inSection 22.4.1, “Intercepting requests with a HandlerInterceptor”.
  • defaultHandler Default handler to use, when this handler mapping does not result ina matching handler.
  • order Based on the value of the order property (see theorg.springframework.core.Ordered interface), Spring sorts all handler mappingsavailable in the context and applies the first matching handler.
  • alwaysUseFullPath Iftrue , Spring uses the full path within the current Servletcontext to find an appropriate handler. Iffalse (the default), the path within thecurrent Servlet mapping is used. For example, if a Servlet is mapped using/testing/* and thealwaysUseFullPath property is set to true,/testing/viewPage.html is used, whereas if the property is set to false,/viewPage.html is used.
  • urlDecode Defaults totrue, as of Spring 2.5. If you prefer to compare encodedpaths, set this flag tofalse. However, theHttpServletRequest always exposes theServlet path in decoded form. Be aware that the Servlet path will not match whencompared with encoded paths.

The following example shows how to configure an interceptor:

<beans><beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"><propertyname="interceptors"><beanclass="example.MyInterceptor"/></property></bean><beans>

22.4.1 Intercepting requests with a HandlerInterceptor

Spring’s handler mapping mechanism includes handler interceptors, which are useful whenyou want to apply specific functionality to certain requests, for example, checking fora principal.

Interceptors located in the handler mapping must implementHandlerInterceptor from theorg.springframework.web.servlet package. This interface defines three methods:preHandle(..) is calledbefore the actual handler is executed;postHandle(..) iscalledafter the handler is executed; andafterCompletion(..) is calledafterthe complete request has finished. These three methods should provide enoughflexibility to do all kinds of preprocessing and postprocessing.

ThepreHandle(..) method returns a boolean value. You can use this method to break orcontinue the processing of the execution chain. When this method returnstrue, thehandler execution chain will continue; when it returns false, theDispatcherServletassumes the interceptor itself has taken care of requests (and, for example, rendered anappropriate view) and does not continue executing the other interceptors and the actualhandler in the execution chain.

Interceptors can be configured using theinterceptors property, which is present onallHandlerMapping classes extending fromAbstractHandlerMapping. This is shown inthe example below:

<beans><beanid="handlerMapping"class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"><propertyname="interceptors"><list><refbean="officeHoursInterceptor"/></list></property></bean><beanid="officeHoursInterceptor"class="samples.TimeBasedAccessInterceptor"><propertyname="openingTime"value="9"/><propertyname="closingTime"value="18"/></bean></beans>
package samples;publicclass TimeBasedAccessInterceptorextends HandlerInterceptorAdapter {privateint openingTime;privateint closingTime;publicvoid setOpeningTime(int openingTime) {this.openingTime = openingTime;    }publicvoid setClosingTime(int closingTime) {this.closingTime = closingTime;    }publicboolean preHandle(HttpServletRequest request, HttpServletResponse response,            Object handler)throws Exception {        Calendar cal = Calendar.getInstance();int hour = cal.get(HOUR_OF_DAY);if (openingTime <= hour && hour < closingTime) {return true;        }        response.sendRedirect("http://host.com/outsideOfficeHours.html");return false;    }}

Any request handled by this mapping is intercepted by theTimeBasedAccessInterceptor.If the current time is outside office hours, the user is redirected to a static HTMLfile that says, for example, you can only access the website during office hours.

[Note]Note

When using theRequestMappingHandlerMapping the actual handler is an instance ofHandlerMethod which identifies the specific controller method that will be invoked.

As you can see, the Spring adapter classHandlerInterceptorAdapter makes it easier toextend theHandlerInterceptor interface.

[Tip]Tip

In the example above, the configured interceptor will apply to all requests handled withannotated controller methods. If you want to narrow down the URL paths to which aninterceptor applies, you can use the MVC namespace or the MVC Java config, or declarebean instances of typeMappedInterceptor to do that. SeeSection 22.16.1, “Enabling the MVC Java Config or the MVC XML Namespace”.

Note that thepostHandle method ofHandlerInterceptor is not always ideally suited foruse with@ResponseBody andResponseEntity methods. In such cases anHttpMessageConverterwrites to and commits the response beforepostHandle is called which makes it impossibleto change the response, for example to add a header. Instead an application can implementResponseBodyAdvice and either declare it as an@ControllerAdvice bean or configure itdirectly onRequestMappingHandlerAdapter.

22.5 Resolving views

All MVC frameworks for web applications provide a way to address views. Spring providesview resolvers, which enable you to render models in a browser without tying you to aspecific view technology. Out of the box, Spring enables you to use JSPs, Velocitytemplates and XSLT views, for example. SeeChapter 23,View Technologies for a discussion of how to integrateand use a number of disparate view technologies.

The two interfaces that are important to the way Spring handles views areViewResolverandView. TheViewResolver provides a mapping between view names and actual views.TheView interface addresses the preparation of the request and hands the request overto one of the view technologies.

22.5.1 Resolving views with the ViewResolver interface

As discussed inSection 22.3, “Implementing Controllers”, all handler methods in the Spring Web MVCcontrollers must resolve to a logical view name, either explicitly (e.g., by returning aString,View, orModelAndView) or implicitly (i.e., based on conventions). Viewsin Spring are addressed by a logical view name and are resolved by a view resolver.Spring comes with quite a few view resolvers. This table lists most of them; a couple ofexamples follow.

Table 22.3. View resolvers

ViewResolverDescription

AbstractCachingViewResolver

Abstract view resolver that caches views. Often views need preparation before they can be used; extending this view resolver provides caching.

XmlViewResolver

Implementation ofViewResolver that accepts a configuration file written in XML with the same DTD as Spring’s XML bean factories. The default configuration file is/WEB-INF/views.xml.

ResourceBundleViewResolver

Implementation ofViewResolver that uses bean definitions in aResourceBundle, specified by the bundle base name. Typically you define the bundle in a properties file, located in the classpath. The default file name isviews.properties.

UrlBasedViewResolver

Simple implementation of theViewResolver interface that effects the direct resolution of logical view names to URLs, without an explicit mapping definition. This is appropriate if your logical names match the names of your view resources in a straightforward manner, without the need for arbitrary mappings.

InternalResourceViewResolver

Convenient subclass ofUrlBasedViewResolver that supportsInternalResourceView (in effect, Servlets and JSPs) and subclasses such asJstlView andTilesView. You can specify the view class for all views generated by this resolver by usingsetViewClass(..). See theUrlBasedViewResolver javadocs for details.

VelocityViewResolver /FreeMarkerViewResolver

Convenient subclass ofUrlBasedViewResolver that supportsVelocityView (in effect, Velocity templates) orFreeMarkerView ,respectively, and custom subclasses of them.

ContentNegotiatingViewResolver

Implementation of theViewResolver interface that resolves a view based on the request file name orAccept header. SeeSection 22.5.4, “ContentNegotiatingViewResolver”.


As an example, with JSP as a view technology, you can use theUrlBasedViewResolver.This view resolver translates a view name to a URL and hands the request over to theRequestDispatcher to render the view.

<beanid="viewResolver"class="org.springframework.web.servlet.view.UrlBasedViewResolver"><propertyname="viewClass"value="org.springframework.web.servlet.view.JstlView"/><propertyname="prefix"value="/WEB-INF/jsp/"/><propertyname="suffix"value=".jsp"/></bean>

When returningtest as a logical view name, this view resolver forwards the request totheRequestDispatcher that will send the request to/WEB-INF/jsp/test.jsp.

When you combine different view technologies in a web application, you can use theResourceBundleViewResolver:

<beanid="viewResolver"class="org.springframework.web.servlet.view.ResourceBundleViewResolver"><propertyname="basename"value="views"/><propertyname="defaultParentView"value="parentView"/></bean>

TheResourceBundleViewResolver inspects theResourceBundle identified by thebasename, and for each view it is supposed to resolve, it uses the value of the property[viewname].(class) as the view class and the value of the property[viewname].url asthe view url. Examples can be found in the next chapter which covers view technologies.As you can see, you can identify a parent view, from which all views in the propertiesfile "extend". This way you can specify a default view class, for example.

[Note]Note

Subclasses ofAbstractCachingViewResolver cache view instances that they resolve.Caching improves performance of certain view technologies. It’s possible to turn off thecache by setting thecache property tofalse. Furthermore, if you must refresh acertain view at runtime (for example when a Velocity template is modified), you can usetheremoveFromCache(String viewName, Locale loc) method.

22.5.2 Chaining ViewResolvers

Spring supports multiple view resolvers. Thus you can chain resolvers and, for example,override specific views in certain circumstances. You chain view resolvers by addingmore than one resolver to your application context and, if necessary, by setting theorder property to specify ordering. Remember, the higher the order property, the laterthe view resolver is positioned in the chain.

In the following example, the chain of view resolvers consists of two resolvers, anInternalResourceViewResolver, which is always automatically positioned as the lastresolver in the chain, and anXmlViewResolver for specifying Excel views. Excel viewsare not supported by theInternalResourceViewResolver.

<beanid="jspViewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="viewClass"value="org.springframework.web.servlet.view.JstlView"/><propertyname="prefix"value="/WEB-INF/jsp/"/><propertyname="suffix"value=".jsp"/></bean><beanid="excelViewResolver"class="org.springframework.web.servlet.view.XmlViewResolver"><propertyname="order"value="1"/><propertyname="location"value="/WEB-INF/views.xml"/></bean><!-- in views.xml --><beans><beanname="report"class="org.springframework.example.ReportExcelView"/></beans>

If a specific view resolver does not result in a view, Spring examines the context forother view resolvers. If additional view resolvers exist, Spring continues to inspectthem until a view is resolved. If no view resolver returns a view, Spring throws aServletException.

The contract of a view resolver specifies that a view resolvercan return null toindicate the view could not be found. Not all view resolvers do this, however, becausein some cases, the resolver simply cannot detect whether or not the view exists. Forexample, theInternalResourceViewResolver uses theRequestDispatcher internally, anddispatching is the only way to figure out if a JSP exists, but this action can onlyexecute once. The same holds for theVelocityViewResolver and some others. Check thejavadocs of the specific view resolver to see whether it reports non-existing views.Thus, putting anInternalResourceViewResolver in the chain in a place other thanthe last results in the chain not being fully inspected, because theInternalResourceViewResolver willalways return a view!

22.5.3 Redirecting to Views

As mentioned previously, a controller typically returns a logical view name, which aview resolver resolves to a particular view technology. For view technologies such asJSPs that are processed through the Servlet or JSP engine, this resolution is usuallyhandled through the combination ofInternalResourceViewResolver andInternalResourceView, which issues an internal forward or include via the ServletAPI’sRequestDispatcher.forward(..) method orRequestDispatcher.include() method.For other view technologies, such as Velocity, XSLT, and so on, the view itself writesthe content directly to the response stream.

It is sometimes desirable to issue an HTTP redirect back to the client, before the viewis rendered. This is desirable, for example, when one controller has been called withPOST data, and the response is actually a delegation to another controller (forexample on a successful form submission). In this case, a normal internal forward willmean that the other controller will also see the samePOST data, which is potentiallyproblematic if it can confuse it with other expected data. Another reason to perform aredirect before displaying the result is to eliminate the possibility of the usersubmitting the form data multiple times. In this scenario, the browser will first sendan initialPOST; it will then receive a response to redirect to a different URL; andfinally the browser will perform a subsequentGET for the URL named in the redirectresponse. Thus, from the perspective of the browser, the current page does not reflectthe result of aPOST but rather of aGET. The end effect is that there is no way theuser can accidentally re-POST the same data by performing a refresh. The refreshforces aGET of the result page, not a resend of the initialPOST data.

RedirectView

One way to force a redirect as the result of a controller response is for the controllerto create and return an instance of Spring’sRedirectView. In this case,DispatcherServlet does not use the normal view resolution mechanism. Rather because ithas been given the (redirect) view already, theDispatcherServlet simply instructs theview to do its work. TheRedirectView in turn callsHttpServletResponse.sendRedirect()to send an HTTP redirect to the client browser.

If you useRedirectView and the view is created by the controller itself, it isrecommended that you configure the redirect URL to be injected into the controller sothat it is not baked into the controller but configured in the context along with theview names. Thethe section called “The redirect: prefix” facilitates this decoupling.

Passing Data To the Redirect Target

By default all model attributes are considered to be exposed as URI template variables inthe redirect URL. Of the remaining attributes those that are primitive types orcollections/arrays of primitive types are automatically appended as query parameters.

Appending primitive type attributes as query parameters may be the desired result if amodel instance was prepared specifically for the redirect. However, in annotatedcontrollers the model may contain additional attributes added for rendering purposes (e.g.drop-down field values). To avoid the possibility of having such attributes appear in theURL, an@RequestMapping method can declare an argument of typeRedirectAttributes anduse it to specify the exact attributes to make available toRedirectView. If the methoddoes redirect, the content ofRedirectAttributes is used. Otherwise the content of themodel is used.

TheRequestMappingHandlerAdapter provides a flag called"ignoreDefaultModelOnRedirect" that can be used to indicate the content of the defaultModel should never be used if a controller method redirects. Instead the controllermethod should declare an attribute of typeRedirectAttributes or if it doesn’t do sono attributes should be passed on toRedirectView. Both the MVC namespace and the MVCJava config keep this flag set tofalse in order to maintain backwards compatibility.However, for new applications we recommend setting it totrue

Note that URI template variables from the present request are automatically madeavailable when expanding a redirect URL and do not need to be added explicitly neitherthroughModel norRedirectAttributes. For example:

@PostMapping("/files/{path}")public String upload(...) {// ...return"redirect:files/{path}";}

Another way of passing data to the redirect target is viaFlash Attributes. Unlikeother redirect attributes, flash attributes are saved in the HTTP session (and hence donot appear in the URL). SeeSection 22.6, “Using flash attributes” for more information.

The redirect: prefix

While the use ofRedirectView works fine, if the controller itself creates theRedirectView, there is no avoiding the fact that the controller is aware that aredirection is happening. This is really suboptimal and couples things too tightly. Thecontroller should not really care about how the response gets handled. In general itshould operate only in terms of view names that have been injected into it.

The specialredirect: prefix allows you to accomplish this. If a view name is returnedthat has the prefixredirect:, theUrlBasedViewResolver (and all subclasses) willrecognize this as a special indication that a redirect is needed. The rest of the viewname will be treated as the redirect URL.

The net effect is the same as if the controller had returned aRedirectView, but nowthe controller itself can simply operate in terms of logical view names. A logical viewname such asredirect:/myapp/some/resource will redirect relative to the currentServlet context, while a name such asredirect:http://myhost.com/some/arbitrary/pathwill redirect to an absolute URL.

Note that the controller handler is annotated with the@ResponseStatus, the annotationvalue takes precedence over the response status set byRedirectView.

The forward: prefix

It is also possible to use a specialforward: prefix for view names that areultimately resolved byUrlBasedViewResolver and subclasses. This creates anInternalResourceView (which ultimately does aRequestDispatcher.forward()) aroundthe rest of the view name, which is considered a URL. Therefore, this prefix is notuseful withInternalResourceViewResolver andInternalResourceView (for JSPs forexample). But the prefix can be helpful when you are primarily using another viewtechnology, but still want to force a forward of a resource to be handled by theServlet/JSP engine. (Note that you may also chain multiple view resolvers, instead.)

As with theredirect: prefix, if the view name with theforward: prefix is injectedinto the controller, the controller does not detect that anything special is happeningin terms of handling the response.

22.5.4 ContentNegotiatingViewResolver

TheContentNegotiatingViewResolver does not resolve views itself but rather delegatesto other view resolvers, selecting the view that resembles the representation requestedby the client. Two strategies exist for a client to request a representation from theserver:

[Note]Note

One issue with theAccept header is that it is impossible to set it in a web browserwithin HTML. For example, in Firefox, it is fixed to:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

For this reason it is common to see the use of a distinct URI for each representationwhen developing browser based web applications.

To support multiple representations of a resource, Spring provides theContentNegotiatingViewResolver to resolve a view based on the file extension orAccept header of the HTTP request.ContentNegotiatingViewResolver does not performthe view resolution itself but instead delegates to a list of view resolvers that youspecify through the bean propertyViewResolvers.

TheContentNegotiatingViewResolver selects an appropriateView to handle the requestby comparing the request media type(s) with the media type (also known asContent-Type) supported by theView associated with each of itsViewResolvers. ThefirstView in the list that has a compatibleContent-Type returns the representationto the client. If a compatible view cannot be supplied by theViewResolver chain, thenthe list of views specified through theDefaultViews property will be consulted. Thislatter option is appropriate for singletonViews that can render an appropriaterepresentation of the current resource regardless of the logical view name. TheAcceptheader may include wild cards, for exampletext/*, in which case aView whoseContent-Type wastext/xml is a compatible match.

To support custom resolution of a view based on a file extension, use aContentNegotiationManager: seeSection 22.16.6, “Content Negotiation”.

Here is an example configuration of aContentNegotiatingViewResolver:

<beanclass="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><propertyname="viewResolvers"><list><beanclass="org.springframework.web.servlet.view.BeanNameViewResolver"/><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/jsp/"/><propertyname="suffix"value=".jsp"/></bean></list></property><propertyname="defaultViews"><list><beanclass="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/></list></property></bean><beanid="content"class="com.foo.samples.rest.SampleContentAtomView"/>

TheInternalResourceViewResolver handles the translation of view names and JSP pages,while theBeanNameViewResolver returns a view based on the name of a bean. (See"Resolving views with the ViewResolver interface" for moredetails on how Spring looks up and instantiates a view.) In this example, thecontentbean is a class that inherits fromAbstractAtomFeedView, which returns an Atom RSSfeed. For more information on creating an Atom Feed representation, see the section AtomViews.

In the above configuration, if a request is made with an.html extension, the viewresolver looks for a view that matches thetext/html media type. TheInternalResourceViewResolver provides the matching view fortext/html. If therequest is made with the file extension.atom, the view resolver looks for a view thatmatches theapplication/atom+xml media type. This view is provided by theBeanNameViewResolver that maps to theSampleContentAtomView if the view namereturned iscontent. If the request is made with the file extension.json, theMappingJackson2JsonView instance from theDefaultViews list will be selectedregardless of the view name. Alternatively, client requests can be made without a fileextension but with theAccept header set to the preferred media-type, and the sameresolution of request to views would occur.

[Note]Note

If `ContentNegotiatingViewResolver’s list of ViewResolvers is not configuredexplicitly, it automatically uses any ViewResolvers defined in the application context.

The corresponding controller code that returns an Atom RSS feed for a URI of the formhttp://localhost/content.atom orhttp://localhost/content with anAccept header ofapplication/atom+xml is shown below.

@Controllerpublicclass ContentController {private List<SampleContent> contentList =new ArrayList<SampleContent>();@GetMapping("/content")public ModelAndView getContent() {        ModelAndView mav =new ModelAndView();        mav.setViewName("content");        mav.addObject("sampleContentList", contentList);return mav;    }}

22.6 Using flash attributes

Flash attributes provide a way for one request to store attributes intended for use inanother. This is most commonly needed when redirecting — for example, thePost/Redirect/Get pattern. Flash attributes are saved temporarily before theredirect (typically in the session) to be made available to the request after theredirect and removed immediately.

Spring MVC has two main abstractions in support of flash attributes.FlashMap is usedto hold flash attributes whileFlashMapManager is used to store, retrieve, and manageFlashMap instances.

Flash attribute support is always "on" and does not need to enabled explicitly althoughif not used, it never causes HTTP session creation. On each request there is an "input"FlashMap with attributes passed from a previous request (if any) and an "output"FlashMap with attributes to save for a subsequent request. BothFlashMap instancesare accessible from anywhere in Spring MVC through static methods inRequestContextUtils.

Annotated controllers typically do not need to work withFlashMap directly. Instead an@RequestMapping method can accept an argument of typeRedirectAttributes and use itto add flash attributes for a redirect scenario. Flash attributes added viaRedirectAttributes are automatically propagated to the "output" FlashMap. Similarly,after the redirect, attributes from the "input"FlashMap are automatically added to theModel of the controller serving the target URL.

Matching requests to flash attributes

The concept of flash attributes exists in many other Web frameworks and has proven to beexposed sometimes to concurrency issues. This is because by definition flash attributesare to be stored until the next request. However the very "next" request may not be theintended recipient but another asynchronous request (e.g. polling or resource requests)in which case the flash attributes are removed too early.

To reduce the possibility of such issues,RedirectView automatically "stamps"FlashMap instances with the path and query parameters of the target redirect URL. Inturn the defaultFlashMapManager matches that information to incoming requests whenlooking up the "input"FlashMap.

This does not eliminate the possibility of a concurrency issue entirely but neverthelessreduces it greatly with information that is already available in the redirect URL.Therefore the use of flash attributes is recommended mainly for redirect scenarios .

22.7 Building URIs

Spring MVC provides a mechanism for building and encoding a URI usingUriComponentsBuilder andUriComponents.

For example you can expand and encode a URI template string:

UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build();URI uri = uriComponents.expand("42","21").encode().toUri();

Note thatUriComponents is immutable and theexpand() andencode() operationsreturn new instances if necessary.

You can also expand and encode using individual URI components:

UriComponents uriComponents = UriComponentsBuilder.newInstance()        .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()        .expand("42","21")        .encode();

In a Servlet environment theServletUriComponentsBuilder subclass provides staticfactory methods to copy available URL information from a Servlet requests:

HttpServletRequest request = ...// Re-use host, scheme, port, path and query string// Replace the "accountId" query paramServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)        .replaceQueryParam("accountId","{id}").build()        .expand("123")        .encode();

Alternatively, you may choose to copy a subset of the available information up to andincluding the context path:

// Re-use host, port and context path// Append "/accounts" to the pathServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request)        .path("/accounts").build()

Or in cases where theDispatcherServlet is mapped by name (e.g./main/*), you canalso have the literal part of the servlet mapping included:

// Re-use host, port, context path// Append the literal part of the servlet mapping to the path// Append "/accounts" to the pathServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request)        .path("/accounts").build()

22.7.1 Building URIs to Controllers and methods

Spring MVC also provides a mechanism for building links to controller methods. For example, given:

@Controller@RequestMapping("/hotels/{hotel}")publicclass BookingController {@GetMapping("/bookings/{booking}")public String getBooking(@PathVariable Long booking) {// ...    }}

You can prepare a link by referring to the method by name:

UriComponents uriComponents = MvcUriComponentsBuilder    .fromMethodName(BookingController.class,"getBooking",21).buildAndExpand(42);URI uri = uriComponents.encode().toUri();

In the above example we provided actual method argument values, in this case the long value 21,to be used as a path variable and inserted into the URL. Furthermore, we provided thevalue 42 in order to fill in any remaining URI variables such as the "hotel" variable inheritedfrom the type-level request mapping. If the method had more arguments you can supply null forarguments not needed for the URL. In general only@PathVariable and@RequestParam argumentsare relevant for constructing the URL.

There are additional ways to useMvcUriComponentsBuilder. For example you can use a techniqueakin to mock testing through proxies to avoid referring to the controller method by name(the example assumes static import ofMvcUriComponentsBuilder.on):

UriComponents uriComponents = MvcUriComponentsBuilder    .fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);URI uri = uriComponents.encode().toUri();

The above examples use static methods inMvcUriComponentsBuilder. Internally they relyonServletUriComponentsBuilder to prepare a base URL from the scheme, host, port,context path and servlet path of the current request. This works well in most cases,however sometimes it may be insufficient. For example you may be outside the context ofa request (e.g. a batch process that prepares links) or perhaps you need to insert a pathprefix (e.g. a locale prefix that was removed from the request path and needs to bere-inserted into links).

For such cases you can use the static "fromXxx" overloaded methods that accept aUriComponentsBuilder to use base URL. Or you can create an instance ofMvcUriComponentsBuilderwith a base URL and then use the instance-based "withXxx" methods. For example:

UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en");MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);URI uri = uriComponents.encode().toUri();

22.7.2 Working with "Forwarded" and "X-Forwarded-*" Headers

As a request goes through proxies such as load balancers the host, port, andscheme may change presenting a challenge for applications that need to create linksto resources since the links should reflect the host, port, and scheme of theoriginal request as seen from a client perspective.

RFC 7239 defines the "Forwarded" HTTP headerfor proxies to use to provide information about the original request. There are alsoother non-standard headers in use such as "X-Forwarded-Host", "X-Forwarded-Port",and "X-Forwarded-Proto".

BothServletUriComponentsBuilder andMvcUriComponentsBuilder detect, extract, and useinformation from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port",and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflectthe original request.

TheForwardedHeaderFilter provides an alternative to do the same once and globally forthe entire application. The filter wraps the request in order to overlay host, port, andscheme information and also "hides" any forwarded headers for subsequent processing.

Note that there are security considerations when using forwarded headers as explainedin Section 8 of RFC 7239. At the application level it is difficult to determine whetherforwarded headers can be trusted or not. This is why the network upstream should beconfigured correctly to filter out untrusted forwarded headers from the outside.

Applications that don’t have a proxy and don’t need to use forwarded headers canconfigure theForwardedHeaderFilter to remove and ignore such headers.

22.7.3 Building URIs to Controllers and methods from views

You can also build links to annotated controllers from views such as JSP, Thymeleaf,FreeMarker. This can be done using thefromMappingName method inMvcUriComponentsBuilderwhich refers to mappings by name.

Every@RequestMapping is assigned a default name based on the capital letters of theclass and the full method name. For example, the methodgetFoo in classFooControlleris assigned the name "FC#getFoo". This strategy can be replaced or customized by creatingan instance ofHandlerMethodMappingNamingStrategy and plugging it into yourRequestMappingHandlerMapping. The default strategy implementation also looks at thename attribute on@RequestMapping and uses that if present. That means if the defaultmapping name assigned conflicts with another (e.g. overloaded methods) you can assigna name explicitly on the@RequestMapping.

[Note]Note

The assigned request mapping names are logged at TRACE level on startup.

The Spring JSP tag library provides a function calledmvcUrl that can be used toprepare links to controller methods based on this mechanism.

For example given:

@RequestMapping("/people/{id}/addresses")publicclass PersonAddressController {@RequestMapping("/{country}")public HttpEntity getAddress(@PathVariable String country) { ... }}

You can prepare a link from a JSP as follows:

<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>...<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a>

The above example relies on themvcUrl JSP function declared in the Spring tag library(i.e. META-INF/spring.tld). For more advanced cases (e.g. a custom base URL as explainedin the previous section), it is easy to define your own function, or use a custom tag file,in order to use a specific instance ofMvcUriComponentsBuilder with a custom base URL.

22.8 Using locales

Most parts of Spring’s architecture support internationalization, just as the Spring webMVC framework does.DispatcherServlet enables you to automatically resolve messagesusing the client’s locale. This is done withLocaleResolver objects.

When a request comes in, theDispatcherServlet looks for a locale resolver, and if itfinds one it tries to use it to set the locale. Using theRequestContext.getLocale()method, you can always retrieve the locale that was resolved by the locale resolver.

In addition to automatic locale resolution, you can also attach an interceptor to thehandler mapping (seeSection 22.4.1, “Intercepting requests with a HandlerInterceptor” for more information on handlermapping interceptors) to change the locale under specific circumstances, for example,based on a parameter in the request.

Locale resolvers and interceptors are defined in theorg.springframework.web.servlet.i18n package and are configured in your applicationcontext in the normal way. Here is a selection of the locale resolvers included inSpring.

22.8.1 Obtaining Time Zone Information

In addition to obtaining the client’s locale, it is often useful to know their time zone.TheLocaleContextResolver interface offers an extension toLocaleResolver that allowsresolvers to provide a richerLocaleContext, which may include time zone information.

When available, the user’sTimeZone can be obtained using theRequestContext.getTimeZone() method. Time zone information will automatically be usedby Date/TimeConverter andFormatter objects registered with Spring’sConversionService.

22.8.2 AcceptHeaderLocaleResolver

This locale resolver inspects theaccept-language header in the request that was sentby the client (e.g., a web browser). Usually this header field contains the locale ofthe client’s operating system.Note that this resolver does not support time zoneinformation.

22.8.3 CookieLocaleResolver

This locale resolver inspects aCookie that might exist on the client to see if aLocale orTimeZone is specified. If so, it uses the specified details. Using theproperties of this locale resolver, you can specify the name of the cookie as well as themaximum age. Find below an example of defining aCookieLocaleResolver.

<beanid="localeResolver"class="org.springframework.web.servlet.i18n.CookieLocaleResolver"><propertyname="cookieName"value="clientlanguage"/><!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --><propertyname="cookieMaxAge"value="100000"/></bean>

Table 22.4. CookieLocaleResolver properties

PropertyDefaultDescription

cookieName

classname + LOCALE

The name of the cookie

cookieMaxAge

Servlet container default

The maximum time a cookie will stay persistent on the client. If -1 is specified, the cookie will not be persisted; it will only be available until the client shuts down their browser.

cookiePath

/

Limits the visibility of the cookie to a certain part of your site. When cookiePath is specified, the cookie will only be visible to that path and the paths below it.


22.8.4 SessionLocaleResolver

TheSessionLocaleResolver allows you to retrieveLocale andTimeZone from thesession that might be associated with the user’s request. In contrast toCookieLocaleResolver, this strategy stores locally chosen locale settings in theServlet container’sHttpSession. As a consequence, those settings are just temporaryfor each session and therefore lost when each session terminates.

Note that there is no direct relationship with external session management mechanismssuch as the Spring Session project. ThisSessionLocaleResolver will simply evaluate andmodify correspondingHttpSession attributes against the currentHttpServletRequest.

22.8.5 LocaleChangeInterceptor

You can enable changing of locales by adding theLocaleChangeInterceptor to one of thehandler mappings (seeSection 22.4, “Handler mappings”). It will detect a parameter in the requestand change the locale. It callssetLocale() on theLocaleResolver that also existsin the context. The following example shows that calls to all*.view resourcescontaining a parameter namedsiteLanguage will now change the locale. So, for example,a request for the following URL,http://www.sf.net/home.view?siteLanguage=nl willchange the site language to Dutch.

<beanid="localeChangeInterceptor"class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"><propertyname="paramName"value="siteLanguage"/></bean><beanid="localeResolver"class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/><beanid="urlMapping"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><propertyname="interceptors"><list><refbean="localeChangeInterceptor"/></list></property><propertyname="mappings"><value>/**/*.view=someController</value></property></bean>

22.9 Using themes

22.9.1 Overview of themes

You can apply Spring Web MVC framework themes to set the overall look-and-feel of yourapplication, thereby enhancing user experience. A theme is a collection of staticresources, typically style sheets and images, that affect the visual style of theapplication.

22.9.2 Defining themes

To use themes in your web application, you must set up an implementation of theorg.springframework.ui.context.ThemeSource interface. TheWebApplicationContextinterface extendsThemeSource but delegates its responsibilities to a dedicatedimplementation. By default the delegate will be anorg.springframework.ui.context.support.ResourceBundleThemeSource implementation thatloads properties files from the root of the classpath. To use a customThemeSourceimplementation or to configure the base name prefix of theResourceBundleThemeSource,you can register a bean in the application context with the reserved namethemeSource.The web application context automatically detects a bean with that name and uses it.

When using theResourceBundleThemeSource, a theme is defined in a simple propertiesfile. The properties file lists the resources that make up the theme. Here is an example:

styleSheet=/themes/cool/style.cssbackground=/themes/cool/img/coolBg.jpg

The keys of the properties are the names that refer to the themed elements from viewcode. For a JSP, you typically do this using thespring:theme custom tag, which isvery similar to thespring:message tag. The following JSP fragment uses the themedefined in the previous example to customize the look and feel:

<%@taglibprefix="spring"uri="http://www.springframework.org/tags"%><html><head><linkrel="stylesheet"href="<spring:theme code='styleSheet'/>"type="text/css"/></head><bodystyle="background=<spring:theme code='background'/>">        ...</body></html>

By default, theResourceBundleThemeSource uses an empty base name prefix. As a result,the properties files are loaded from the root of the classpath. Thus you would put thecool.properties theme definition in a directory at the root of the classpath, forexample, in/WEB-INF/classes. TheResourceBundleThemeSource uses the standard Javaresource bundle loading mechanism, allowing for full internationalization of themes. Forexample, we could have a/WEB-INF/classes/cool_nl.properties that references a specialbackground image with Dutch text on it.

22.9.3 Theme resolvers

After you define themes, as in the preceding section, you decide which theme to use. TheDispatcherServlet will look for a bean namedthemeResolver to find out whichThemeResolver implementation to use. A theme resolver works in much the same way as aLocaleResolver. It detects the theme to use for a particular request and can alsoalter the request’s theme. The following theme resolvers are provided by Spring:

Table 22.5. ThemeResolver implementations

ClassDescription

FixedThemeResolver

Selects a fixed theme, set using thedefaultThemeName property.

SessionThemeResolver

The theme is maintained in the user’s HTTP session. It only needs to be set once for each session, but is not persisted between sessions.

CookieThemeResolver

The selected theme is stored in a cookie on the client.


Spring also provides aThemeChangeInterceptor that allows theme changes on everyrequest with a simple request parameter.

22.10 Spring’s multipart (file upload) support

22.10.1 Introduction

Spring’s built-in multipart support handles file uploads in web applications. You enablethis multipart support with pluggableMultipartResolver objects, defined in theorg.springframework.web.multipart package. Spring provides oneMultipartResolverimplementation for use withCommonsFileUpload and another for use with Servlet 3.0 multipart request parsing.

By default, Spring does no multipart handling, because some developers want to handlemultiparts themselves. You enable Spring multipart handling by adding a multipartresolver to the web application’s context. Each request is inspected to see if itcontains a multipart. If no multipart is found, the request continues as expected. If amultipart is found in the request, theMultipartResolver that has been declared inyour context is used. After that, the multipart attribute in your request is treatedlike any other attribute.

22.10.2 Using a MultipartResolver withCommons FileUpload

The following example shows how to use theCommonsMultipartResolver:

<beanid="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- one of the properties available; the maximum file size in bytes --><propertyname="maxUploadSize"value="100000"/></bean>

Of course you also need to put the appropriate jars in your classpath for the multipartresolver to work. In the case of theCommonsMultipartResolver, you need to usecommons-fileupload.jar.

When the SpringDispatcherServlet detects a multi-part request, it activates theresolver that has been declared in your context and hands over the request. The resolverthen wraps the currentHttpServletRequest into aMultipartHttpServletRequest thatsupports multipart file uploads. Using theMultipartHttpServletRequest, you can getinformation about the multiparts contained by this request and actually get access tothe multipart files themselves in your controllers.

22.10.3 Using a MultipartResolver withServlet 3.0

In order to use Servlet 3.0 based multipart parsing, you need to mark theDispatcherServlet with a"multipart-config" section inweb.xml, or with ajavax.servlet.MultipartConfigElement in programmatic Servlet registration, or in caseof a custom Servlet class possibly with ajavax.servlet.annotation.MultipartConfigannotation on your Servlet class. Configuration settings such as maximum sizes orstorage locations need to be applied at that Servlet registration level as Servlet 3.0does not allow for those settings to be done from the MultipartResolver.

Once Servlet 3.0 multipart parsing has been enabled in one of the above mentioned waysyou can add theStandardServletMultipartResolver to your Spring configuration:

<beanid="multipartResolver"class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>

22.10.4 Handling a file upload in a form

After theMultipartResolver completes its job, the request is processed like anyother. First, create a form with a file input that will allow the user to upload a form.The encoding attribute (enctype="multipart/form-data") lets the browser know how toencode the form as multipart request:

<html><head><title>Upload a file please</title></head><body><h1>Please upload a file</h1><formmethod="post"action="/form"enctype="multipart/form-data"><inputtype="text"name="name"/><inputtype="file"name="file"/><inputtype="submit"/></form></body></html>

The next step is to create a controller that handles the file upload. This controller isvery similar to anormal annotated@Controller, except that weuseMultipartHttpServletRequest orMultipartFile in the method parameters:

@Controllerpublicclass FileUploadController {@PostMapping("/form")public String handleFormUpload(@RequestParam("name") String name,@RequestParam("file") MultipartFile file) {if (!file.isEmpty()) {byte[] bytes = file.getBytes();// store the bytes somewherereturn"redirect:uploadSuccess";        }return"redirect:uploadFailure";    }}

Note how the@RequestParam method parameters map to the input elements declared in theform. In this example, nothing is done with thebyte[], but in practice you can saveit in a database, store it on the file system, and so on.

When using Servlet 3.0 multipart parsing you can also usejavax.servlet.http.Part forthe method parameter:

@Controllerpublicclass FileUploadController {@PostMapping("/form")public String handleFormUpload(@RequestParam("name") String name,@RequestParam("file") Part file) {        InputStream inputStream = file.getInputStream();// store bytes from uploaded file somewherereturn"redirect:uploadSuccess";    }}

22.10.5 Handling a file upload request from programmatic clients

Multipart requests can also be submitted from non-browser clients in a RESTful servicescenario. All of the above examples and configuration apply here as well. However,unlike browsers that typically submit files and simple form fields, a programmaticclient can also send more complex data of a specific content type — for example amultipart request with a file and second part with JSON formatted data:

POST /someUrlContent-Type: multipart/mixed--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7VpContent-Disposition: form-data; name="meta-data"Content-Type: application/json; charset=UTF-8Content-Transfer-Encoding: 8bit{"name": "value"}--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7VpContent-Disposition: form-data; name="file-data"; filename="file.properties"Content-Type: text/xmlContent-Transfer-Encoding: 8bit... File Data ...

You could access the part named "meta-data" with a@RequestParam("meta-data") Stringmetadata controller method argument. However, you would probably prefer to accept astrongly typed object initialized from the JSON formatted data in the body of therequest part, very similar to the way@RequestBody converts the body of anon-multipart request to a target object with the help of anHttpMessageConverter.

You can use the@RequestPart annotation instead of the@RequestParam annotation forthis purpose. It allows you to have the content of a specific multipart passed throughanHttpMessageConverter taking into consideration the'Content-Type' header of themultipart:

@PostMapping("/someUrl")public String onSubmit(@RequestPart("meta-data") MetaData metadata,        @RequestPart("file-data") MultipartFile file) {// ...}

Notice howMultipartFile method arguments can be accessed with@RequestParam or with@RequestPart interchangeably. However, the@RequestPart("meta-data") MetaData methodargument in this case is read as JSON content based on its'Content-Type' header andconverted with the help of theMappingJackson2HttpMessageConverter.

22.11 Handling exceptions

22.11.1 HandlerExceptionResolver

SpringHandlerExceptionResolver implementations deal with unexpected exceptions thatoccur during controller execution. AHandlerExceptionResolver somewhat resembles theexception mappings you can define in the web application descriptorweb.xml. However,they provide a more flexible way to do so. For example they provide information aboutwhich handler was executing when the exception was thrown. Furthermore, a programmaticway of handling exceptions gives you more options for responding appropriately beforethe request is forwarded to another URL (the same end result as when you use the Servletspecific exception mappings).

Besides implementing theHandlerExceptionResolver interface, which is only a matter ofimplementing theresolveException(Exception, Handler) method and returning aModelAndView, you may also use the providedSimpleMappingExceptionResolver or create@ExceptionHandler methods. TheSimpleMappingExceptionResolver enables you to takethe class name of any exception that might be thrown and map it to a view name. This isfunctionally equivalent to the exception mapping feature from the Servlet API, but it isalso possible to implement more finely grained mappings of exceptions from differenthandlers. The@ExceptionHandler annotation on the other hand can be used on methodsthat should be invoked to handle an exception. Such methods may be defined locallywithin an@Controller or may apply to many@Controller classes when defined within an@ControllerAdvice class. The following sections explain this in more detail.

22.11.2 @ExceptionHandler

TheHandlerExceptionResolver interface and theSimpleMappingExceptionResolverimplementations allow you to map Exceptions to specific views declaratively along withsome optional Java logic before forwarding to those views. However, in some cases,especially when relying on@ResponseBody methods rather than on view resolution, itmay be more convenient to directly set the status of the response and optionally writeerror content to the body of the response.

You can do that with@ExceptionHandler methods. When declared within a controller suchmethods apply to exceptions raised by@RequestMapping methods of that controller (orany of its subclasses). You can also declare an@ExceptionHandler method within an@ControllerAdvice class in which case it handles exceptions from@RequestMappingmethods from many controllers. Below is an example of a controller-local@ExceptionHandler method:

@Controllerpublicclass SimpleController {// @RequestMapping methods omitted ...@ExceptionHandler(IOException.class)public ResponseEntity<String> handleIOException(IOException ex) {// prepare responseEntityreturn responseEntity;    }}

The@ExceptionHandler value can be set to an array of Exception types. If an exceptionis thrown that matches one of the types in the list, then the method annotated with thematching@ExceptionHandler will be invoked. If the annotation value is not set thenthe exception types listed as method arguments are used.

[Tip]Tip

For@ExceptionHandler methods, a root exception match will be preferred to justmatching a cause of the current exception, among the handler methods of a particularcontroller or advice bean. However, a cause match on a higher-priority@ControllerAdvicewill still be preferred to a any match (whether root or cause level) on a lower-priorityadvice bean. As a consequence, when using a multi-advice arrangement, please declare yourprimary root exception mappings on a prioritized advice bean with a corresponding order!

Much like standard controller methods annotated with a@RequestMapping annotation, themethod arguments and return values of@ExceptionHandler methods can be flexible. Forexample, theHttpServletRequest can be accessed in Servlet environments and thePortletRequest in Portlet environments. The return type can be aString, which isinterpreted as a view name, aModelAndView object, aResponseEntity, or you can alsoadd the@ResponseBody to have the method return value converted with messageconverters and written to the response stream.

22.11.3 Handling Standard Spring MVC Exceptions

Spring MVC may raise a number of exceptions while processing a request. TheSimpleMappingExceptionResolver can easily map any exception to a default error view asneeded. However, when working with clients that interpret responses in an automated wayyou will want to set specific status code on the response. Depending on the exceptionraised the status code may indicate a client error (4xx) or a server error (5xx).

TheDefaultHandlerExceptionResolver translates Spring MVC exceptions to specific errorstatus codes. It is registered by default with the MVC namespace, the MVC Java config,and also by theDispatcherServlet (i.e. when not using the MVC namespace or Javaconfig). Listed below are some of the exceptions handled by this resolver and thecorresponding status codes:

ExceptionHTTP Status Code

BindException

400 (Bad Request)

ConversionNotSupportedException

500 (Internal Server Error)

HttpMediaTypeNotAcceptableException

406 (Not Acceptable)

HttpMediaTypeNotSupportedException

415 (Unsupported Media Type)

HttpMessageNotReadableException

400 (Bad Request)

HttpMessageNotWritableException

500 (Internal Server Error)

HttpRequestMethodNotSupportedException

405 (Method Not Allowed)

MethodArgumentNotValidException

400 (Bad Request)

MissingPathVariableException

500 (Internal Server Error)

MissingServletRequestParameterException

400 (Bad Request)

MissingServletRequestPartException

400 (Bad Request)

NoHandlerFoundException

404 (Not Found)

NoSuchRequestHandlingMethodException

404 (Not Found)

TypeMismatchException

400 (Bad Request)

TheDefaultHandlerExceptionResolver works transparently by setting the status of theresponse. However, it stops short of writing any error content to the body of theresponse while your application may need to add developer-friendly content to everyerror response for example when providing a REST API. You can prepare aModelAndViewand render error content through view resolution — i.e. by configuring aContentNegotiatingViewResolver,MappingJackson2JsonView, and so on. However, you mayprefer to use@ExceptionHandler methods instead.

If you prefer to write error content via@ExceptionHandler methods you can extendResponseEntityExceptionHandler instead. This is a convenient base for@ControllerAdvice classes providing an@ExceptionHandler method to handle standardSpring MVC exceptions and returnResponseEntity. That allows you to customize theresponse and write error content with message converters. See theResponseEntityExceptionHandler javadocs for more details.

22.11.4 Annotating Business Exceptions With @ResponseStatus

A business exception can be annotated with@ResponseStatus. When the exception israised, theResponseStatusExceptionResolver handles it by setting the status of theresponse accordingly. By default theDispatcherServlet registers theResponseStatusExceptionResolver and it is available for use.

22.11.5 Customizing the Default Servlet Container Error Page

When the status of the response is set to an error status code and the body of theresponse is empty, Servlet containers commonly render an HTML formatted error page. Tocustomize the default error page of the container, you can declare an<error-page>element inweb.xml. Up until Servlet 3, that element had to be mapped to a specificstatus code or exception type. Starting with Servlet 3 an error page does not need to bemapped, which effectively means the specified location customizes the default Servletcontainer error page.

<error-page><location>/error</location></error-page>

Note that the actual location for the error page can be a JSP page or some other URLwithin the container including one handled through an@Controller method:

When writing error information, the status code and the error message set on theHttpServletResponse can be accessed through request attributes in a controller:

@Controllerpublicclass ErrorController {@RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic Map<String, Object> handle(HttpServletRequest request) {        Map<String, Object> map =new HashMap<String, Object>();        map.put("status", request.getAttribute("javax.servlet.error.status_code"));        map.put("reason", request.getAttribute("javax.servlet.error.message"));return map;    }}

or in a JSP:

<%@pagecontentType="application/json"pageEncoding="UTF-8"%>{status:<%=request.getAttribute("javax.servlet.error.status_code")%>,reason:<%=request.getAttribute("javax.servlet.error.message")%>}

22.12 Web Security

TheSpring Security project provides featuresto protect web applications from malicious exploits. Check out the reference documentation in the sections on"CSRF protection","Security Response Headers", and also"Spring MVC Integration".Note that using Spring Security to secure the application is not necessarily required for all features.For example CSRF protection can be added simply by adding theCsrfFilter andCsrfRequestDataValueProcessor to your configuration. See theSpring MVC Showcasefor an example.

Another option is to use a framework dedicated to Web Security.HDIV is one such framework and integrates with Spring MVC.

22.13 Convention over configuration support

For a lot of projects, sticking to established conventions and having reasonabledefaults is just what they (the projects) need, and Spring Web MVC now has explicitsupport forconvention over configuration. What this means is that if you establisha set of naming conventions and suchlike, you cansubstantially cut down on theamount of configuration that is required to set up handler mappings, view resolvers,ModelAndView instances, etc. This is a great boon with regards to rapid prototyping,and can also lend a degree of (always good-to-have) consistency across a codebase shouldyou choose to move forward with it into production.

Convention-over-configuration support addresses the three core areas of MVC: models,views, and controllers.

22.13.1 The Controller ControllerClassNameHandlerMapping

TheControllerClassNameHandlerMapping class is aHandlerMapping implementation thatuses a convention to determine the mapping between request URLs and theControllerinstances that are to handle those requests.

Consider the following simpleController implementation. Take special notice of thename of the class.

publicclassViewShoppingCartControllerimplements Controller {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {// the implementation is not hugely important for this example...    }}

Here is a snippet from the corresponding Spring Web MVC configuration file:

<beanclass="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/><beanid="viewShoppingCart"><!-- inject dependencies as required... --></bean>

TheControllerClassNameHandlerMapping finds all of the various handler (orController) beans defined in its application context and stripsController off thename to define its handler mappings. Thus,ViewShoppingCartController maps to the/viewshoppingcart* request URL.

Let’s look at some more examples so that the central idea becomes immediately familiar.(Notice all lowercase in the URLs, in contrast to camel-casedController class names.)

  • WelcomeController maps to the/welcome* request URL
  • HomeController maps to the/home* request URL
  • IndexController maps to the/index* request URL
  • RegisterController maps to the/register* request URL

In the case ofMultiActionController handler classes, the mappings generated areslightly more complex. TheController names in the following examples are assumed tobeMultiActionController implementations:

  • AdminController maps to the/admin/* request URL
  • CatalogController maps to the/catalog/* request URL

If you follow the convention of naming yourController implementations asxxxController, theControllerClassNameHandlerMapping saves you the tedium ofdefining and maintaining a potentiallylooooongSimpleUrlHandlerMapping (orsuchlike).

TheControllerClassNameHandlerMapping class extends theAbstractHandlerMapping baseclass so you can defineHandlerInterceptor instances and everything else just as youwould with many otherHandlerMapping implementations.

22.13.2 The Model ModelMap (ModelAndView)

TheModelMap class is essentially a glorifiedMap that can make adding objects thatare to be displayed in (or on) aView adhere to a common naming convention. Considerthe followingController implementation; notice that objects are added to theModelAndView without any associated name specified.

publicclass DisplayShoppingCartControllerimplements Controller {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {        List cartItems =// get a List of CartItem objects        User user =// get the User doing the shopping        ModelAndView mav =new ModelAndView("displayShoppingCart"); <-- the logical view name        mav.addObject(cartItems); <-- look ma, no name, just the object        mav.addObject(user); <-- and again ma!return mav;    }}

TheModelAndView class uses aModelMap class that is a customMap implementationthat automatically generates a key for an object when an object is added to it. Thestrategy for determining the name for an added object is, in the case of a scalar objectsuch asUser, to use the short class name of the object’s class. The followingexamples are names that are generated for scalar objects put into aModelMap instance.

  • Anx.y.User instance added will have the nameuser generated.
  • Anx.y.Registration instance added will have the nameregistration generated.
  • Anx.y.Foo instance added will have the namefoo generated.
  • Ajava.util.HashMap instance added will have the namehashMap generated. Youprobably want to be explicit about the name in this case becausehashMap is lessthan intuitive.
  • Addingnull will result in anIllegalArgumentException being thrown. If the object(or objects) that you are adding could benull, then you will also want to beexplicit about the name.

What, no automatic pluralization?

Spring Web MVC’s convention-over-configuration support does not support automaticpluralization. That is, you cannot add aList ofPerson objects to aModelAndViewand have the generated name bepeople.

This decision was made after some debate, with the "Principle of Least Surprise" winningout in the end.

The strategy for generating a name after adding aSet or aList is to peek into thecollection, take the short class name of the first object in the collection, and usethat withList appended to the name. The same applies to arrays although with arraysit is not necessary to peek into the array contents. A few examples will make thesemantics of name generation for collections clearer:

  • Anx.y.User[] array with zero or morex.y.User elements added will have the nameuserList generated.
  • Anx.y.Foo[] array with zero or morex.y.User elements added will have the namefooList generated.
  • Ajava.util.ArrayList with one or morex.y.User elements added will have the nameuserList generated.
  • Ajava.util.HashSet with one or morex.y.Foo elements added will have the namefooList generated.
  • Anemptyjava.util.ArrayList will not be added at all (in effect, theaddObject(..) call will essentially be a no-op).

22.13.3 Default view name

TheRequestToViewNameTranslator interface determines a logicalView name when nosuch logical view name is explicitly supplied. It has just one implementation, theDefaultRequestToViewNameTranslator class.

TheDefaultRequestToViewNameTranslator maps request URLs to logical view names, aswith this example:

publicclass RegistrationControllerimplements Controller {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {// process the request...        ModelAndView mav =new ModelAndView();// add data as necessary to the model...return mav;// notice that no View or logical view name has been set    }}
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- this bean with the well known name generates view names for us --><beanid="viewNameTranslator"class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/><beanclass="x.y.RegistrationController"><!-- inject dependencies as necessary --></bean><!-- maps request URLs to Controller names --><beanclass="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/><beanid="viewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/jsp/"/><propertyname="suffix"value=".jsp"/></bean></beans>

Notice how in the implementation of thehandleRequest(..) method noView or logicalview name is ever set on theModelAndView that is returned. TheDefaultRequestToViewNameTranslator is tasked with generating alogical view namefrom the URL of the request. In the case of the aboveRegistrationController, which isused in conjunction with theControllerClassNameHandlerMapping, a request URL ofhttp://localhost/registration.html results in a logical view name ofregistrationbeing generated by theDefaultRequestToViewNameTranslator. This logical view name isthen resolved into the/WEB-INF/jsp/registration.jsp view by theInternalResourceViewResolver bean.

[Tip]Tip

You do not need to define aDefaultRequestToViewNameTranslator bean explicitly. If youlike the default settings of theDefaultRequestToViewNameTranslator, you can rely onthe Spring Web MVCDispatcherServlet to instantiate an instance of this class if oneis not explicitly configured.

Of course, if you need to change the default settings, then you do need to configureyour ownDefaultRequestToViewNameTranslator bean explicitly. Consult the comprehensiveDefaultRequestToViewNameTranslator javadocs for details on the various propertiesthat can be configured.

22.14 HTTP caching support

A good HTTP caching strategy can significantly improve the performance of a web applicationand the experience of its clients. The'Cache-Control' HTTP response header is mostlyresponsible for this, along with conditional headers such as'Last-Modified' and'ETag'.

The'Cache-Control' HTTP response header advises private caches (e.g. browsers) andpublic caches (e.g. proxies) on how they can cache HTTP responses for further reuse.

AnETag (entity tag) is an HTTP response headerreturned by an HTTP/1.1 compliant web server used to determine change in content at agiven URL. It can be considered to be the more sophisticated successor to theLast-Modified header. When a server returns a representation with an ETag header, theclient can use this header in subsequent GETs, in anIf-None-Match header. If thecontent has not changed, the server returns304: Not Modified.

This section describes the different choices available to configure HTTP caching in aSpring Web MVC application.

22.14.1 Cache-Control HTTP header

Spring Web MVC supports many use cases and ways to configure "Cache-Control" headers foran application. WhileRFC 7234 Section 5.2.2completely describes that header and its possible directives, there are several ways toaddress the most common cases.

Spring Web MVC uses a configuration convention in several of its APIs:setCachePeriod(int seconds):

  • A-1 value won’t generate a'Cache-Control' response header.
  • A0 value will prevent caching using the'Cache-Control: no-store' directive.
  • Ann > 0 value will cache the given response forn seconds using the'Cache-Control: max-age=n' directive.

TheCacheControl builderclass simply describes the available "Cache-Control" directives and makes it easier tobuild your own HTTP caching strategy. Once built, aCacheControl instance can then beaccepted as an argument in several Spring Web MVC APIs.

// Cache for an hour - "Cache-Control: max-age=3600"CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);// Prevent caching - "Cache-Control: no-store"CacheControl ccNoStore = CacheControl.noStore();// Cache for ten days in public and private caches,// public caches should not transform the response// "Cache-Control: max-age=864000, public, no-transform"CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS)                                    .noTransform().cachePublic();

22.14.2 HTTP caching support for static resources

Static resources should be served with appropriate'Cache-Control' and conditionalheaders for optimal performance.Configuring aResourceHttpRequestHandler for servingstatic resources not only natively writes'Last-Modified' headers by reading a file’smetadata, but also'Cache-Control' headers if properly configured.

You can set thecachePeriod attribute on aResourceHttpRequestHandler or useaCacheControl instance, which supports more specific directives:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**")                .addResourceLocations("/public-resources/")                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());    }}

And in XML:

<mvc:resourcesmapping="/resources/**"location="/public-resources/"><mvc:cache-controlmax-age="3600"cache-public="true"/></mvc:resources>

22.14.3 Support for the Cache-Control, ETag and Last-Modified response headers in Controllers

Controllers can support'Cache-Control','ETag', and/or'If-Modified-Since' HTTP requests;this is indeed recommended if a'Cache-Control' header is to be set on the response.This involves calculating a lastModifiedlong and/or an Etag value for a given request,comparing it against the'If-Modified-Since' request header value, and potentially returninga response with status code 304 (Not Modified).

As described inthe section called “Using HttpEntity”, controllers can interact with the request/response usingHttpEntity types. Controllers returningResponseEntity can include HTTP caching informationin responses like this:

@GetMapping("/book/{id}")public ResponseEntity<Book> showBook(@PathVariable Long id) {    Book book = findBook(id);    String version = book.getVersion();return ResponseEntity                .ok()                .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))                .eTag(version)// lastModified is also available                .body(book);}

Doing this will not only include'ETag' and'Cache-Control' headers in the response, it willalso convert theresponse to anHTTP 304 Not Modified response with an empty body if the conditional headers sent by the clientmatch the caching information set by the Controller.

An@RequestMapping method may also wish to support the same behavior.This can be achieved as follows:

@RequestMappingpublic String myHandleMethod(WebRequest webRequest, Model model) {long lastModified =// 1. application-specific calculationif (request.checkNotModified(lastModified)) {// 2. shortcut exit - no further processing necessaryreturn null;    }// 3. or otherwise further request processing, actually preparing content    model.addAttribute(...);return"myViewName";}

There are two key elements here: callingrequest.checkNotModified(lastModified) andreturningnull. The former sets the appropriate response status and headersbefore it returnstrue.The latter, in combination with the former, causes Spring MVC to do no furtherprocessing of the request.

Note that there are 3 variants for this:

  • request.checkNotModified(lastModified) compares lastModified with the'If-Modified-Since' or'If-Unmodified-Since' request header
  • request.checkNotModified(eTag) compares eTag with the'If-None-Match' request header
  • request.checkNotModified(eTag, lastModified) does both, meaning that bothconditions should be valid

When receiving conditional'GET'/'HEAD' requests,checkNotModified will checkthat the resource has not been modified and if so, it will result in aHTTP 304 Not Modifiedresponse. In case of conditional'POST'/'PUT'/'DELETE' requests,checkNotModifiedwill check that the resource has not been modified and if it has been, it will result in aHTTP 409 Precondition Failed response to prevent concurrent modifications.

22.14.4 Shallow ETag support

Support for ETags is provided by the Servlet filterShallowEtagHeaderFilter. It is aplain Servlet Filter, and thus can be used in combination with any web framework. TheShallowEtagHeaderFilter filter creates so-called shallow ETags (as opposed to deepETags, more about that later).The filter caches the content of the rendered JSP (orother content), generates an MD5 hash over that, and returns that as an ETag header inthe response. The next time a client sends a request for the same resource, it uses thathash as theIf-None-Match value. The filter detects this, renders the view again, andcompares the two hashes. If they are equal, a304 is returned.

Note that this strategy saves network bandwidth but not CPU, as the full response must becomputed for each request. Other strategies at the controller level (described above) cansave network bandwidth and avoid computation.

This filter has awriteWeakETag parameter that configures the filter to write Weak ETags,like this:W/"02a2d595e6ed9a0b24f027f2b63b134d6", as defined inRFC 7232 Section 2.3.

You configure theShallowEtagHeaderFilter inweb.xml:

<filter><filter-name>etagFilter</filter-name><filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class><!-- Optional parameter that configures the filter to write weak ETags    <init-param>        <param-name>writeWeakETag</param-name>        <param-value>true</param-value>    </init-param>    --></filter><filter-mapping><filter-name>etagFilter</filter-name><servlet-name>petclinic</servlet-name></filter-mapping>

Or in Servlet 3.0+ environments,

publicclass MyWebAppInitializerextends AbstractDispatcherServletInitializer {// ...@Overrideprotected Filter[] getServletFilters() {returnnew Filter[] {new ShallowEtagHeaderFilter() };    }}

SeeSection 22.15, “Code-based Servlet container initialization” for more details.

22.15 Code-based Servlet container initialization

In a Servlet 3.0+ environment, you have the option of configuring the Servlet containerprogrammatically as an alternative or in combination with aweb.xml file. Below is anexample of registering aDispatcherServlet:

import org.springframework.web.WebApplicationInitializer;publicclass MyWebApplicationInitializerimplements WebApplicationInitializer {@Overridepublicvoid onStartup(ServletContext container) {        XmlWebApplicationContext appContext =new XmlWebApplicationContext();        appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");        ServletRegistration.Dynamic registration = container.addServlet("dispatcher",new DispatcherServlet(appContext));        registration.setLoadOnStartup(1);        registration.addMapping("/");    }}

WebApplicationInitializer is an interface provided by Spring MVC that ensures yourimplementation is detected and automatically used to initialize any Servlet 3 container.An abstract base class implementation ofWebApplicationInitializer namedAbstractDispatcherServletInitializer makes it even easier to register theDispatcherServlet by simply overriding methods to specify the servlet mapping and thelocation of theDispatcherServlet configuration.

This is recommended for applications that use Java-based Spring configuration:

publicclass MyWebAppInitializerextends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return null;    }@Overrideprotected Class<?>[] getServletConfigClasses() {returnnew Class[] { MyWebConfig.class };    }@Overrideprotected String[] getServletMappings() {returnnew String[] {"/" };    }}

If using XML-based Spring configuration, you should extend directly fromAbstractDispatcherServletInitializer:

publicclass MyWebAppInitializerextends AbstractDispatcherServletInitializer {@Overrideprotected WebApplicationContext createRootApplicationContext() {return null;    }@Overrideprotected WebApplicationContext createServletApplicationContext() {        XmlWebApplicationContext cxt =new XmlWebApplicationContext();        cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");return cxt;    }@Overrideprotected String[] getServletMappings() {returnnew String[] {"/" };    }}

AbstractDispatcherServletInitializer also provides a convenient way to addFilterinstances and have them automatically mapped to theDispatcherServlet:

publicclass MyWebAppInitializerextends AbstractDispatcherServletInitializer {// ...@Overrideprotected Filter[] getServletFilters() {returnnew Filter[] {new HiddenHttpMethodFilter(),new CharacterEncodingFilter() };    }}

Each filter is added with a default name based on its concrete type and automaticallymapped to theDispatcherServlet.

TheisAsyncSupported protected method ofAbstractDispatcherServletInitializerprovides a single place to enable async support on theDispatcherServlet and allfilters mapped to it. By default this flag is set totrue.

Finally, if you need to further customize theDispatcherServlet itself, you canoverride thecreateDispatcherServlet method.

22.16 Configuring Spring MVC

Section 22.2.1, “Special Bean Types In the WebApplicationContext” andSection 22.2.2, “Default DispatcherServlet Configuration” explained about SpringMVC’s special beans and the default implementations used by theDispatcherServlet. Inthis section you’ll learn about two additional ways of configuring Spring MVC. Namelythe MVC Java config and the MVC XML namespace.

The MVC Java config and the MVC namespace provide similar default configuration thatoverrides theDispatcherServlet defaults. The goal is to spare most applications fromhaving to create the same configuration and also to provide higher-level constructs forconfiguring Spring MVC that serve as a simple starting point and require little or noprior knowledge of the underlying configuration.

You can choose either the MVC Java config or the MVC namespace depending on yourpreference. Also as you will see further below, with the MVC Java config it is easier tosee the underlying configuration as well as to make fine-grained customizations directlyto the created Spring MVC beans. But let’s start from the beginning.

22.16.1 Enabling the MVC Java Config or the MVC XML Namespace

To enable MVC Java config add the annotation@EnableWebMvc to one of your@Configuration classes:

@Configuration@EnableWebMvcpublicclass WebConfig {}

To achieve the same in XML use themvc:annotation-driven element in yourDispatcherServlet context (or in your root context if you have no DispatcherServletcontext defined):

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc.xsd"><mvc:annotation-driven/></beans>

The above registers aRequestMappingHandlerMapping, aRequestMappingHandlerAdapter,and anExceptionHandlerExceptionResolver (among others) in support of processingrequests with annotated controller methods using annotations such as@RequestMapping,@ExceptionHandler, and others.

It also enables the following:

  1. Spring 3 style type conversion through aConversionService instancein addition to the JavaBeans PropertyEditors used for Data Binding.
  2. Support forformatting Number fields using the@NumberFormat annotationthrough theConversionService.
  3. Support forformattingDate,Calendar,Long, and Joda Time fields using the@DateTimeFormat annotation.
  4. Support forvalidating@Controller inputs with@Valid, ifa JSR-303 Provider is present on the classpath.
  5. HttpMessageConverter support for@RequestBody method parameters and@ResponseBodymethod return values from@RequestMapping or@ExceptionHandler methods.

    This is the complete list of HttpMessageConverters set up by mvc:annotation-driven:

    1. ByteArrayHttpMessageConverter converts byte arrays.
    2. StringHttpMessageConverter converts strings.
    3. ResourceHttpMessageConverter converts to/fromorg.springframework.core.io.Resource for all media types.
    4. SourceHttpMessageConverter converts to/from ajavax.xml.transform.Source.
    5. FormHttpMessageConverter converts form data to/from aMultiValueMap<String,String>.
    6. Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added ifJAXB2 is present and Jackson 2 XML extension is not present on the classpath.
    7. MappingJackson2HttpMessageConverter converts to/from JSON — added if Jackson 2is present on the classpath.
    8. MappingJackson2XmlHttpMessageConverter converts to/from XML — added ifJackson 2 XML extension is presenton the classpath.
    9. AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on theclasspath.
    10. RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present onthe classpath.

SeeSection 22.16.12, “Message Converters” for more information about how to customize thesedefault converters.

[Note]Note

Jackson JSON and XML converters are created usingObjectMapper instances created byJackson2ObjectMapperBuilderin order to provide a better default configuration.

This builder customizes Jackson’s default properties with the following ones:

It also automatically registers the following well-known modules if they are detected on the classpath:

  1. jackson-datatype-jdk7: support for Java 7 types likejava.nio.file.Path.
  2. jackson-datatype-joda: support for Joda-Time types.
  3. jackson-datatype-jsr310: support for Java 8 Date & Time API types.
  4. jackson-datatype-jdk8: support for other Java 8 types likeOptional.

22.16.2 Customizing the Provided Configuration

To customize the default configuration in Java you simply implement theWebMvcConfigurer interface or more likely extend the classWebMvcConfigurerAdapterand override the methods you need:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {// Override configuration methods...}

To customize the default configuration of<mvc:annotation-driven/> check whatattributes and sub-elements it supports. You can view theSpring MVC XML schema or use the codecompletion feature of your IDE to discover what attributes and sub-elements areavailable.

22.16.3 Conversion and Formatting

By default formatters forNumber andDate types are installed, including support forthe@NumberFormat and@DateTimeFormat annotations. Full support for the Joda Timeformatting library is also installed if Joda Time is present on the classpath. Toregister custom formatters and converters, override theaddFormatters method:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addFormatters(FormatterRegistry registry) {// ...    }}

In the MVC namespace the same defaults apply when<mvc:annotation-driven> is added.To register custom formatters and converters simply supply aConversionService:

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc.xsd"><mvc:annotation-drivenconversion-service="conversionService"/><beanid="conversionService"class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><propertyname="converters"><set><beanclass="org.example.MyConverter"/></set></property><propertyname="formatters"><set><beanclass="org.example.MyFormatter"/><beanclass="org.example.MyAnnotationFormatterFactory"/></set></property><propertyname="formatterRegistrars"><set><beanclass="org.example.MyFormatterRegistrar"/></set></property></bean></beans>
[Note]Note

SeeSection 9.6.4, “FormatterRegistrar SPI” and theFormattingConversionServiceFactoryBeanfor more information on when to use FormatterRegistrars.

22.16.4 Validation

Spring provides aValidator interface that can be used for validation in all layersof an application. In Spring MVC you can configure it for use as a globalValidator instance, to be usedwhenever an@Valid or@Validated controller method argument is encountered, and/or as a localValidator within a controller through an@InitBinder method. Global and local validatorinstances can be combined to provide composite validation.

Spring alsosupports JSR-303/JSR-349 Bean ValidationviaLocalValidatorFactoryBean which adapts the Springorg.springframework.validation.Validatorinterface to the Bean Validationjavax.validation.Validator contract. This class can beplugged into Spring MVC as a global validator as described next.

By default use of@EnableWebMvc or<mvc:annotation-driven> automatically registers BeanValidation support in Spring MVC through theLocalValidatorFactoryBean when a Bean Validationprovider such as Hibernate Validator is detected on the classpath.

[Note]Note

Sometimes it’s convenient to have aLocalValidatorFactoryBean injected into a controlleror another class. The easiest way to do that is to declare your own@Bean and also mark itwith@Primary in order to avoid a conflict with the one provided with the MVC Java config.

If you prefer to use the one from the MVC Java config, you’ll need to override themvcValidator method fromWebMvcConfigurationSupport and declare the method to explicitlyreturnLocalValidatorFactory rather thanValidator. SeeSection 22.16.13, “Advanced Customizations with MVC Java Config”for information on how to switch to extend the provided configuration.

Alternatively you can configure your own globalValidator instance:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublic Validator getValidator(); {// return "global" validator    }}

and in XML:

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc.xsd"><mvc:annotation-drivenvalidator="globalValidator"/></beans>

To combine global with local validation, simply add one or more local validator(s):

@Controllerpublicclass MyController {@InitBinderprotectedvoid initBinder(WebDataBinder binder) {        binder.addValidators(new FooValidator());    }}

With this minimal configuration any time an@Valid or@Validated method argument is encountered, itwill be validated by the configured validators. Any validation violations will automaticallybe exposed as errors in theBindingResult accessible as a method argument and also renderablein Spring MVC HTML views.

22.16.5 Interceptors

You can configureHandlerInterceptors orWebRequestInterceptors to be applied to allincoming requests or restricted to specific URL path patterns.

An example of registering interceptors in Java:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new LocaleInterceptor());        registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");    }}

And in XML use the<mvc:interceptors> element:

<mvc:interceptors><beanclass="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/><mvc:interceptor><mvc:mappingpath="/**"/><mvc:exclude-mappingpath="/admin/**"/><beanclass="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/></mvc:interceptor><mvc:interceptor><mvc:mappingpath="/secure/*"/><beanclass="org.example.SecurityInterceptor"/></mvc:interceptor></mvc:interceptors>

22.16.6 Content Negotiation

You can configure how Spring MVC determines the requested media types from the request.The available options are to check the URL path for a file extension, check the"Accept" header, a specific query parameter, or to fall back on a default contenttype when nothing is requested. By default the path extension in the request URIis checked first and the "Accept" header is checked second.

The MVC Java config and the MVC namespace registerjson,xml,rss,atom bydefault if corresponding dependencies are on the classpath. Additionalpath extension-to-media type mappings may also be registered explicitly and thatalso has the effect of whitelisting them as safe extensions for the purpose of RFDattack detection (seethe section called “Suffix Pattern Matching and RFD” for more detail).

Below is an example of customizing content negotiation options through the MVC Java config:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configureContentNegotiation(ContentNegotiationConfigurer configurer) {        configurer.mediaType("json", MediaType.APPLICATION_JSON);    }}

In the MVC namespace, the<mvc:annotation-driven> element has acontent-negotiation-manager attribute, which expects aContentNegotiationManagerthat in turn can be created with aContentNegotiationManagerFactoryBean:

<mvc:annotation-drivencontent-negotiation-manager="contentNegotiationManager"/><beanid="contentNegotiationManager"class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"><propertyname="mediaTypes"><value>            json=application/json            xml=application/xml</value></property></bean>

If not using the MVC Java config or the MVC namespace, you’ll need to create an instanceofContentNegotiationManager and use it to configureRequestMappingHandlerMappingfor request mapping purposes, andRequestMappingHandlerAdapter andExceptionHandlerExceptionResolver for content negotiation purposes.

Note thatContentNegotiatingViewResolver now can also be configured with aContentNegotiationManager, so you can use one shared instance throughout Spring MVC.

In more advanced cases, it may be useful to configure multipleContentNegotiationManager instances that in turn may contain customContentNegotiationStrategy implementations. For example you could configureExceptionHandlerExceptionResolver with aContentNegotiationManager that alwaysresolves the requested media type to"application/json". Or you may want to plug acustom strategy that has some logic to select a default content type (e.g. either XML orJSON) if no content types were requested.

22.16.7 View Controllers

This is a shortcut for defining aParameterizableViewController that immediatelyforwards to a view when invoked. Use it in static cases when there is no Java controllerlogic to execute before the view generates the response.

An example of forwarding a request for"/" to a view called"home" in Java:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addViewControllers(ViewControllerRegistry registry) {        registry.addViewController("/").setViewName("home");    }}

And the same in XML use the<mvc:view-controller> element:

<mvc:view-controllerpath="/"view-name="home"/>

22.16.8 View Resolvers

The MVC config simplifies the registration of view resolvers.

The following is a Java config example that configures content negotiation viewresolution using FreeMarker HTML templates and Jackson as a defaultView forJSON rendering:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configureViewResolvers(ViewResolverRegistry registry) {        registry.enableContentNegotiation(new MappingJackson2JsonView());        registry.jsp();    }}

And the same in XML:

<mvc:view-resolvers><mvc:content-negotiation><mvc:default-views><beanclass="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/></mvc:default-views></mvc:content-negotiation><mvc:jsp/></mvc:view-resolvers>

Note however that FreeMarker, Velocity, Tiles, Groovy Markup and script templates also requireconfiguration of the underlying view technology.

The MVC namespace provides dedicated elements. For example with FreeMarker:

<mvc:view-resolvers><mvc:content-negotiation><mvc:default-views><beanclass="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/></mvc:default-views></mvc:content-negotiation><mvc:freemarkercache="false"/></mvc:view-resolvers><mvc:freemarker-configurer><mvc:template-loader-pathlocation="/freemarker"/></mvc:freemarker-configurer>

In Java config simply add the respective "Configurer" bean:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configureViewResolvers(ViewResolverRegistry registry) {        registry.enableContentNegotiation(new MappingJackson2JsonView());        registry.freeMarker().cache(false);    }@Beanpublic FreeMarkerConfigurer freeMarkerConfigurer() {        FreeMarkerConfigurer configurer =new FreeMarkerConfigurer();        configurer.setTemplateLoaderPath("/WEB-INF/");return configurer;    }}

22.16.9 Serving of Resources

This option allows static resource requests following a particular URL pattern to beserved by aResourceHttpRequestHandler from any of a list ofResource locations.This provides a convenient way to serve static resources from locations other than theweb application root, including locations on the classpath. Thecache-period propertymay be used to set far future expiration headers (1 year is the recommendation ofoptimization tools such as Page Speed and YSlow) so that they will be more efficientlyutilized by the client. The handler also properly evaluates theLast-Modified header(if present) so that a304 status code will be returned as appropriate, avoidingunnecessary overhead for resources that are already cached by the client. For example,to serve resource requests with a URL pattern of/resources/** from apublic-resources directory within the web application root you would use:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");    }}

And the same in XML:

<mvc:resourcesmapping="/resources/**"location="/public-resources/"/>

To serve these resources with a 1-year future expiration to ensure maximum use of thebrowser cache and a reduction in HTTP requests made by the browser:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926);    }}

And in XML:

<mvc:resourcesmapping="/resources/**"location="/public-resources/"cache-period="31556926"/>

For more details, seeHTTP caching support for static resources.

Themapping attribute must be an Ant pattern that can be used bySimpleUrlHandlerMapping, and thelocation attribute must specify one or more validresource directory locations. Multiple resource locations may be specified using acomma-separated list of values. The locations specified will be checked in the specifiedorder for the presence of the resource for any given request. For example, to enable theserving of resources from both the web application root and from a known path of/META-INF/public-web-resources/ in any jar on the classpath use:

@EnableWebMvc@Configurationpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**")                .addResourceLocations("/","classpath:/META-INF/public-web-resources/");    }}

And in XML:

<mvc:resourcesmapping="/resources/**"location="/, classpath:/META-INF/public-web-resources/"/>

When serving resources that may change when a new version of the application isdeployed it is recommended that you incorporate a version string into the mappingpattern used to request the resources so that you may force clients to request thenewly deployed version of your application’s resources. Support for versioned URLs isbuilt into the framework and can be enabled by configuring a resource chainon the resource handler. The chain consists of one moreResourceResolverinstances followed by one or moreResourceTransformer instances. Together theycan provide arbitrary resolution and transformation of resources.

The built-inVersionResourceResolver can be configured with different strategies.For example aFixedVersionStrategy can use a property, a date, or other as the version.AContentVersionStrategy uses an MD5 hash computed from the content of the resource(known as "fingerprinting" URLs). Note that theVersionResourceResolver will automaticallyuse the resolved version strings as HTTP ETag header values when serving resources.

ContentVersionStrategy is a good default choice to use except in cases whereit cannot be used (e.g. with JavaScript module loaders). You can configuredifferent version strategies against different patterns as shown below. Keep in mindalso that computing content-based versions is expensive and therefore resource chaincaching should be enabled in production.

Java config example;

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**")                .addResourceLocations("/public-resources/")                .resourceChain(true)                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));    }}

XML example:

<mvc:resourcesmapping="/resources/**"location="/public-resources/"><mvc:resource-chain><mvc:resource-cache/><mvc:resolvers><mvc:version-resolver><mvc:content-version-strategypatterns="/**"/></mvc:version-resolver></mvc:resolvers></mvc:resource-chain></mvc:resources>

In order for the above to work the application must alsorender URLs with versions. The easiest way to do that is to configure theResourceUrlEncodingFilter which wraps the response and overrides itsencodeURL method.This will work in JSPs, FreeMarker, Velocity, and any other view technology that callsthe responseencodeURL method. Alternatively, an application can also inject anduse directly theResourceUrlProvider bean, which is automatically declared with the MVCJava config and the MVC namespace.

Webjars are also supported withWebJarsResourceResolver, which is automatically registeredwhen the"org.webjars:webjars-locator" library is on classpath. This resolver allowsthe resource chain to resolve version agnostic libraries from HTTP GET requests"GET /jquery/jquery.min.js" will return resource"/jquery/1.2.0/jquery.min.js".It also works by rewriting resource URLs in templates<script src="/jquery/jquery.min.js"/> → <script src="/jquery/1.2.0/jquery.min.js"/>.

22.16.10 Default Servlet

This allows for mapping theDispatcherServlet to "/" (thus overriding the mappingof the container’s default Servlet), while still allowing static resource requests to behandled by the container’s default Servlet. It configures aDefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priorityrelative to other URL mappings.

This handler will forward all requests to the default Servlet. Therefore it is importantthat it remains last in the order of all other URLHandlerMappings. That will be thecase if you use<mvc:annotation-driven> or alternatively if you are setting up yourown customizedHandlerMapping instance be sure to set itsorder property to a valuelower than that of theDefaultServletHttpRequestHandler, which isInteger.MAX_VALUE.

To enable the feature using the default setup use:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {        configurer.enable();    }}

Or in XML:

<mvc:default-servlet-handler/>

The caveat to overriding the "/" Servlet mapping is that theRequestDispatcher for thedefault Servlet must be retrieved by name rather than by path. TheDefaultServletHttpRequestHandler will attempt to auto-detect the default Servlet forthe container at startup time, using a list of known names for most of the major Servletcontainers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere).If the default Servlet has been custom configured with a different name, or if adifferent Servlet container is being used where the default Servlet name is unknown,then the default Servlet’s name must be explicitly provided as in the following example:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {        configurer.enable("myCustomDefaultServlet");    }}

Or in XML:

<mvc:default-servlet-handlerdefault-servlet-name="myCustomDefaultServlet"/>

22.16.11 Path Matching

This allows customizing various settings related to URL mapping and path matching.For details on the individual options check out thePathMatchConfigurer API.

Below is an example in Java config:

@Configuration@EnableWebMvcpublicclass WebConfigextends WebMvcConfigurerAdapter {@Overridepublicvoid configurePathMatch(PathMatchConfigurer configurer) {        configurer            .setUseSuffixPatternMatch(true)            .setUseTrailingSlashMatch(false)            .setUseRegisteredSuffixPatternMatch(true)            .setPathMatcher(antPathMatcher())            .setUrlPathHelper(urlPathHelper());    }@Beanpublic UrlPathHelper urlPathHelper() {//...    }@Beanpublic PathMatcher antPathMatcher() {//...    }}

And the same in XML, use the<mvc:path-matching> element:

<mvc:annotation-driven><mvc:path-matchingsuffix-pattern="true"trailing-slash="false"registered-suffixes-only="true"path-helper="pathHelper"path-matcher="pathMatcher"/></mvc:annotation-driven><beanid="pathHelper"class="org.example.app.MyPathHelper"/><beanid="pathMatcher"class="org.example.app.MyPathMatcher"/>

22.16.12 Message Converters

Customization ofHttpMessageConverter can be achieved in Java config by overridingconfigureMessageConverters()if you want to replace the default converters created by Spring MVC, or by overridingextendMessageConverters()if you just want to customize them or add additional converters to the default ones.

Below is an example that adds Jackson JSON and XML converters with a customizedObjectMapper instead of default ones:

@Configuration@EnableWebMvcpublicclass WebConfigurationextends WebMvcConfigurerAdapter {@Overridepublicvoid configureMessageConverters(List<HttpMessageConverter<?>> converters) {        Jackson2ObjectMapperBuilder builder =new Jackson2ObjectMapperBuilder()                .indentOutput(true)                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))                .modulesToInstall(new ParameterNamesModule());        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));    }}

In this example,Jackson2ObjectMapperBuilder is used to create a common configuration forbothMappingJackson2HttpMessageConverter andMappingJackson2XmlHttpMessageConverter withindentation enabled, a customized date format and the registration ofjackson-module-parameter-namesthat adds support for accessing parameter names (feature added in Java 8).

[Note]Note

Enabling indentation with Jackson XML support requireswoodstox-core-asldependency in addition tojackson-dataformat-xml one.

Other interesting Jackson modules are available:

  1. jackson-datatype-money: support forjavax.money types (unofficial module)
  2. jackson-datatype-hibernate: support for Hibernate specific types and properties (including lazy-loading aspects)

It is also possible to do the same in XML:

<mvc:annotation-driven><mvc:message-converters><beanclass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><propertyname="objectMapper"ref="objectMapper"/></bean><beanclass="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter"><propertyname="objectMapper"ref="xmlMapper"/></bean></mvc:message-converters></mvc:annotation-driven><beanid="objectMapper"class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"p:indentOutput="true"p:simpleDateFormat="yyyy-MM-dd"p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/><beanid="xmlMapper"parent="objectMapper"p:createXmlMapper="true"/>

22.16.13 Advanced Customizations with MVC Java Config

As you can see from the above examples, MVC Java config and the MVC namespace providehigher level constructs that do not require deep knowledge of the underlying beanscreated for you. Instead it helps you to focus on your application needs. However, atsome point you may need more fine-grained control or you may simply wish to understandthe underlying configuration.

The first step towards more fine-grained control is to see the underlying beans createdfor you. In MVC Java config you can see the javadocs and the@Bean methods inWebMvcConfigurationSupport. The configuration in this class is automatically importedthrough the@EnableWebMvc annotation. In fact if you open@EnableWebMvc you can seethe@Import statement.

The next step towards more fine-grained control is to customize a property on one of thebeans created inWebMvcConfigurationSupport or perhaps to provide your own instance.This requires two things — remove the@EnableWebMvc annotation in order to preventthe import and then extend fromDelegatingWebMvcConfiguration, a subclass ofWebMvcConfigurationSupport.Here is an example:

@Configurationpublicclass WebConfigextends DelegatingWebMvcConfiguration {@Overridepublicvoid addInterceptors(InterceptorRegistry registry){// ...    }@Override@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter() {// Create or let "super" create the adapter// Then customize one of its properties    }}
[Note]Note

An application should have only one configuration extendingDelegatingWebMvcConfigurationor a single@EnableWebMvc annotated class, since they both register the same underlyingbeans.

Modifying beans in this way does not prevent you from using any of the higher-levelconstructs shown earlier in this section.WebMvcConfigurerAdapter subclasses andWebMvcConfigurer implementations are still being used.

22.16.14 Advanced Customizations with the MVC Namespace

Fine-grained control over the configuration created for you is a bit harder with the MVCnamespace.

If you do need to do that, rather than replicating the configuration it provides,consider configuring aBeanPostProcessor that detects the bean you want to customizeby type and then modifying its properties as necessary. For example:

@Componentpublicclass MyPostProcessorimplements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String name)throws BeansException {if (beaninstanceof RequestMappingHandlerAdapter) {// Modify properties of the adapter        }    }}

Note thatMyPostProcessor needs to be included in an<component scan/> in order forit to be detected or if you prefer you can declare it explicitly with an XML beandeclaration.


Prev Up Next
Part VI. The Web Home 23. View Technologies

[8]ページ先頭

©2009-2025 Movatter.jp