Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

In this page I'd like to look carefully at where that command object is coming from and what binding is being done.

Panel
bgColor#FFFFCE
borderStylesolid
titleWhere is the object coming from?borderStylesolid

We need to look carefully at the "object" argument. The object here is a Command. LoginController extends SimpleFormController which extends AbstractFormController which extends BaseCommandController which extends AbstractController which extends WebContentGenerator which extends WebApplicationObjectSupport which extends ApplicationObjectSupport. In Spring Web MVC FormControllers, the forms play the role of "commands" in the more general CommandController model.

So, in AbstractController there is a final method handleRequest():

Code Block
titleAbstractController handleRequest()
public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		// delegate to WebContentGenerator for checking and preparing
		checkAndPrepare(request, response, this instanceof LastModified);

		// execute in synchronized block if required
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				synchronized (session) {
					return handleRequestInternal(request, response);
				}
			}
		}
		
		return handleRequestInternal(request, response);
	}

As we can see here, it delegates to the method handleRequestInternal(), which is declared to be abstract:

Code Block
titleAbstractController handleRequestInternal()
/**
 * Template method. Subclasses must implement this.
 * The contract is the same as for handleRequest.
 * @see #handleRequest
 */
 protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
	    throws Exception;

AbstractCommandController implements this abstract method:

Code Block
titleAbstractCommandController handleRequestInternal() implementation
/**
 * Handles two cases: form submissions and showing a new form.
 * Delegates the decision between the two to isFormSubmission,
 * always treating requests without existing form session attribute
 * as new form when using session form mode.
 */
 protected final ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    if (isFormSubmission(request)) {
        if (isSessionForm() && request.getSession().getAttribute(getFormSessionAttributeName()) == null) {
            // cannot submit a session form if no form object is in the session
            return handleInvalidSubmit(request, response);
        }
        
        // process submit
        Object command = getCommand(request);
        ServletRequestDataBinder binder = bindAndValidate(request, command);
        return processFormSubmission(request, response, command, binder.getErrors());
    
   } else {
        return showNewForm(request, response);
   }
 }

And in turn delegates to processFormSubmission(). Note that it first binds the request to the command and validates the request.

Warning
iconfalse
titlebinding

The bindAndValidate() method is implemented in BaseCommandController as:

Code Block
titleBaseCommandController bindAndValidate()
/**
	 * Bind the parameters of the given request to the given command object.
	 * @param request current HTTP request
	 * @param command the command to bind onto
	 * @return the ServletRequestDataBinder instance for additional custom validation
	 * @throws Exception in case of invalid state or arguments
	 */
	protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)
			throws Exception {
		ServletRequestDataBinder binder = createBinder(request, command);
		binder.bind(request);
		onBind(request, command, binder.getErrors());
		if (this.validators != null && isValidateOnBinding() && !suppressValidation(request)) {
			for (int i = 0; i < this.validators.length; i++) {
				ValidationUtils.invokeValidator(this.validators[i], command, binder.getErrors());
			}
		}
		onBindAndValidate(request, command, binder.getErrors());
		return binder;
	}

That createBinder method it invokes looks like this:

Code Block
titleBaseCommandController createBinder()
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command)
	    throws Exception {
		ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName());
		if (this.messageCodesResolver != null) {
			binder.setMessageCodesResolver(this.messageCodesResolver);
		}
		initBinder(request, binder);
		return binder;
	}

The upshot of the matter is that setter methods for JavaBean properties the names of which correspond to the names of parameters on the HttpServletRequest are invoked with the values of those HttpServletRequest parameters as arguments.

whereas processFormSubmission() is an abstract method of AbstractFormController:

Code Block
titleprocessFormSubmission is abstract method of AbstractFormController
protected abstract ModelAndView processFormSubmission(
			HttpServletRequest request,	HttpServletResponse response, Object command, BindException errors)
			throws Exception;

LoginController defaults this Command class:

Code Block
titleLoginController afterPropertiesSet() excerpt
  public void afterPropertiesSet() throws Exception {

        if (this.getCommandClass() == null) {
            this.setCommandName("credentials");
            this.setCommandClass(UsernamePasswordCredentials.class);

In BaseCommandController, we have:

Code Block
titleBaseCommandController getCommand()
/**
 * Retrieve a command object for the given request.
 * <p>Default implementation calls createCommand. Subclasses can override this.
 * @param request current HTTP request
 * @return object command to bind onto
 * @see #createCommand
 */
 protected Object getCommand(HttpServletRequest request) throws Exception {
    return createCommand();
 }

And the createCommand() method to which it delegates:

Code Block
titleBaseCommandController createCommand()
/**
 * Create a new command instance for the command class of this controller.
 * @return the new command instance
 * @throws InstantiationException if the command class could not be instantiated
 * @throws IllegalAccessException if the class or its constructor is not accessible
 */
 protected final Object createCommand() throws InstantiationException, IllegalAccessException {
    if (this.commandClass == null) {
         throw new IllegalStateException("Cannot create command without commandClass being set - " +
            "either set commandClass or override formBackingObject");
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
    }
    return this.commandClass.newInstance();
 }

In summary

To sum up, the "command" argument of type Object to the processFormSubmission() method of LoginController is an instance of the Class that was set using the setCommandClass() method by configuring the commandClass JavaBean property of LoginController or by allowing it to default as implemented in LoginController's afterPropertiesSet().

...