Translations of this page:

Spring MVC

Most Java™ Platform, Enterprise Edition (JEE) applications will be accessed through a web front end. In the JEE environment this means Java Servlets. The predominant architecture when they were first introduced was to employ a servlet to process the incoming parameters, talk to the business logic using other classes or directly to a database via JDBC (Java Database Connectivity) and then package up the results in an HTML page. The problem with this is that it mixes control, presentation and business logic. Web pages designers have to know some Java and everything is tightly coupled making the code fragile and hard to maintain.

All of that is old knowledge. Java Server Pages, an HTML page with embedded Java that gets pre-compiled into a Java Servlet, went some way to separating display logic from other code. Template engines such as Freemarker, Velocity and more recently, Sun's JSTL (JavaServer Pages Standard Tag Library) made it even simpler for page designers to access dynamically generated data. Parameter processing by the Servlet and business logic were also separated. This common design pattern was codified into the Model (backend data), View (web pages) and Controller (request/response processing) or MVC.

Spring Dispatcher Servlet

Many applications still consisted of a lot of controller servlets which required configuration. A further improvement was the introduction of the Front Controller pattern. Now a single servlet handles the request and then hands over processing to other parts of the application to perform the actual work. Spring's MVC model implements this framework with its DispatcherServlet class filling the role of the front controller.

DispatcherServlet has to be configured in the Web Application's (WAR) web.xml file like any other servlet:

<servlet>
    <description>Spring MVC Dispatcher Servlet</description>
    <display-name>DispatcherServlet</display-name>
    <servlet-name>spring2jpa</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

Context Loader Listener

The servlet-name is important. When the Web application starts and it will look for the Spring application context, the file that wires the application together using spring2jpa as the root name

WEB-INF/spring2jpa-servlet.xml

For the rest of this document we will refer to this as the servlet.xml file.

We need to configure a context loader in the web.xml file. Normally we will use ContextLoaderListener but some web containers initialize servlet listeners after servlets, in this case we need to use ContextLoaderServlet. To make configuration cleaner and easier to maintain we have specified a separate configuration file for the Service layer (Model) application context.

<context-param>
    <param-name>contextConfigLocation</param-name> 
    <param-value>/WEB-INF/applicationContext-jpa.xml</param-value> 
</context-param>
 
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener>
<code>
 
Finally we map any resource that ends in ".htm" to the front controller. We could have used any extension we liked. As far as a web server, or search engine, is concerned the content appears to be static HTML. 
 
<code xml>
<servlet-mapping>
    <servlet-name>spring2jpa</servlet-name>
    <url-pattern>*.htm</url-pattern>
</servlet-mapping>

Mapping resources to controllers

“Would you tell me, please, which way I ought to go from here?” “That depends a good deal on where you want to get to,” said the Cat. “I don't much care where-” said Alice. “Then it doesn't matter which way you go,” said the Cat. ”-so long as I get somewhere,” Alice added as an explanation. “Oh, you're sure to do that,” said the Cat, “if you only walk long enough.” - Alice in Wonderland

We want to tell the front controller how each .htm resource maps onto individual controller classes. We can do this with the SimpleUrlHandlerMapping. This is configures in the servlet.xml file. Here we associate the resource home.htm with the class mainController and custdet.htm with custDetailsController.

  <bean id="controllermap" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
    <props>
      <prop key="/home.htm">mainController</prop>
      <prop key="/custdet.htm">custDetailsController</prop>
    </props>
    </property>
  </bean>

If you have a lot of mappings to do it is better to use ControllerClassNameHandlerMapping. This uses a convention to map a resource name to a controller so home.htm would map to HomeController etc.

Main Controller

This controller bean receives the request from the front controller when someone accesses the resouce home.htm in the application context. It interacts with any service classes and sends the results to a page returned to the user's web browser (the View). The “home page” controller of the Spring2JPA application is very simple. It extends the Spring MVC AbstractController class. The handleRequestInternal method requests a list of all Customer objects using a CustomerDAO service that has been injected into the controller by the Spring framework.

....
 
public class MainController extends AbstractController {
  public ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) {
    List<Customer> customers = customerDAO.findAll();
....

The method returns this list to the browser using a ModelAndView object. Every controllers handleRequestInternal() method must return a ModelAndView. The object encapsulates the View and the Model data that will be used by the View. The first parameter is a logical name, the view resolver uses this name to look up the actual View object.

The next parameters are a name/value pair giving the name of the Model and the Model itself. These will be accessible from the View.

    return new ModelAndView("home", "customers", customers);
}

We have to declare MainController bean in the servlet.xml file. This also states that the service class customerDAO (configured in applicationContext.xml) will be injected into the controller. This injection chit-chat all sounds very sexy but in reality the Spring framework calls a setter method setCustomerDAO(CustomerDAO c).

<bean id="mainController" class="com.abcseo.mvc.MainController">
    <property name="customerDAO">
      <ref bean="customerDAO"/>
    </property>
</bean>

The View Resolver

The final part of the jigsaw is getting from our MainController object to the page that displays the Model data. The view resolver bean takes the first parameter returned by the ModelAndView object. The view resolver is declared in the servlet.xml file.

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

There are a number of different view resolvers for different purposes. The InternalResourceViewResolver uses the view name and adds the prefix and suffix to lookup the actual resource in the WAR file, in this case /jsp/home.jsp.

The View

In our example we are using JSTL to access the model data from our JSP. The View lists all of the customers returned by the model in a table.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
....
<c:forEach var="cust" items="${customers}">
<tr>
<td><a href="custdet.htm?custID=${cust.custid}">${cust.custid}</td>
<td>${cust.firstName} ${cust.lastName}</td>
</tr>
</c:forEach>

Handling Parameters

In the home View each customer is clickable. Clicking on a link calls the custdet.htm resource with the customer id in the query string. The aim is to return a detailed view of the customer via the same

front controller -> customer details controller -> customer data access object -> customer view

Process we saw with the main controller. We have already shown how the custdet.htm resource is mapped onto the custDetailsController class. As we need to talk to the Model this controller is declared in the same way as the MainController to have a customerDAO service object injected. We also need to handle our customer id parameter.

This time our controller extends the AbstractCommandController class. Command controllers automagically bind request parameters to a command objects. They can also be wired to validators to ensure that the request parameters are okay. Our command object has setters and getters for each request parameter.

public class CustDetailsCommand {
  private long custID;
 
  public long getCustID() { return custID; }
 
  public void setCustID(long i) { custID = i; }
}

The constructor sets the name of the command class. The main execution method is handle(). The Object parameter is our command object. We cast this to the correct type and use the setter to get the customer id passed via the query string. We then look up the customer details using this key and return this data to the View resolver.

public class CustDetailsController extends AbstractCommandController {
 
  public CustDetailsController() {
    setCommandClass(CustDetailsCommand.class);
  }
 
  protected ModelAndView handle(HttpServletRequest req,
      HttpServletResponse resp, Object cmd, BindException ex)
      throws Exception {
    CustDetailsCommand c = (CustDetailsCommand) cmd;
    Customer cust = customerDAO.findById(c.getCustID());
    return new ModelAndView("custdetails", "cust", cust);
  }
....

That's pretty much it for putting together the MVC part of our application. Next we'll look at how we assemble all this with Maven.

Part III: Building the Spring MVC WAR with Maven

Resources

Spring in Action, 2nd Edition, Craig Walls, ch 13-14

tech/java/spring-mvc.txt · Last modified: 2007/10/02 17:15 by davidof
Recent changes RSS feed