Friday, August 29, 2014

Spring MVC’s Default Behavior

View Resolver

First up is the view resolver. We can’t specify all the views ahead of time and the only way the marketing department communicates new pages to us is by publishing them into our JSP directory, Springs InternalResourceViewResolver is the perfect fit.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/content/" />
    <property name="suffix" value=".jsp" />
</bean>
Assuming the build script pulls the published HTML files into the projects /WEB-INF/jsp/content directory and renames them with a “.jsp” extension, then all the pages should be available with a view that matches the path to the page, minus the ViewResolver’s specified prefix and suffix. For example, if we have a page stored at /WEB-INF/jsp/content/members/profile.jsp, A controller can access that page by returning a view name of members/profile.

The Default Controller

Now that there is a view resolver capable of seeing all the pages produced by the marketing department, a controller must be created and mapped so that it will produce the correct view name.
package com.sourceallies.base;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
@Controller
public class DefaultController {
 
    @RequestMapping("/**/*")
    public void defaultRequest(){}
}
That was easy. It would be hard to come up with a simpler controller, in fact it doesn’t look like it does anything at all! In reality it is taking advantage of Spring MVC’s default behavior for just about everything. With annotation based controllers, request mapped methods have a variety of possible return types. Strings are interpreted as view names, and ModelView objects are used like they were in previous versions of Spring MVC. But if the method has no return type then we get some interesting behavior. On void methods (or String methods that return null) Spring MVC looks at the request URI, strips off the app context and any file extensions, and uses whatever is left over as the view name. For example, if my application is mapped to /corporate/ and my request URL is /corporate/members/profile.html then Spring MVC will generate a view name of “members/profile”. The other thing to note is the RequestMapping, /**/* is the most generic mapping there is. It will match any request that comes through the Spring MVC servlet. This is how to avoid having to add new mappings for every new page. This is exactly what we want to use when working with the ViewResolver we defined earlier. All of the pages from the CMS live relative to each other, and the URLs requested from the client should match the file structure from the CMS.
So at this point I now have a more complicated version of the normal servlet request process. Its usefulness becomes apparent when marketing needs a new piece of data on a specific page and I need to create a controller.

@Controller
public class ProfileController {
 
    @RequestMapping("/members/profile.html")
    public void testRequest(Model model) {
        Profile profile = ...LookupProfile
 
        model.addAttribute("profile", profile);
    }
}
All I have to do is create a new controller and map it to the page name from the marketing department.