Common Reference Data in Spring MVC

What is Reference Data?

Does Reference Data differ from Model Information?

Is CSS and image location information Reference/Model Data?

Reference Data is a term used very frequently by Spring MVC developers, especially ones which use the pre-made Spring controllers. The idea of Reference Data has mainly come from the referenceData method, located in the AbstractFormController, which is a base class for both the SimpleFormController and AbstractWizardFormController. The former is a very popular and well used controller in Spring MVC applications. Reference Data is information which is added to the model for use by the view, be it jstl, pdf or freemarker. The terms Model Data and Reference Data are used interchangeably as they are the same; the former is a newer way to refer to information being added to the ‘model’ of the page (Spring 2.5 brought in the new concept of a ‘model map’, where the model is separate to the ModelAndView). This, in itself, paves a very obvious path to the new @ModelAttribute annotation which has appeared in Spring 2.5, a new way to add Reference Data (or Model information) to the ModelMap. Have I lost you?

Reference Data can be of two forms – page specific and application/site specific. Page model data could be lists of products, product categories, number of users on the site or simply data to go into lists and combo boxes, for example. Application/site specific model data is usually simpler and narrower in scope, for example, css and image locations.

This post is going to focus on site / application reference data. I don’t want to delve into too much of the already known (2.0 reference data), instead I would like to focus on the @ModelAttribute annotation and using Interceptors for adding this data to your ModelMap / ModelAndView.

1. model attribute on every set of reference data

The first example is the simplest but not the cleanest; simply put we are going to add the page specific data using individual @ModelAttribute annotated methods.

@Controller
public class SimplePage {

@ModelAttribute(“cssLocation”)
public String cssLocation(){
return “http://static.site.com/css”;
}

@ModelAttribute(“imageLocations”)
public String imageLocations(){
return “http://static.site.com/images”;
}

@RequestMapping(“/simplePage.html”)
public String showSimplePage(ModelMap model){

model.addAttribute(“stuff”, “importantStuff”);

return “simplePage”;
}
}

This is fine for one or two controller, but we can do better.

2. model attribute on every set of reference data in a super class

This is very similar to the first example, except now we are adding the @ModelAttribute methods into a super class which our other controllers can extend.

public abstract class AbstractCommonModelDataController {

@ModelAttribute(“cssLocation”)
public String cssLocation(){
return “http://static.site.com/css”;
}

@ModelAttribute(“imageLocations”)
public String imageLocations(){
return “http://static.site.com/images”;
}

}

This is better than the first example as we have now grouped common behaviour into a super class. This approach is good for a couple of pieces of reference data, but not for many as it becomes a bit messy.

3. model attribute on one method for large common set of reference data, all in a super class

Okay, so as the title says, we are going to use the helpful autowiring magic Spring MVC weaves and have only one method for the model data.

public abstract class AbstractCommonModelDataController {

@ModelAttribute
public void cssLocation(ModelMap model){
model.addAttribute(“cssLocation”, “http://static.site.com/css”);
model.addAttribute(“imageLocation”, “http://static.site.com/images”);
}
}

This is a nice solution, an is very clean and very simple.

But all the previous examples have a common issue, as all @ModelAttribute methods are run before the handler is executed, if the handler returns a redirect the model data will be added to the url as a query string. This should be avoided at all costs as it could expose some secrets on how you have put together your application. One example I propose, which came out of a forum post I helped answer (read more here), is to create a HandlerInterceptor, which implements the postHandle method and add the model data here. Let’s take a look.

4. HandlerInterceptor for common reference data within the entire web application

(also, just a quick sorry for the crap formatting of the code below, WordPress doesn’t handle code very well)

public class CommonModelMapInformationInterceptor implements
HandlerInterceptor {

// This method is unused as this Interceptor is for post handle only
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception { }

// This method is unused as this Interceptor is for post handle only
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return true;
}

public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {

boolean isRedirectView = modelAndView.getView() instanceof RedirectView;
boolean isViewObject = modelAndView.getView() == null;
// if the view name is null then set a default value of true
boolean viewNameStartsWithRedirect = (modelAndView.getViewName() == null ? true : modelAndView.getViewName().startsWith(UrlBasedViewResolver.REDIRECT_URL_PREFIX));

if(modelAndView.hasView() && (
( isViewObject && !isRedirectView) ||
(!isViewObject && !viewNameStartsWithRedirect))){
addCommonModelData(modelAndView);
}
}

public void addCommonModelData(ModelAndView modelAndView){
modelAndView.addObject(“stuff”, “importantStuff”);
modelAndView.addObject(“moreStuff”, “moreImportantStuff”);
}
}

(Download the HandlerInterceptor here)

I like this approach the most as it abstracts the common reference data out of the controllers and is only applied on views which are not redirects; this could be further improved to only work with certain views only (like jstl, freemarker etc.). This set-up does have one major flaw – postHandle methods on HandlerInterceptors are not called when Exceptions occur during handler processing. This means that if you use the SimpleMappingExceptionResolver then you will need to extend it and possibly inject the interceptor in and call the addCommonModelData method. Otherwise you could look at abstracting the addCommonModelData out of the HandlerInterceptor, put it in its own class, declare it as a bean, and inject it into the HandlerInterceptor and HandlerExceptionResolver. Also to note, a small downside to this approach is that you need to register the HandlerInterceptor with any mappers you use, if it be bean name, simple mapping, or annotation mapping.

I am sure there are more than these four approaches for application specific model data and if you have a suggestion, opinion, question, or comment I would be very keen to hear from you.

Thanks

Josh

About these ads

8 Comments

Filed under Spring MVC

8 responses to “Common Reference Data in Spring MVC

  1. Josh:

    Great post, I’m using a similar technique, but I thought that I would mention that the same sort of thing can also be achieved with most of the html templating options available to Spring MVC. On another note, you should consider using the SemaCodeFix plugin to format your code blocks. I’ve used it with great success on my site. Here is the link: http://www.sematopia.com/?p=138

    Carl

  2. Josh Kalderimis

    Hi Carl,

    You are absolutely right, there is no doubt that using an interceptor can be over kill, but in some cases it can be the best solution. Maybe the reference data being added to every page is from the database or maybe you have different properties files for different deployments and would like the information in the properties files be added to every page. In any case, an interceptor is not a perfect solution.

    On another note, I am a big fan of sitemesh and agree that templating can be a great substitute.

    Thank you also for the link to SemaCodeFix, sadly I can’t use it as I’m using wordpress.com as my blog host, but I will remember it if in case I move my post in the future.

    Josh

  3. Jonas

    Post data can also be read, just launch up firebug and you will see :).
    Exposing the model in the url should not be considered a security risk, instead you should design the security layers with this in mind.

    Jonas

  4. jit

    Thanks for the article but i couldn’t understand one part. posthandle will start to work after the controller has finished its work. So my question is how can i use the ModelAndView object of the postHandle before rendering the page?

  5. jit

    I solved the problem. You can ignore my dumb comment :)

  6. nitzshe

    use HandlerInterceptorAdapter instead of HandlerInterceptor. It has implementation of all non used methods.

  7. MichalR

    Hi Josh,

    great post!

    About redirect problem: there is an option to use RedirectView as the return type from a controller method and set its exposeModelAttributes property to false.

    Michal

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s