Spring MVC Fast Tutorial: Form Processing
This tutorial is out of date. See the new version for Spring 4.
What are we going to build?
A form to create a new car.
Spring Form Processing
Form Controller
Struts controllers extend Action whereas Spring controllers extend/implement a Controller class/interface. There are many of them. The most basic is Controller, used previously. To process a form,SimpleFormController is generally used.
A form controller has two roles:
-
initialize the form's initial values
-
process/persist submitted user input:
Command
A Command object is used to store the form values: it's equivalent to a Struts Action Form. However, it doesn't have to extend nor implement anything. So it's even possible to directly use the model class! (Car in our example)
Manager Classes
We need a method to create a car in 'WEB-INF/src/springmvc/service/CarManager.java':
public Car createCar(Car c) {
Car car = new Car();
car.setId((long)carList.size() + 1);
car.setBrand(c.getBrand());
car.setModel(c.getModel());
car.setPrice(c.getPrice());
carList.add(car);
return car;
}
We also need a manager for brands, 'WEB-INF/src/springmvc/service/BrandManager.java':
package springmvc.service;
import java.util.LinkedList;
import java.util.List;
import springmvc.model.Brand;
public class BrandManager {
private static List<Brand> brandList;
static {
Brand brand1 = new Brand();
brand1.setId((long)1);
brand1.setName("Mercedes");
brand1.setCountry("Germany");
Brand brand2 = new Brand();
brand2.setId((long)2);
brand2.setName("Peugeot");
brand2.setCountry("France");
brandList = new LinkedList<Brand>();
brandList.add(brand1);
brandList.add(brand2);
}
public List<Brand> getBrandList() {
return brandList;
}
public Brand getBrandById(Long id) {
for (Brand brand : brandList) {
if (brand.getId().equals(id))
return brand;
}
return null;
}
}
Let's not frown upon the data duplication, it's not the point here
Controller
In 'WEB-INF/springmvc-servlet.xml', we declare a new URL and its Controller:
<bean name="/new_car.html" class="springmvc.web.CarNewController"> <property name="commandClass" value="springmvc.model.Car"/> <property name="formView" value="carNew"/> <property name="successView" value="list_cars.html"/> </bean>
-
commandClass: the Command class
-
formView: the form view name
-
successView: where we go after the form has been successfully submitted
We now create this Controller: 'WEB-INF/src/springmvc/web/CarNewController.java':
package springmvc.web;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;
import springmvc.model.Brand;
import springmvc.model.Car;
import springmvc.service.BrandManager;
import springmvc.service.CarManager;
public class CarNewController extends SimpleFormController {
@Override
protected Object formBackingObject(HttpServletRequest request) throws Exception {
Car defaultCar = new Car();
defaultCar.setModel("new model");
defaultCar.setPrice(new BigDecimal(15000));
return defaultCar;
}
@Override
protected Map referenceData(HttpServletRequest request) throws Exception {
Map<Object, Object> dataMap = new HashMap<Object, Object>();
BrandManager brandManager = new BrandManager();
dataMap.put("brandList", brandManager.getBrandList());
return dataMap;
}
@Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.setDisallowedFields(new String[] {"brand"});
Car car = (Car)binder.getTarget();
// set car's brand from request parameter brand id
BrandManager brandManager = new BrandManager();
Long brandId = null;
try {
brandId = Long.parseLong(request.getParameter("brand"));
} catch (Exception e) {}
if (brandId != null) {
Brand brand = brandManager.getBrandById(brandId);
car.setBrand(brand);
}
}
@Override
public ModelAndView onSubmit(Object command) throws ServletException {
CarManager carManager = new CarManager();
carManager.createCar((Car)command);
return new ModelAndView(new RedirectView(getSuccessView()));
}
}
These methods are called before the form is displayed:
-
formBackingObject: initialize the Command used to init the form.
-
referenceData: set the view attributes (using a Map)
These are called after:
-
initBinder: prevent Spring to do some bindings and so them by ourselves if needed. Here we used the brand id parameter to set the actual Brand.
-
onSubmit: the main code. In this case, we used the command object, which is a Car, to create a new Car.
View
Let's now create the view 'jsp/carNew.jsp':
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<body>
<h1>New Car</h1>
<form:form method="post">
Brand<br />
<form:select path="brand">
<form:options items="${brandList}" itemLabel="name" itemValue="id" />
</form:select>
<br /><br />
Model<br />
<form:input path="model"/><br /><br />
Price<br />
<form:input path="price"/><br /><br />
<input type="submit" value="Submit">
</form:form>
</body>
</html>
It's blissfully simple. We use the brandList attribute defined in referenceDatamethod in the controller to populate the form select.
Result
We rebuild (ant), relaunch Tomcat and check it's working:http://localhost:8180/springmvc/new_car.html
Summary
Your project should now look like that:
You can download it here.
This is how Spring processes a form. Some advantages compared to Struts:
-
controller code is divided between several methods to override only when necessary
-
model beans can be used as form beans: no extra classes to write. For complex attributes (like brand in our example), we can override Spring and do the bindings ourselves.
-
xml configuration is clear and straightforward