Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC complex model population from multiple sources

Well, my question may sound a little bit fuzzy, but here it is, anyways. I'm building a web application using Spring MVC 3.1.M1, JSP 2.1 (without Tiles, I use plain JSP tag files to compose my layouts).

Basically, my pages are built using layouts of some common parts - header, footer, banner, menu etc. Most of these parts are dynamic, i.e. contain current user's related information.

JSP does not have a "component" notion, so I cannot define part of my template and its backing java code in some one place, coupled together. In my @Controllers, I have to fully populate my model, including data for header, footer, menu and other stuff. What I really want to do is to avoid this code duplication. Abstract BaseController class with some generic model population methods does not look good too.

JSP and Spring MVC are a very often used together, so I expect some best-practices to exist on this subject. Lets discuss this.

like image 696
Max Alexejev Avatar asked Mar 21 '11 20:03

Max Alexejev


2 Answers

Ok, so I spent some time with Spring MVC reference and sample applications, and found some additional ways to accomplish my mission. Here they are:

1) Way number one, bad and unusable, just to mention here. Abstract BaseController with methods like populateHeaderData(Model model), populateFooterData(Model model) and so on. All @RequestMapping methods in all controller classes that extend BaseController call these methods to populate layout-specific model data.

Pros: none

Cons: code duplication remains the same, just the amount of duplicated code is reduced

2) @ModelAttribute methods, i.e. implicit model enrichment. Looks like

@Controller
@RequestMapping(value="/account")
public class AccountController {

    @ModelAttribute("visitorName")
    private String putVisitor() {
        return visitorService.getVisitorName();
    }

    // handler methods
}

And in JSP,

<span id="username">Welcome, ${visitorName}!</span>

Pros: no need to call model enrichment methods explicitly - it just works

Cons: its a tricky thing here. Spring MVC utilizes "push" templating model instead of "pull". What it means in this case is that when any of @RequestMapping methods, defined in this class, is called, all @ModelAttribute methods of this class are invoked. There is no difference if template really needs visitorName and if the template actually exist for specific action. This includes POST requests for form submits, etc. In fact, this forces us to change controllers separation. For example, all form submits should be in separate controller classes, and handler methods should be somehow grouped by layouts. I have to think more about it, maybe its not that bad as it looks at first glance.

More cons: suppose we have layouts A and B with the same non-static header, and B and C with the same non-static footer (all other parts are different). We cannot implement base class for layout B, since there is no multiple inheritance in Java.

Question to the audience: Spring MVC reference states "The following return types are supported for handler methods: A ModelAndView object, with the model implicitly enriched with command objects and the results of @ModelAttribute annotated reference data accessor methods... ". What the hell these command objects are?

3) My own pull-like method. We can create custom contexts in a form of

@Component("headerContext")
public class HeaderContext {

    @Autowired
    private VisitorService visitorService;

    public String getVisitorName() {
        return visitorService.getVisitorName();
    }

    // more getters here

}

Then, expose such beans to JSP EL via

<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/"/>
    <beans:property name="suffix" value=".jsp"/>
    <beans:property name="exposedContextBeanNames" value="headerContext,footerContext"/>
</beans:bean>

And in header.tag (JSP tag file for reused header)

<span id="username">Welcome, ${headerContext.visitorName}!</span>

Pros: "pull" strategy (nobody asks - nothing is exeduted), easy to make contexts @Scope("request") and enable request-wide caching, no problems with multiple inheritance. Just coded in one place, configured in one place and may be used in any JSP or tag file as a usual expression.

Cons: mix of push and pull within one framework (have to think more about it), no Spring MVC support in context implementation classes (I mean these nasty prepopulated arguments in controller handler methods), just spring beans.

like image 90
Max Alexejev Avatar answered Sep 21 '22 03:09

Max Alexejev


The springframework contains handler interceptors as part of the handler mapping mechanism.
Within the interceptor you can use the postHandle method before the actual handler is executed.

Such a interceptor must implement the org.springframework.web.servlet.HandlerInterceptor or the org.springframework.web.servlet.handler.HandlerInterceptorAdapter for simplified implementation.

public class MyHandlerInterceptor extends HandlerInterceptorAdapter {

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

        //populate header, menu, footer, ... model
    }
}

and the configuration for the handler mapping.

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
    <list>
        <bean id="myInterceptor" class="...MyHandlerInterceptor"/>
    </list>
</property>
like image 43
RicoZ Avatar answered Sep 22 '22 03:09

RicoZ