Mastering Spring MVC

Enjoy Spring-based Web development with the Spring MVC module

1 2 3 4 Page 2
Page 2 of 4
<servlet>
  <servlet-name>geeknews</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

Next, that DispatcherServlet needs to be mapped to a URI pattern. You can choose any pattern that you want, but most Spring applications map requests to pages ending in .htm. Add the following to your web.xml file:

<servlet-mapping>
  <servlet-name>geeknews</servlet-name>
  <url-pattern>*.htm</url-pattern>
</servlet-mapping>

In this case, all requests that end in .htm will be sent to the geeknews DispatcherServlet. By default, Spring looks for your Spring beans in a file whose name starts with the servlet name followed by -servlet.xml. So we need to create a geeknews-servlet.xml file that contains the HomePageController, our URL mapping strategy (to map URLs to controllers), and our view resolver, and place it in the WEB-INF directory. Listing 2 shows the contents of the geeknews-servlet.xml file.

Listing 2. geeknews-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"        
       xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">

   <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="mappings">
          <props>
              <prop key="/home.htm">homePageController</prop>
           </props>
          </property>
    </bean>

   <bean id="homePageController" class="com.geekcap.geeknews.web.HomePageController">
      <property name="geekNewsService" ref="geekNewsService" />
   </bean>

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

The geeknews-servlet.xml file defines the HomePageController with the name homePageController. It injects the geekNewsService bean (as a reference, which means this is not a String value, but rather a reference to another bean) into its geekNewsService attribute. The geekNewsService bean is created in the geeknews-service.xml file (discussed below). In addition to defining the homePageController, the geeknews-servlet.xml file defines the urlMapping bean and the viewResolver bean.

The urlMapping bean is responsible for translating a URL pattern to a controller. There are several URL mappers:

  • BeanNameUrlHandlerMapping: Maps a URL to a bean based on the name of the controller's bean, as defined in the bean's XML definition.
  • SimpleUrlHandlerMapping: Maps a URL to a bean based on a list of properties. (In Listing 2, the SimpleUrlHandlerMapping class is used to map /home.htm to the bean with the ID of homePageController.)
  • ControllerClassNameHandlerMapping: Maps a URL to a bean based on the bean's class name. For example, HomePageController would be mapped to /homePage*, such as /home.htm.
  • ControllerBeanNameHandlerMapping: Similar to the BeanNameUrlHandlerMapping mapper, but does not expect bean names to follow the URL convention. Also supports Controller annotations.
  • CommonsPathMapHandlerMapping: Maps URLs to a controller based on Jakarta Commons Attributes metadata.
  • DefaultAnnotationHandlerMapping: Maps URLs to a controller for methods that implement the RequestMapping annotation.

The view resolver is responsible for translating the view name in the ModelAndView into a component, such as a JSP, that renders the view. The several available view resolvers are mostly based on the type of view they are forwarding to. View resolvers exist for FreeMarker, Jasper Reports, Velocity, XML, XSLT, and of course JSPs via URL paths. Listing 2 defines the InternalResourceViewResolver, which prefixes the view name with / and suffixes it with .jsp. Thus when the HomePageController returns the view name home, it is resolved to /home.jsp.

Putting it all together:

  1. The DispatcherServlet handles all requests that end in .htm, so it handles /home.htm.
  2. The SimpleUrlHandlerMapping maps /home.htm to the HomePageController.
  3. The HomePageController loads a list of articles from the GeekNewsService and returns those articles in a ModelAndView object whose destination is home.
  4. The InternalResourceViewResolver prefixes home with / and suffixes it with .jsp, which means that it is forwarded to /home.jsp for presentation.

Listing 3 shows the complete web.xml file.

Listing 3. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
   version="2.4">

  <display-name>SpringMVC</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/geeknews-services.xml
        /WEB-INF/geeknews-dao.xml
    </param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>geeknews</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>geeknews</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

  <taglib>
    <taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
    <taglib-location>/WEB-INF/tld/c.tld</taglib-location>
  </taglib>

  <taglib>
    <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
    <taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
  </taglib>

  <welcome-file-list>
   <welcome-file>index.html</welcome-file>
  </welcome-file-list>

</web-app>

The DispatcherServlet automatically loads the geeknews-servlet.xml file, but when you build Spring applications it's a good practice to divide bean XML files into their logical components. In Listing 3, the contextConfigLocation context parameter defines two additional Spring XML files that should be loaded: geeknews-services.xml and geeknews-dao.xml. The ContextLoaderListener class is responsible for loading resources and reads the contextConfigLocation parameter to determine which configuration files to load. Finally, the web.xml file imports two components of the Java Standard Tag Library (JSTL) that will be used in the JSP file.

Listing 4 shows the source code for the home.jsp file.

Listing 4. home.jsp

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="com.geekcap.geeknews.core.*,java.util.List"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      <title>Geek News</title>
      <link type="text/css" rel="stylesheet" href="css/geeknews.css" />
   </head>

<body>

<%@ include file="header.jsp" %>

<div id="mainContent">

<p><a href="post.htm">Post</a></p>

<c:forEach items="${articles}" var="article">

<div class="article">
<p><a href="article.htm?id=<c:out value="${article.id}"/>"><c:out value="${article.title}" /></a> 
   by <span class="articleAuthor"><c:out value="${article.author}" /></span>
   on <span class="articleDate"><fmt:formatDate value="${article.date}" type="both" /></span>
