4. Code Example

Example 1. PersonController

The @Controller indicates the class is a Spring MVC controller stereotype which is automatically registered by context:component-scan in the web-application-context.xml. The @RequestMapping annotation on the methods use the value attribute to map the method to a path. The method attribute is used to indicate the HTTP request type (ex: GET, POST, DELETE). More sophisticated targeting based on available parameters can also be done, but is not needed in this example.

The first method, newRequest, is annotated with @ModelAttribute. This indicates that the method will be called before every request. In this case it takes the request parameter 'id', but doesn't require the parameter. By default specifying @RequestParameter would cause an error if it wasn't available and no other method was available for processing the request. The newRequest method looks up the person from the database and returns it or if the 'id' param is null it returns a new instance for the form to bind to. Without specifying a specific name for the model to be bound to, it will be bound to the class name. So Person will be bound to 'person'.

The first form method handles a create and edit for an HTTP GET request since it's annotated with @RequestMapping(method=RequestMethod.GET). It's just a place holder since the newRequest method has already created or retrieved the appropriate bean from the db. By default, Spring will continue forwarding the request where it was headed before it was intercepted by the controller. This could be changed by returning a String with the new path or by returning a view instance (like ModelAndView.

The second form method handles a save from the person form. It will only accept a request that is an HTTP POST and it has the method signature form(Person person, Model model). By specifying the person variable, Spring will automatically retrieve or create (depending on it's scope) an instance of Person and bind any available request parameters to it. Since it is also the default model object, any values set on it will be available on the page the request forwards to. The Model is made available just by specifying it. This can also be done for the HttpServletRequest and HttpServletResponse. The method sets a create date if one isn't already set, saves the person bean, then returns the saved person instance which replaces the existing model after a success message is set on the model for display on form.

The last two method are delete and search. The delete method is very straight forward. It just deletes person, and then redirects to the search page. The search method retrieves all persons and returns them in a Collection. It doesn't explicitly set the return value to be bound to the scope 'persons' using the @ModelAttribute(SEARCH_MODEL_KEY).

                
@Controller
public class PersonController {

    private static final String SEARCH_VIEW_KEY = "redirect:search.html";
    private static final String SEARCH_MODEL_KEY = "persons";

    private final PersonRepository repository;

    @Autowired
    public PersonController(PersonRepository repository) {
        this.repository = repository;
    }

    /**
     * For every request for this controller, this will 
     * create a person instance for the form.
     */
    @ModelAttribute
    public Person newRequest(@RequestParam(required=false) Integer id) {
        return (id != null ? repository.findOne(id) : new Person());
    }

    /**
     * <p>Person form request.</p>
     * 
     * <p>Expected HTTP GET and request '/person/form'.</p>
     */
    @RequestMapping(value="/person/form", method=RequestMethod.GET)
    public void form() {}
    
    /**
     * <p>Saves a person.</p>
     * 
     * <p>Expected HTTP POST and request '/person/form'.</p>
     */
    @RequestMapping(value="/person/form", method=RequestMethod.POST)
    public Person form(Person person, Model model) {
        if (person.getCreated() == null) {
            person.setCreated(new Date());
        }

        Person result = repository.saveAndFlush(person);
        
        model.addAttribute("statusMessageKey", "person.form.msg.success");
        
        return result;
    }

    /**
     * <p>Deletes a person.</p>
     * 
     * <p>Expected HTTP POST and request '/person/delete'.</p>
     */
    @RequestMapping(value="/person/delete", method=RequestMethod.POST)
    public String delete(Person person) {
        repository.delete(person);

        return SEARCH_VIEW_KEY;
    }

    /**
     * <p>Searches for all persons and returns them in a 
     * <code>Collection</code>.</p>
     * 
     * <p>Expected HTTP GET and request '/person/search'.</p>
     */
    @RequestMapping(value="/person/search", method=RequestMethod.GET)
    public @ModelAttribute(SEARCH_MODEL_KEY) Collection<Person> search() {
        return repository.findAll();
    }

}