Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: Common Model Objects

  1. #1
    Join Date
    May 2007
    Posts
    10

    Default Common Model Objects

    Its been a long time since I have worked with Spring's MVC layer, or with IoC in general, but this seems like a common problem.

    There are some templating utilities as well as some common configuration that I always want stuffed in the ModelAndView. Is there any easy way to wire this with Spring?

    I have been attempting to just extend ModelAndView, however, there are many final methods in the Spring framework that cannot be overridden.

  2. #2
    Join Date
    Jul 2005
    Location
    Idaho
    Posts
    231

    Default

    One method that we have used with Velocity is to have a 'defaultModel' to return with the ModelAndView.
    Code:
    Map model = getDefaultModel();
    ...
    public Map getDefaultModel ( ) {
      Map model = new HashMap();
      model.put( "dateTool", new DateTool() );
      model.put( "esc", new EscapeTool() );
      model.put( "number", new NumberTool() );
      return model;
    }
    You should be able to do this with other template technologies too.

    How that helps,

    Steve O

  3. #3
    Join Date
    May 2007
    Posts
    10

    Default

    Steveo,

    Thanks for the response. The same thing occured to me, but if a developer wants to consctruct a different ModelAndView and then get to the template only to realize they are missing objects?

    I am looking for something transparent to the caller.

  4. #4
    Join Date
    Apr 2007
    Location
    Wellington, New Zealand
    Posts
    125

    Default

    Hi Johnhup,

    If you are using the new 2.5 styled controllers then there are quite a few ways to have common reference data added to the model using the @ModelAttribute annotation. If you are using the 2.0 controllers and the ModelAndView paradigm then there are still some good options available, but they tend to be less flexible

    First off, you could create a bean which you can inject into your controllers which includes a 'getCommonModelAndView' method. This is simple and easy and flexible as all controller types can use it, 2.0 and 2.5.

    Another option is to extend a commonly used class, say SimpleFormController, and override referenceData or add a new method (like above), but this is not flexible as it will only apply to one controller type.

    Another way is to create an interceptor which implements postHandle and adds the common reference data to the ModelAndView. This is nice as it is separate from the controllers and works only after processing has been accomplished. Although, its important to note that it will not fire if an exception occurs during handling as the postHandle processing is skipped.

    If you are using the 2.5 styled controllers you can create a super class which has a @ModelAttribute method which all controllers inherit, eg.
    Code:
    @ModelAttribute
    public void commonModelData(ModelMap modelMap){
        modelMap.addAttribute("stuff", "stuff");
    }
    Be warned, if you add common model data and then redirect, the model data will be appended to the query string, I recommend using the interceptor but checking what the view is. If you use a handler exception resolver then you might have to create a hook for it to use the interceptor as well.

    I hope this helps (and makes sense)

    Josh

  5. #5
    Join Date
    May 2007
    Posts
    10

    Default

    Quote Originally Posted by joshk View Post
    Another way is to create an interceptor which implements postHandle and adds the common reference data to the ModelAndView. This is nice as it is separate from the controllers and works only after processing has been accomplished. Although, its important to note that it will not fire if an exception occurs during handling as the postHandle processing is skipped.
    Thanks Josh, that does make sense, especially using the WebRequestInterceptor.postHandle. I would like to do this route as it is transparent to the caller, however, how do I get beans that were created using Spring's container into a super class that all my controllers would extend? My main problem is, I dont want to have to wire up all of this shared functionality into every controller.

    I have been working with programatic frameworks like Pico for a while now so its hard to get my head around this again. I gotta say I really miss pico's auto-wiring constructor injection.

  6. #6
    Join Date
    Apr 2007
    Location
    Wellington, New Zealand
    Posts
    125

    Default

    Hi John,

    Spring has auto-wiring as well, you just have to tell the bean definition that you want to use it.

    Sorry, can I just confirm, you want to use an interceptor ? if so, why do you need to create a controller super class ?

    Josh

  7. #7
    Join Date
    May 2007
    Posts
    10

    Default

    I have read that Spring also has autowiring but sometimes there are ambiguous names due the 'targets' as the other engineer has called them which are proxy classes.

    Now I see what you mean regarding the Post Handlers. Let me see if I get you correctly. If I create a class that implements any of those handlers then Spring will call them before rendering the request?

  8. #8
    Join Date
    Apr 2007
    Location
    Wellington, New Zealand
    Posts
    125

    Default

    Hi John,

    I need to make an amendment to my earlier post, do not use the WebRequestInterceptor as its postHandle method does not allow you to check the view because only the ModelMap is available for use. Instead use the HandlerInterceptor interface and implement the postHandle method. Check if the view exists and if it isn't of type RedirectView and the viewname doesn't start with "redirect:", add the reference data. eg.

    (sorry if its a bit messy)

    Code:
    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))){
    		modelAndView.addObject("stuff", "importantStuff");
    		modelAndView.addObject("moreStuff", "moreImportantStuff");
    	}
    }
    (Please note, I have not tried out this code)

    You will also need to register each Interceptor with each handler mapper.

    I hope this helps

    Josh

  9. #9
    Join Date
    May 2007
    Posts
    10

    Default

    That worked like a charm. Thanks for your input Josh. Do you happen to know any documents that would explain more of the 'frameworky' type of aspects to Spring? I was thinking of writing a dynamic proxy to solve this problem but I was too afraid to go down that path!

    Again, thanks for all your detailed help.

  10. #10
    Join Date
    Apr 2007
    Location
    Wellington, New Zealand
    Posts
    125

    Default

    Hi John,

    Its no problem at all, its interesting for me as well as I start digging into the code to check how it works.

    Regarding the dynamic proxies, could you explain a bit further what you would like to achieve, anything is possible its just knowing the right path.

    Josh

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •