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.
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:
DispatcherServlet, handler mapping, view resolver, and soon — can be fulfilled by a specialized object.Map supports easyintegration with any view technology.Session.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”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.
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).
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.
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.
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/*" }; }}
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 type | Explanation |
|---|---|
Maps incoming requests to handlers and a list of pre- and post-processors (handler interceptors) based on some criteria the details of which vary by | |
HandlerAdapter | Helps the |
Maps exceptions to views also allowing for more complex exception handling code. | |
Resolves logical String-based view names to actual | |
Resolves the locale a client is using and possibly their time zone, in order to be able to offer internationalized views | |
Resolves themes your web application can use, for example, to offer personalized layouts | |
Parses multi-part requests for example to support processing file uploads from HTML forms. | |
Stores and retrieves the "input" and the "output" |
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.
After you set up aDispatcherServlet, and a request comes in for that specificDispatcherServlet, theDispatcherServlet starts processing the request as follows:
WebApplicationContext 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.MultipartHttpServletRequest forfurther processing by other elements in the process. SeeSection 22.10, “Spring’s multipart (file upload) support” for furtherinformation about multipart handling.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
| Parameter | Explanation |
|---|---|
| Class that implements |
| String that is passed to the context instance (specified by |
| Namespace of the |
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 |
|---|---|
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.
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>
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.
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@PatchMappingThe 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"; }}
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"/>.
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:
SimpleUrlHandlerMapping orBeanNameUrlHandlerMapping and then narrow the method based on@RequestMappingannotations.@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.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 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 |
|---|---|
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 @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”.
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) {// ...}
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}).
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:
/** is less specific than any other pattern.For example/api/{a}/{b}/{c} is more specific./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).
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.
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.
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 |
|---|---|
This was originally introduced as part of work forCVE-2015-5211.Below are additional recommendations from the report:
|
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 |
|---|---|
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 the In the MVC namespace, the <?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> |
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 |
|---|---|
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. |
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 |
|---|---|
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 in |
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 |
|---|---|
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. |
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 |
|---|---|
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. |
@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.
@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 |
|---|---|
Spring 3.1 introduced a new set of support classes for |
The following are the supported method arguments:
ServletRequest orHttpServletRequest.HttpSession. An argument of this type enforcesthe presence of a corresponding session. As a consequence, such an argument is nevernull.![]() | Note |
|---|---|
Session access may not be thread-safe, in particular in a Servlet environment. Considersetting the |
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”.@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 |
|---|---|
JDK 1.8’s |
The following are the supported return types:
ModelAndView object, with the model implicitly enriched with command objects andthe results of@ModelAttribute annotated reference data accessor methods.Model 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.Map 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.View 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).String 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).@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”.HttpEntity<?> 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”.HttpHeaders object to return a response with no body.Callable<?> can be returned when the application wants to produce the return valueasynchronously in a thread managed by Spring MVC.DeferredResult<?> can be returned when the application wants to produce the returnvalue from a thread of its own choosing.ListenableFuture<?> orCompletableFuture<?>/CompletionStage<?> can be returnedwhen the application wants to produce the value from a thread pool submission.ResponseBodyEmitter can be returned to write multiple objects to the responseasynchronously; also supported as the body within aResponseEntity.SseEmitter can be returned to write Server-Sent Events to the responseasynchronously; also supported as the body within aResponseEntity.StreamingResponseBody can be returned to write to the response OutputStreamasynchronously; also supported as the body within aResponseEntity.@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.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.
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 |
|---|---|
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. |
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.
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.
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.
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 |
|---|---|
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 type |
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”.
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:
@SessionAttributes — seethe section called “Using @SessionAttributes to store model attributes in the HTTP session between requests”.@ModelAttribute method in the samecontroller — as explained in the previous section.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.
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 {// ...}
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”.
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) {// ...}
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 |
|---|---|
As |
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.
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 |
|---|---|
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 |
This annotation is supported for annotated handler methods in Servlet and Portletenvironments.
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”).
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.
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")); }// ...}
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.
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.
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 that despite |
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"; }}
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.
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:
ServletRequest 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.request.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.ServletRequest 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:
Callable.Callable toaTaskExecutor for processing in a separate thread.DispatcherServlet and all Filter’s exit the Servlet container threadbut the response remains open.Callable produces a result and Spring MVC dispatches the request backto the Servlet container to resume processing.DispatcherServlet 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:
DeferredResult and saves it in some in-memoryqueue or list where it can be accessed.DispatcherServlet and all configured Filter’s exit the requestprocessing thread but the response remains open.DeferredResult from some thread and Spring MVCdispatches the request back to the Servlet container.DispatcherServlet 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.
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.
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.
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.
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.
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.
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.
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.
Thespring-test module offers first class support for testing annotated controllers.SeeSection 15.6, “Spring MVC Test Framework”.
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>
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 |
|---|---|
When using the |
As you can see, the Spring adapter classHandlerInterceptorAdapter makes it easier toextend theHandlerInterceptor interface.
![]() | 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 type |
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.
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.
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
| ViewResolver | Description |
|---|---|
| Abstract view resolver that caches views. Often views need preparation before they can be used; extending this view resolver provides caching. |
| Implementation of |
| Implementation of |
| Simple implementation of the |
| Convenient subclass of |
| Convenient subclass of |
| Implementation of the |
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 |
|---|---|
Subclasses of |
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!
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.
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.
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.
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.
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.
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:
http://www.example.com/users/fred.pdf requests a PDFrepresentation of the user fred, andhttp://www.example.com/users/fred.xml requestsan XML representation.Accept HTTPrequest header to list themediatypes that it understands. For example, an HTTP request forhttp://www.example.com/users/fred with anAccept header set toapplication/pdfrequests a PDF representation of the user fred, whilehttp://www.example.com/users/fred with anAccept header set totext/xml requestsan XML representation. This strategy is known ascontent negotiation.![]() | Note |
|---|---|
One issue with the 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 |
|---|---|
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; }}
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 .
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()
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();
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.
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 |
|---|---|
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.
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.
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.
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.
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
| Property | Default | Description |
|---|---|---|
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. |
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.
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>
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.
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.
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
| Class | Description |
|---|---|
| Selects a fixed theme, set using the |
| 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. |
| 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.
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.
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.
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>
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"; }}
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.
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.
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 |
|---|---|
For |
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.
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:
| Exception | HTTP Status Code |
|---|---|
| 400 (Bad Request) |
| 500 (Internal Server Error) |
| 406 (Not Acceptable) |
| 415 (Unsupported Media Type) |
| 400 (Bad Request) |
| 500 (Internal Server Error) |
| 405 (Method Not Allowed) |
| 400 (Bad Request) |
| 500 (Internal Server Error) |
| 400 (Bad Request) |
| 400 (Bad Request) |
| 404 (Not Found) |
| 404 (Not Found) |
| 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.
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.
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")%>}
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.
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.
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 URLHomeController maps to the/home* request URLIndexController maps to the/index* request URLRegisterController maps to the/register* request URLIn 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 URLCatalogController maps to the/catalog/* request URLIf 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.
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.
x.y.User instance added will have the nameuser generated.x.y.Registration instance added will have the nameregistration generated.x.y.Foo instance added will have the namefoo generated.java.util.HashMap instance added will have the namehashMap generated. Youprobably want to be explicit about the name in this case becausehashMap is lessthan intuitive.null 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:
x.y.User[] array with zero or morex.y.User elements added will have the nameuserList generated.x.y.Foo[] array with zero or morex.y.User elements added will have the namefooList generated.java.util.ArrayList with one or morex.y.User elements added will have the nameuserList generated.java.util.HashSet with one or morex.y.Foo elements added will have the namefooList generated.java.util.ArrayList will not be added at all (in effect, theaddObject(..) call will essentially be a no-op).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 |
|---|---|
You do not need to define a |
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.
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.
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):
-1 value won’t generate a'Cache-Control' response header.0 value will prevent caching using the'Cache-Control: no-store' directive.n > 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();
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>
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 headerrequest.checkNotModified(eTag) compares eTag with the'If-None-Match' request headerrequest.checkNotModified(eTag, lastModified) does both, meaning that bothconditions should be validWhen 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.
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.
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.
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.
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:
@NumberFormat annotationthrough theConversionService.Date,Calendar,Long, and Joda Time fields using the@DateTimeFormat annotation.@Controller inputs with@Valid, ifa JSR-303 Provider is present on the classpath.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:
ByteArrayHttpMessageConverter converts byte arrays.StringHttpMessageConverter converts strings.ResourceHttpMessageConverter converts to/fromorg.springframework.core.io.Resource for all media types.SourceHttpMessageConverter converts to/from ajavax.xml.transform.Source.FormHttpMessageConverter converts form data to/from aMultiValueMap<String,String>.Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added ifJAXB2 is present and Jackson 2 XML extension is not present on the classpath.MappingJackson2HttpMessageConverter converts to/from JSON — added if Jackson 2is present on the classpath.MappingJackson2XmlHttpMessageConverter converts to/from XML — added ifJackson 2 XML extension is presenton the classpath.AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on theclasspath.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 |
|---|---|
Jackson JSON and XML converters are created using 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:
|
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.
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 |
|---|---|
SeeSection 9.6.4, “FormatterRegistrar SPI” and the |
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 |
|---|---|
Sometimes it’s convenient to have a If you prefer to use the one from the MVC Java config, you’ll need to override the |
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.
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>
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.
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"/>
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; }}
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"/>.
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"/>
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"/>
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 |
|---|---|
Enabling indentation with Jackson XML support requires |
Other interesting Jackson modules are available:
javax.money types (unofficial module)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"/>
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 |
|---|---|
An application should have only one configuration extending Modifying beans in this way does not prevent you from using any of the higher-levelconstructs shown earlier in this section. |
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.