Top 5 enhancements of Spring MVC 3.1

by Rafał Borowiec on 16th December 2011

Spring Java software development framework After many months of development, Spring 3.1 has been finally released. The release is shipped with some exciting features like caching abstraction, bean profiles and container configuration simplifications. However, in this blog post I will describe my top 5 enhancements of Spring MVC 3.1.

1. Flash attributes support

Flash attributes are used to store attributes between requests and  are used mostly in Post/Redirect/Get pattern. Prior to Spring 3.1 some additional (repeatable) steps were required to utilize flash attributes in Spring MVC based applications:
  • Create or copy FlashMap object to hold flash attributes
    			public class FlashMap implements Serializable  {
    				// implementation omitted
    			}
    		
  • Create or copy FlashMapFilter to your project
    			public class FlashMapFilter extends OncePerRequestFilter {
    				// implementation omitted
    			}
    		
  • Include FlashMapFilter in web.xml
  • Put flash attributes in the controller when needed
    			@PreAuthorize("hasRole('ROLE_USER')")
    			@RequestMapping(value = Routing.SOME_ROUTING, method = RequestMethod.POST)
    			public String processForm(@Valid Form form, BindingResult result) {
    
    				// process the form
    				// ...
    
    				// add flash attribute
    				FlashMap.addAttribute("message", "Form processed");
    
    				// redirect
    				return RoutingHelper.redirect(Routing.SOME_OTHER_ROUTING);
    			}
    		
With Spring 3.1 flash attributes support is enabled by default. No additional configuration is required. @Controller method accepts the argument of a type RedirectAttributes. RedirectAttributes is an interface that extends from standard Spring MVC Model interface and it may be used to store flash attributes that will be available after the redirection.
	@PreAuthorize("hasRole('ROLE_USER')")
	@RequestMapping(value = Routing.SOME_ROUTING, method = RequestMethod.POST)
	public String processForm(@Valid Form form, BindingResult result, RedirectAttributes redirectAttributes) {

		// process the form
		// ...

		// add flash attribute
		redirectAttributes.addFlashAttribute("message", "Form processed");

		// redirect
		return RoutingHelper.redirect(Routing.SOME_OTHER_ROUTING);
	}

2. @Validated annotation with support for JSR-303 validation groups

Spring MVC Bean Validation support has been extended with a new @Validated annotation that provides support of JSR 303 validation groups. A group defines a subset of constraints that is validated for a given object graph. Each constraint declaration may define the group or the list of groups it belongs to.
	public class User {
		@NotNull
		private String name;

		@NotNull(groups = { Simple.class })
		private String address;

		@NotNull(groups = { Simple.class, Extended.class })
		private String licenceType;

		@NotNull(groups = { Extended.class })
		private String creditCard;
	}
 
	public interface Simple {

	}

	public interface Extended {

	}
The example usage of the new @Validated annotation is shown in the following example:

	@Controller
	public class MyController {

		@RequestMapping(value = "/user/default", method = RequestMethod.GET)
		public String createDefaultUser(@Validated({ Default.class }) User user, BindingResult result) {
			if(result.hasErrors()) {
				// Validation on 'name'
			}
			return null;
		}

		@RequestMapping(value = "/user/simple", method = RequestMethod.GET)
		public String createSimpleUser(@Validated({ Simple.class }) User user, BindingResult result) {
			if(result.hasErrors()) {
				// Validation on 'licenceType' and 'address'
			}
			return null;
		}

		@RequestMapping(value = "/user/extended", method = RequestMethod.GET)
		public String createExtendedUser(@Validated({ Simple.class, Extended.class }) User user, BindingResult result) {
			if(result.hasErrors()) {
				// Validation on 'creditCard' and 'licenceType' and 'address'
			}

			return null;
		}
	}

The support for validation groups as added to Spring 3.1 is really handy. To read more about validation groups download and read the JSR 303 specification.

3. Support @Valid on @RequestBody method arguments

