Reputation: 63
I develop REST application based on Spring MVC.
I have following hierarhy of Controllers:
UserController extends ImageController
ImageController extends SuggestController // Controller that has GET-DELETE-POST for image
SuggestController extends CrudController// Controller that has some suggest methods
CrudController // CRUD operations
So I had following mappings in runtume:
/users (POST-GET-PUT-DELETE)
/users/suggest (GET, POST(Spring Pageable))
/users/image (POST-GET-DELETE)
It was ok before I realized, that one controller must be able to give me images of its stuff, but cannot implement "suggest" methods:
/stuff (POST-GET-PUT-DELETE)
/stuff/image (POST-GET-DELETE)
And another one does not have "image" functionality, but has "suggest":
/things (POST-GET-PUT-DELETE)
/things/suggest (GET, POST(Spring Pageable))
Java says : "Use composition in such cases":
StuffController {
@InlineThisController
ImageController imagedController;
@InlineThisController
CrudController crudController;
... //some additional methods
}
So how can I acheive this in Spring MVC without boilerplate for delegation? Annotations? XML?
Upvotes: 2
Views: 2655
Reputation: 63
Here are possible approaches:
Upvotes: 0
Reputation: 148890
Spring MVC will not allow you to override method annotated with @RequestMapping, or more exactly it does not allow you to annotate the overriding method with @RequestMapping and will use the mapping in base class.
But you can always define 2 methods in base class : one annotated with @RequestMapping that only delegates to second not annotated. Then you are then free to override the second method in subclasses. Example :
Abstract base class for a CRUD controller
public abstract class AbstractCRUDController<K extends Serializable, X>
// X is the data class, K is the class of the key (int or String)
@RequestMapping({"/{data}"})
public String show(@ModelAttribute("data") X data, HttpSession session, Model model,
@RequestParam(value = "pagesize", required = false, defaultValue = "-1") int pageSize,
WebRequest request,
@RequestParam(value = "all", defaultValue = "false") boolean all) {
return doShow(data, session, model, pageSize, request, all);
}
protected String doShow(X data, HttpSession session, Model model,
int pageSize, WebRequest request, boolean all) {
return this.getPrefix() + "/view";
}
@RequestMapping(value={"/{data}/edit"}, method=RequestMethod.GET)
public String edit(@ModelAttribute("data") X data, Model model) {
return doEdit(data, model);
}
protected String doEdit(@ModelAttribute("data") X data, Model model) {
model.addAttribute("title", editTitle);
return this.getPrefix() + "/edit";
}
@RequestMapping(value={"/{data}/edit"}, method=RequestMethod.POST)
public String update(@ModelAttribute("data") X data, BindingResult result, Model model) {
if (data == null) {
throw new NoSuchElementException();
}
if (save(data, result, model, SimpleSaveType.UPDATE, null) != null) {
return "redirect:" + savedUrl(data);
}
else {
model.addAttribute("title", editTitle);
return getPrefix() + "/edit";
}
}
public K save(X data, BindingResult result, Model model, SaveType saveType, K id) {
...
}
...
public abstract String getPrefix();
}
Concrete implementation for class ProcessQ
@Controller
@RequestMapping(ProcessController.PREFIX)
public class ProcessController extends AbstractCRUDController<Integer, ProcessQ> {
public static final String PREFIX = "/process";
@Override
public String getPrefix() {
return PREFIX;
}
@Override
protected String doShow(ProcessQ process, HttpSession session, Model model,
int pageSize, WebRequest request, boolean all) {
String viewName = super.doShow(process, session, model, pageSize, request, all);
// specialized processing for class ProcessQ
...
return viewName;
}
...
}
Example is taken from a real program, that's the reason why you can see elements for pagination, error processing and access to underlying request.
Upvotes: 2