Rossen Stoyanchev at Spring Framework is finally ready to tackle the thorny issue of @ModelAttribute
inter-dependency.
The new annotation-based handlers allow easy definition of simple controllers, but when combined with JDK 7’s unpredictable ordering of reflected methods, problems could occur..
@ModelAttribute
annotation on a method essentially defines it to provide “reference data”; being calling to populate the model, before the handler method is invoked.
The Problem
Essentially the problem arose when using two or more @ModelAttribute
methods to load entities or provide reference data, with one depending on the other.
The most common scenario would probably be:
- load some entity from the database
- calculate or provide reference data, dependent on that entity.
For example:
@ModelAttribute("job") protected Job loadOrCreate (@RequestParam(value="id", required=false) Integer id); @ModelAttribute("jobDisplay") protected JobDisplay buildJobUI (@ModelAttribute("job") Job job);
Reflection order used to be fairly predictable. However in JDK 7, deliberate changes were made to reduce people depending on reflection order.
What I found, is that due to reflection ordering, buildJobUI()
was being found before loadOrCreate()
.
Since Spring was not coded to resolve dependencies, but just processed in the order returned, buildJobUI()
was having a blank default Job
instance created for it & data binding performed into that. Following that, loadOrCreate()
was skipped since the attribute was already present in the implicit model.
So, Job
was never loaded from the database, loadOrCreate()
was skipped, and fields were bound into an incorrect blank instance. As you would expect, this resulted in total UI failure.
Intended Solution
Rossen’s plan is to check @ModelAttribute
method dependencies and invoke them in an appropriate order (vs the current order determined by reflection), or raise an exception (e.g. circular dependency).
While adding a significant degree of complexity & sophistication internally, at the API level this should provide an “it just works” intuitive extension of the present mechanism.
Rossen and Patras Vlad Sebastian have also opened consideration of @ModelAttribute
method depends on an @RequestMapping
method, i.e. a command object bound and validated by the @RequestMapping
method? Rossen thinks he could treat those differently and invoke them after, not before. He’s re-opened an old ticket SPR-5695 and will explore that as an option.
To me, this makes sense. I quite often have “render” methods to build & supplement the View Model, based on the entity after request-handling. However to some extent, I miss having a clear distinction between “referenceData” and “renderModel” as these are quite different phases of the request-handling lifecycle.
Do you use @ModelAttribute
methods? What do you think about the proposed improvements?
Read the full discussion here:
– SPR-6299 Support for @ModelAttribute interdependency