The @RequestMapping handler methods support @RequestBody annotated parameter for accessing the HTTP request body. The conversion is done through the available message converters, including FormHttpMessageConverter (application/x-www-form-urlencoded), MarshallingHttpMessageConverter (application/xml), MappingJacksonHttpMessageConverter (application/json) and others. But the @RequestBody annotated method argument could not be automatically validated. Manual validation was required:
	@Controller
	public class MyController {

		@Autowired
		private Validator validator;

		@RequestMapping(value = "", method = RequestMethod.PUT)
		@ResponseStatus(value = HttpStatus.CREATED)
		@Secured("ROLE_ADMIN")
		@Transactional
		public void save(@RequestBody User user) {
			validate(user);
			dao.save(user);
		}

		private void validate(User user) {
			// perfom validation using injected validator
		}
	}
In Spring 3.1 the @RequestBody method argument can be annotated with @Valid to invoke automatic validation. This makes the code can be simplified:
@Controller
public class MyController {

	@RequestMapping(value = "/user", method = RequestMethod.POST)
	@ResponseStatus(value = HttpStatus.CREATED)
	@Transactional
	public void save(@Valid @RequestBody User user) {
		dao.save(user);
	}

	@ExceptionHandler
	@ResponseStatus(value = HttpStatus.BAD_REQUEST)
	@ResponseBody
	public String handleMethodArgumentNotValidException(
			MethodArgumentNotValidException error) {
		return "Bad request: " + error.getMessage();
	}
}

In the above example, Spring automatically performs the validation and in case of error MethodArgumentNotValidException is thrown. Optional @ExceptionHandler method may be easily created to add custom behavior for handling this type of exception.

4. Supporting PUT request with form encoded data

The limitation of Servlet implementation is that it does not handle the encoded data of the HTTP PUT request properly. Introduction of the new HttpPutFormContentFilter solves that problem. With Spring 3.1 developing a RESTfull or RESTlike API became much simpler. See my previous post to read more about the usage of HttpPutFormContentFilter in a web application.

5. Java based configuration

The xml configuration of a Spring MVC application may be completely replaced by the Java based configuration. The simplest way to configure a web application is to use the @EnableWebMvc annotation:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" }, excludeFilters = @Filter(type = FilterType.ANNOTATION, value = Configuration.class))
@Import(PersistanceContextConfiguration.class)
public class ServletContextConfiguration extends WebMvcConfigurerAdapter {

	@Bean
	public TilesViewResolver configureTilesViewResolver() {
		return new TilesViewResolver();
	}

	@Bean
	public TilesConfigurer configureTilesConfigurer() {
		TilesConfigurer configurer = new TilesConfigurer();
		configurer.setDefinitions(new String[] { "/WEB-INF/tiles/tiles.xml",
				"/WEB-INF/views/**/views.xml" });
		return configurer;
	}

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("login");
	}

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/").addResourceLocations(
				"/recourses/**");
	}

	@Override
	public void configureDefaultServletHandling(
			DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
}
Extending from WebMvcConfigurerAdapter is not required, but it allows for the customization of the default Spring MVC configuration. The adapter enables us to register additional components like formatters, converters, custom validators, interceptors etc. This way the custom configuration can be done by Java code only. See my previous post about configuring a Spring MVC application with no xml in a Servlet 3.0 based environment.

Spring MVC 3.1 has a lot to offer

Spring MVC 3.1 brings many enhancements. The features mentioned in this blog post should have a positive impact on the application development with Spring MVC. They are not revolutionary, though. However, when I look at all the Spring features and enhancements shipped within the 3.1 release, I feel that the framework is going in the right direction. The caching abstraction, bean profiles, configuration simplifications and many other core features should allow to develop applications more efficiently.

References:

  • http://blog.springsource.org/2011/12/13/spring-framework-3-1-goes-ga/
  • http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/new-in-3.1.html
  • http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/
  • http://javarevisited.blogspot.com/2011/01/how-classpath-work-in-java.html JP@ classpath in Java

    Superb, is there any significant change on Spring Security as well or that’s not included in this release ?

  • Anonymous

    Spring Security 3.1 is seperate project, see 
    http://static.springsource.org/spring-security/site/downloads.html, currently RC3

  • http://michalorman.pl/ Michał Orman

    Actually when you put @Valid and @RequestBody annotations on controller’s method parameter than you DO NOT add BindingResult parameter. Otherwise you will receive something like this:

    “An Errors/BindingResult argument is expected to be immediately after the model attribute argument in the controller method signature”

    In fact in this case there is no binding performed at all, it is just deserialization of the request body to object using some message converter. 

    Spring automatically performs the validation (if @Valid is given) and in case of error will throw MethodArgumentNotValidException which by default result in 400 Bad Request, but if you need to put some specific body to the response you can write exception handler using @ExceptionHandler annotation (now together with @ResponseBody which is one of my top 5 new features in Spring 3.1).

  • Anonymous

    Hi Michał,

    Good point. I created this snapshot based on following sentence: “An @RequestBody:twitter 
    method argument can be annotated with @Valid to invoke automatic validation similar to the support for @ModelAttribute method arguments” and I missed the way the validation is actually done.

    It is also worth mentioning that my original code will deploy properly. Spring starts complaining (and throws the java.lang.IllegalStateException) when you actually execute the request against handler’s method.

    I will update the post with an example.

    Thank you very much for you comments. I am happy to see someone actually verified what I wrote. 

  • http://michalorman.pl/ Michał Orman

    Yeah, yesterday it took me few hours investigating this issue, while I was also sure that the method signature is same as in spring 3.0 for regular requests (without @RequestBody).  But it looks that in spring there is a clear distinction between binding (mapping GET/POST params to fields) and unmarshalling (reading body and creating an object using some message converter). When you handle MethodArgumentNotValidException you have access to BindingResult, which is in my opinion a little lack of consequence and may confuse, while in this case there was no binding performed.

    Well, at the end I’ve learned something from the inner depths of Spring ;)

  • Anonymous

    I think the usage of @Valid on @RequestBody:disqus 
     is needed when you build an API and you allow external call to be made with (for example) application/xml content type. In that case you usually return an xml wrapping an error message, code etc. (at least I would do so). So this lack of consequence may be justified imho.

  • Li Su

    hi,  i’m very interesting the Routing and RoutingHelper , and how to implement it and get Url from action+controller name, can u share it, or some idea? thx!

  • kevin

    I know its old post. But I am wondering why the @Valid is not kicking in for me, though binding works. I am using Spring 3.2.RELEASE. Hibernate Validator. Any idea?

  • rborowiec

    For Spring MVC 3.2 quickstart you can check my archetype: https://github.com/kolorobot/spring-mvc-quickstart-archetype

  • Thiago Henrique Java Leite

    wow,

    Condolences, very, very good at conversation two guys Michał Orman and rborowiec a (rborowiec) says that will put the solution in the post, the other (Michał Orman) says he learned more about Spring and solved the problem, very good, very good …

    Where’s the solution?

    I’m like a crazy behind resolving this, if you can please post the solution would be very grateful …

    Rborowiec And as you said he is happy to know that someone is reading your post, then make someone happy by putting the solution of the problem @ responseBody with BindingResult in use, please!

    Thanks, pending!

  • rborowiec

    Hi Li,

    Thanks for your comment, and sorry for a late response.

    In this simple example the RoutingHelper has just methods to return Spring MVC “redirect” and “forward”. It may also support parameters passing to an URL. This solution simplifies the readability.

    As it goes to managing the routings, in small project we sometimes use a Routing class that contains constants that define the routings used in the controllers.

    To be honest, I don’t know the perfect solution for that.

    Do you know any? What is your expierience?

  • Sam

    Hello,I’m very interested in the difference of @valid and @validated.
    Can you tell me something? Thanks a lot!!

  • rborowiec

    Some differences:

    - @Valid is a standard JSR 303 annotation, @Validated – is a Spring one. See http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/validation/annotation/Validated.html

    - With @Validated you can use JSR 303 validation groups. Before we needed a workaround in Spring MVC. See http://blog.codeleak.pl/2011/03/how-to-jsr303-validation-groups-in.html

    - It can be used for method level validation. See this post: http://blog.codeleak.pl/2012/03/how-to-method-level-validation-in.html

    I hope it answers your question.