Even the most extreme Spring opponents have to admit it is all about making developers life easier. Version 3.2 of Spring MVC brings even more sweetness to the table.
Sweetness #1: No web.xml
The ability to run a webapp without any web deployment descriptor comes from Servlet 3.0.
One option would be to annotate your servlet with the @WebServlet
annotation to set mapping and complementary data.
When you get your servlet for free, like Spring’s DispatcherServlet
, you’d need to subclass you servlet for no other purpose than adding annotation(s).
Alternatively, Servlet 3.0 offers a way to both programmatically register servlets in the container and to offer hooks at startup through the ServletContainerInitializer
interface.
The container will call the onStartup()
method of all concrete implementation at webapp startup.
The Spring framework leverages this feature to do so for WebApplicationInitializer
instances.
Spring MVC 3.2 provides such an implementation - AbstractContextLoaderInitializer
, to programmatically register the DispatcherServlet
.
This means that as soon as the spring-webmvc jar is in the WEB-INF/lib
folder of the webapp, you’ve got the Dispatcher servlet up and ready.
This replaces both the servlet and servlet mapping and the context listener declarations in the web.xml.
Sweetness #2: Easy Java configuration integration
Java configuration is the way to configure Spring injection explicitly in a typesafe way. I won’t go into the full demonstration of it, as I already wrote about that some time ago.
Earlier Spring versions provided a way to use Java configuration classes instead of XML files in the web deployment descriptor.
Spring 3.2 offers AbstractAnnotationConfigDispatcherServletInitializer
, a AbstractContextLoaderInitializer
subclass with hooks for Java configuration classes.
Your own concrete subclass has to implement methods to define servlet mappings, as well as root and web Java configuration classes:
public class SugarSpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { JavaConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
At this point, you just need to create those configuration classes.
Sweetness #3: integration testing at the mapping level
Your code should be unit-tested, that is tested in isolation to ensure that each method is bug-free, and integration-tested to ensure that collaboration between classes yield expected results.
Before v. 3.2, the Spring test framework let you assemble your configuration classes / files easily enough. The problem lay in the way you would call entry-points - the controllers. You could call methods of those controllers, but not the mappings, leaving those untested.
With v. 3.2, Spring Test brings a whole MVC testing framework which entry-points are mappings.
This way, instead of testing method x()
of controller C
, you would test a request to /z
, letting Spring MVC handle it so we can check for expected results.
The framework also provide expectations for view returning, forwarding, redirecting, and model attribute setting, all with the help of a specific DSL:
public class SayHelloControllerIT extends AbstractTestNGSpringContextTests {
private MockMvc mockMvc;
@BeforeMethod
public void setUp() {
mockMvc = webAppContextSetup((WebApplicationContext) applicationContext).build();
}
@Test(dataProvider = "pathParameterAndExpectedModelValue")
public void accessingSayhelloWithSubpathShouldForwardToJspWithModelFilled(String path, String value) throws Exception {
mockMvc.perform(get("/sayHello/Jo")).andExpect(view().name("sayHello")).andExpect(model().attribute("name", "Jo"));
}
}
The project for this article can be downloaded in Eclipse/Maven format.