</p>

<p class="articleSummary"><c:out value="${article.summary}" /></p>
</div>

</c:forEach>

</div>


<%@ include file="footer.jsp" %>

</body>
</html>

The details of the HTML and CSS presentation are unimportant for this discussion; what's important is that the request object has an articles variable in it. The following snippet demonstrates how the JSTL core library is used to iterate over all articles and display an article's author, title, publication date, and summary:

<c:forEach items="${articles}" var="article">

<p><a href="article.htm?id=<c:out value="${article.id}"/>">
       <c:out value="${article.title}" /></a> 

   by <c:out value="${article.author}" />
   on <fmt:formatDate value="${article.date}" type="both" />
</p>

<p class="articleSummary"><c:out value="${article.summary}" /></p>

</c:forEach>

Figure 1 shows a screenshot of the Geek News homepage.

Geek News home page
Figure 1. Geek News home page (Click to enlarge.)

The header consists of the words GEEK NEWS, hyperlinked to the home page. The body consists of a Post link, which brings up a form (which you'll implement later) for posting new articles, and then one section for each article that includes the title, author, publication date, and summary, followed by a horizontal line (implemented as a CSS bottom border on the article <div>). A footer (not shown) has copyright information (for "Fictitious Company," so don't worry, the code is yours to keep).

The header and footer are imported in the home.jsp page through the JSP include directive. They are both JSPs themselves. In their current state, they could be simple HTML documents, but in the future you might want to add a login link to the header or administration links to the footer.

Setup and deployment

Before you can compile and deploy the HomePageController, you need to download, decompress, and install the latest Spring Framework version (as of this writing, version 2.5.6) and its dependencies.

Spring partitions its functionality into several JAR files, so your WAR file can include only the functionality you need. It's up to you to track down all of the JAR files that you do need. The sample application needs the following Spring resources in its WEB-INF/lib folder, which you can find in Spring's dist/modules directory:

  • spring-beans.jar
  • spring-context.jar
  • spring-core.jar
  • spring-web.jar
  • spring-webmvc.jar

It also needs these resources, located in Spring's lib subdirectories:

  • commons-codec.jar (lib/jakarta-commons)
  • commons-logging.jar (lib/jakarta-commons)
  • jstl.jar (lib/j2ee)
  • standard.jar (lib/jakarta-taglibs)
  • log4j-1.2.15.jar (lib/log4j)

Finally, the Dao class uses JDOM, so you need to download JDOM. Then add jdom.jar (in JDOM's build directory) and xerces.jar (in JDOM's lib directory) to your WAR file.

With these JAR files in your CLASSPATH, you can compile the sample application with the Ant build.xml file included in the source-code download. This file loads a build.properties file that defines the following properties (which you must update to match your environment):

  • tomcat.home: The location where you have Tomcat installed; if you want to use a different application server then you may need to update the build.xml file to include the JAR file that contains the Servlet API..
  • jdom.home: The location where you have JDOM 1.1 installed
  • spring.home: The location where you have Spring installed.

In the end your WAR file should contain the following files:

index.html
article.jsp
footer.jsp
header.jsp
home.jsp
post.jsp
postSuccess.jsp
css/geeknews.css
images/articlebackground.jpg
images/blockquotebackground.gif
images/codebackground.gif
WEB-INF/web.xml
WEB-INF/geeknews-dao.xml
WEB-INF/geeknews-services.xml
WEB-INF/geeknews-servlet.xml
WEB-INF/classes/log4j.properties
WEB-INF/classes/com/geekcap/geeknews/core/GeekNewsService.class
WEB-INF/classes/com/geekcap/geeknews/core/NewsArticle.class
WEB-INF/classes/com/geekcap/geeknews/dao/FileSystemNewsArticleDaoImpl.class
WEB-INF/classes/com/geekcap/geeknews/dao/NewsArticleDao.class
WEB-INF/classes/com/geekcap/geeknews/web/HomePageController.class
WEB-INF/classes/com/geekcap/geeknews/web/LoadArticleController.class
WEB-INF/classes/com/geekcap/geeknews/web/PostArticleFormController.class
WEB-INF/classes/com/geekcap/geeknews/web/command/ArticleCommand.class
WEB-INF/classes/com/geekcap/geeknews/web/validator/NewsArticleValidator.class
WEB-INF/lib/commons-codec.jar
WEB-INF/lib/commons-logging.jar
WEB-INF/lib/jdom.jar
WEB-INF/lib/jstl.jar
WEB-INF/lib/log4j-1.2.15.jar
WEB-INF/lib/spring-beans.jar
WEB-INF/lib/spring-context.jar
WEB-INF/lib/spring-core.jar
WEB-INF/lib/spring-web.jar
WEB-INF/lib/spring-webmvc.jar
WEB-INF/lib/standard.jar
WEB-INF/lib/xerces.jar
WEB-INF/tld/c.tld
WEB-INF/tld/fmt.tld
WEB-INF/tld/fn.tld
WEB-INF/tld/permittedTaglibs.tld
WEB-INF/tld/scriptfree.tld
WEB-INF/tld/sql.tld
WEB-INF/tld/x.tld
1 2 3 4 Page 2
Page 2 of 4