SPRING MVC


introduction | spring mvc webapp basics | ajax and jquery | spring mvc webapp features | practicalities

Last updated: Sept 2009

Spring can be used in combination with many web frameworks to help wire things together. However, Spring also has its own web framework, called Spring MVC, which takes full advantage of Spring's IoC, AOP, and testability. This version of WebOfContacts also demonstrates how a Web application can be made to feel a bit more desktop-like with the use of JavaScript/jQuery/Ajax.




introduction

For the Spring MVC/Ajax version of WebOfContacts these technologies were used:
  • Java 6 (a.k.a. 1.6)
  • Spring Framework 2.5.5 including Spring MVC
  • Hibernate 3.2
  • Tomcat 6.0.14
  • MySQL 5.0.45
  • Eclipse 3.4
  • JUnit 3
  • EasyMock 2.4
  • jQuery 1.3.1
  • jQuery UI 1.5
If you want to download the .war and run this application on your own machine, then you can as Spring, and Hibernate are included as jars in the webapp. However you will already need to have a version of Java at 5 or greater, a recent version of Tomcat, and you'll have to set up the database contactsdb. Then download the WAR (make sure to retain the .war extension) from this site into your Tomcat's webapps directory, and view the application in your browser at http://localhost:8080/WebOfContactsSpringMVCAjax/. The full source is available for download as an Eclipse project.



spring mvc webapp basics

The first thing to do to get a Spring MVC web application working (after copying the Spring jars into your webapp) is set up Spring's DispatcherServlet in your web.xml and define a servlet mapping so that it handles all requests ending in .html. The DispatcherServlet, when it receives a request, consults its handler mappings to decide what Controller will deal with it. It used to be customary to define these mappings in your application context (a bit like the way Struts has its mapping configuration in an XML file); however, the 2.5 release of Spring MVC introduced a radical change of syntax. Using annotations each mapping can be defined in the Controller Java files. Here is an example to show the home page:

@Controller
public class HomePageController
{	
    @RequestMapping("/home.html")
    public ModelAndView showHome() {
	// Set up the model with a view name that leads to home.jsp
        ModelAndView mv = new ModelAndView("home"); 
        return mv;
    }
}

The first annotation, @Controller simply tells the Spring infrastructure to recognize (scan) this Java class as a Controller. The @RequestMapping annotation declares what URL requests this Controller handles. The ModelAndView object takes a view name as a parameter: this effectively means that the home.jsp file will be used for the view. Inside ModelAndView there is also a map of name-value pairs which you could write to in the Java above (if this were a non-trivial example), and then read from in the JSP.

Spring MVC has a whole hierarchy of Controller types. However, you will notice the above class does not extend anything. In earlier versions of Spring MVC it would have extended MultiActionController because it is a Controller that allows each method to be an action (like DispatchAction in Struts). However, here it is not constrained by an implementation hierarchy at all. You can name the methods whatever you like; you can even add parameters at will! For example, if you need to access the request object inside that method, you would simply add HttpServletRequest request as a parameter to showHome() and it would be injected and ready for use.

Not surprisingly the magic of annotations will not work unless you do a bit of general configuration work. This is Spring so you do it in your application context. Notice in the first line, you say what package in your application needs to be scanned for annotated controllers:

    <context:component-scan base-package="com.oranda.webofcontacts.springmvc.controller" />
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>    
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

Inside the JSPs, JSTL tags are used for logic. For forms Spring MVC provides its own tag library form. A small form looks like the following. It is based on a domain object User with attributes username and password.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form commandName="user">
   <tr><td>Username</td><td><form:input path="username"/></td></tr>
   <tr><td>Password</td><td><form:input path="password"/></td></tr>
   <tr><td colspan="2"><input type="submit" value="Create User"/></td></tr>
</form:form>

This code is inside userForm.jsp. By default it will submit to userForm.html as a POST. Inside UserFormController the following method handles the submission of this form:

    @RequestMapping(value = "/userForm.html", method = RequestMethod.POST)
    public ModelAndView submitForm(@ModelAttribute("user") User user, 
    		BindingResult result) {
    	ModelAndView mv = new ModelAndView();
    	if (result.hasErrors()) {
    		// Redisplay the form
    		mv.setViewName("userForm");
    	} else {
    		this.userService.createUser(user);
    		mv.setViewName("redirect:listUsers.html");
    	}
    	
    	return mv;
    }

Again the method name submitForm is entirely arbitrary: the thing that matters is the annotation line above it which says that this method will handle any POST to userForm.html (a GET is handled by a different method which just displays the form). Also we have added parameters to the method simply because we need them. Notice that the ModelAttribute has the same name ("user") as the commandName in the form. The framework will bind the form elements to the User attributes. If there was a problem we will see it in the other argument, the BindingResult and redisplay the form. If there is no problem, a call to the service layer to actually create the user is made, and there is a redirect to the list of users.

A question: where does the userService come from? The answer: it is "autowired". In the UserFormController it is declared as an instance variable like this:

@Autowired
private UserService userService;

The presence of the @Autowired tag means you don't even have to declare in XML that the UserFormController bean uses the UserService; it is picked up by the scanner.




ajax and jquery

In a traditional web application, each action requires a new web page to be loaded, even if the parts (header, footer, navigation, etc) of the new page are the same as on the old page. Ajax (Asynchronous JavaScript with XML) allows calls to the server to made using JavaScript without loading or reloading a web page. This allows the webapp more like a desktop application, and the user perceives an increase in responsiveness.

In an Ajax call, data is passed back to the page in an XMLHttpRequest object. Although it is called that, the data does not have to be in XML format. In fact it is now more common to put it in a format called JSON, which can be parsed directly as a JavaScript object.

Since around 2005 a number of JavaScript libraries have become popular as ways of taking advantage of Ajax and also shielding the developer from inconsistencies between browser implementations of JavaScript. These libraries include jQuery, Dojo, Ext, and Yahoo UI. There are also packages that can help on the server-side. For instance, GWT lets you write you whole application in Java and translates the client-side parts to JavaScript automatically. Another nice package, DWR (Direct Web Remoting), lets you make calls to Java objects from JavaScript -- you set up the DWR servlet, and expose your Java beans in a dwr.xml file.

Probably the most popular client-side library now is jQuery, and this is what WebOfContacts uses to add effects and make Ajax calls. The first thing about jQuery is that it helps you separate out the JavaScript from the HTML. For instance, instead of embedding an onload event in your body tag, you would write:

$(window).load(function() {
    // run this when the whole page has been downloaded
});

However, it is usually desirable to begin processing before images have been downloaded, so instead of $(window).load(function()... as above, it is more common with jQuery to use $(document).ready(function().... Notice the use of anonymous callbacks, very commonly used in jQuery to add functionality to events.

jQuery is also much praised for its convenient selector syntax. For instance if you have a CSS element with the id navbar you can refer to it just as $("#navbar"). (The $ sign is actually a shorthand for jquery, which is found in the imported jquery.js file.) It is also very easy to apply functionality to an event on a set of CSS elements. To make all links slowly disappear on being clicked:

  $("a").click(function(event){
    event.preventDefault();
    $(this).hide("slow");
  });

In WebOfContacts, jQuery is used to preload pages so that later clicks on the navigation bar will show them instantly. The main control file is called navigation.js. The file index.jsp includes all parts of the page, and just loads documents into the main part as requested. When each document (e.g. home.jsp) is loaded it calls a function onPageLoaded in navigation.js which searches through a list of pages to be preloaded, examines the content of div tags in index.jsp to see which ones have already been preloaded, and preloads any that have not been loaded so far using the jQuery function load:

  pageDiv.load(pageId + ".html");

When the user actually requests to see a page, then if the page has been preloaded, the hidden content is displayed, or, if it hasn't, is loaded specially.

jQuery has become so popular that a vast number of widgets have been written for it, some (but not all!) very good. A standard sub-package is jQuery UI which includes widgets, transition effects, and "interactions" such as drag-and-drop. WebOfContacts makes use of the progressbar widget to show the progress of preloading pages, and also the accordion widget for the navigation menu. In navigation.js the core code to initialise the menu (nav-link) is:

    $("#nav-link a").click(function(event) {
      showPageRequested(this.id, this.href);
    });
  
    $('#nav-link').accordion();

The accordion widget allows boxes to slide under each menu item, making it possible to have submenus (although in WebOfContacts there are only descriptions shown under each menu item.)




spring mvc webapp features

Form validation in Spring MVC is done by implementing the Validator interface. The main method is validate() and this takes two arguments: the form object to validate, and an Errors object for recording any problems that are found. An example: on submitting the main Contact form, one of the first things the controller does is call new ContactValidator().validate(contact, result);. This runs the following method:

	public void validate(Object obj, Errors errors) {
		Contact contact = (Contact) obj;
		String firstName = contact.getFirstName();
		if (!StringUtils.hasLength(firstName)) {
			errors.rejectValue("firstName", "required", "required");
		} 
                // Use java.util.regex.* to validate other fields.
                // ...
        }

If the user does not enter a first name then after submission the form is redisplayed with required next to the field. Notice that "firstName" is passed as a parameter to the form:errors tag in the JSP.

 First Name (*): <font color="red"><form:errors path="firstName"/><font>

Note that this covers server-side validation only. If you want to do client-side validation (i.e. JavaScript popups before form submission) too, a good option is to use the Valang package. Although not currently implemented in WebOfContacts, here is an example of Valang rules from the Spring documentation:

<bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
  <property name="valang">
    <value>
    <![CDATA[
      { firstName : length(?) < 30 : 'First name too long' : 'first_name_length' : 30}
      { lastName : length(?) < 50 : 'Last name too long' : 'last_name_length' : 50 }
    ]]>
    </value>
  </property>
</bean>

If you add support for Valang in the JSP form, these limits on the length of first name and last name will be applied both on the client-side and server-side.

File upload is also pretty straightforward. In the JSP just use a regular input tag:

  <form:form commandName="contact" enctype="multipart/form-data">
  ...
    <input type="file" name="photoFile"/>
  ...

In the controller, inject the photoFile parameter as a MultipartFile into the method that handles the submit, and read the input stream from it:

public ModelAndView submitForm(@ModelAttribute("contact") Contact contact, 
    		BindingResult result, HttpServletRequest request,
    		@RequestParam("photoFile") MultipartFile photoFile) {
...
    InputStream in = photoFile.getInputStream();
...

For authentication and authorization, Spring Security (formerly Acegi) is used. In the webapp's web.xml it is necessary first to set up an instance of DelegatingFilterProxy to intercept * (every URL for the webapp). The next key thing is to configure the application context (specifically the applicationContext-security.xml file) to say what roles apply to URL patterns. E.g. to say the user needs to be logged on a user for everything except the login page:

    <sec:http auto-config="true">
        <sec:intercept-url pattern="/loginForm.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <sec:intercept-url pattern="/**" access="ROLE_USER"/>
...        

We also define here the login page and the page the user will be redirected to on a login failure.

        <sec:form-login login-page="/loginForm.html" authentication-failure-url="/index.html?login_error=1" /> 

Also in applicationContext-security.xml we declare our data source for usernames and passwords. (This is a lot simpler than previous version of Acegi, as it relies on defaults.)

    <sec:authentication-provider>
      <sec:jdbc-user-service data-source-ref="dsWebOfContacts" />
    </sec:authentication-provider>

dsWebOfContacts is simply a bean of type BasicDataSource whose attributes are defined elsewhere in the application context. It points to a database which must include a certain schema. By default Acegi expects two tables: USERS and AUTHORITIES. This needs to be understood by the webapp's data access code so that when it creates users it also create entries in the AUTHORITIES table for each user with the appropriate roles (ROLE_USER).

Any logon attempt should submit to a special URL /j_spring_security_check. In WebOfContacts, logon needs to be done using Ajax, so in the web.xml another filter is set up to deal specially with Ajax requests called AjaxSecurityFilter. If a call to /j_spring_security_check is not an Ajax call, then AjaxSecurityFilter will ensure the default processing is followed. Otherwise the URL being requested is extracted and an AjaxLoginResponse object is created based on what the URL is and whether there was an error or not. This response is converted to a format called JSON using the popular XStream/Jettison library, which is a format that can be interpreted by JavaScript as an object to be manipulated directly. On the client-side the login form submit handler looks like this:

	function ajaxLogin() {    
	    var serializedForm = $("#loginForm").serialize();
	    $.post("j_spring_security_check", serializedForm,
         	function(loadData) {
              var error = loadData.loginResponse.error;
              var url = loadData.loginResponse.url;
              
              if (error != null) {
                $("#login-error").append("Login Failed<BR/>");
              } else if (url != null) {
                onLoginSuccess(url);
              }
			}, "json");
    }
This uses the jQuery post function to submit the serialized login form and defines an anonymous callback function to deal with the result. The callback function takes a loadData parameter, which is the expected response, i.e. the JSON object referred to above. Now if the data has no error but instead a URL to go to, jQuery can load in the page in the normal way.



practicalities

In terms of usability Spring MVC is one of the best Java frameworks in my opinion. The annotation style can be confusing at first, but once you get used to it, its flexibility and power become apparent. Along with Spring come many utilities and "Template" classes which enable you to do just about anything you want to. You also have the option to use the Spring Web Flow add-on if your presentation logic gets complicated.

Because Spring MVC has undergone a major revision with 2.5, a lot of the documentation on the Web and in books is outdated and this can be confusing. Guides to the new version are still a bit thin on the ground but the basics are solidly covered at the Spring Framework main site, and Juergen Hoeller's blog.

With this framework I am doing unit testing with JUnit and EasyMock. EasyMock is not specific to Spring MVC -- it can be used with any framework -- but here is as good a place as any to demonstrate its use. EasyMock lets you test components in isolation by mocking out the other components they interact with.

In TestContactFormController the setup method has this code:

    contactFormController = new ContactFormController();
    mockContactService = EasyMock.createMock(ContactService.class);
    contactFormController.setContactService(mockContactService);

This creates a real ContactFormController but makes it use a mock ContactService. Now to test showing the form we have this code:

	public void testShowFormWithContact() throws Exception {
		Contact testContact = new Contact();
		testContact.setId(1);
		testContact.setFirstName("testContactFirstName");
		EasyMock.expect(this.mockContactService.getContact(1L)).andReturn(testContact);
		EasyMock.replay(this.mockContactService);
		
		ModelAndView mv = this.contactFormController.showForm(1L);
		EasyMock.verify(this.mockContactService);
		
		assertEquals("contactForm", mv.getViewName());
		Contact returnedContact = (Contact) mv.getModel().get("contact");
		assertEquals(1, returnedContact.getId());
		assertEquals("testContactFirstName", returnedContact.getFirstName());
	}

The main line in this code, the line that actually runs the test is ModelAndView mv = this.contactFormController.showForm(1L);. Now, when this line is run, it will try to show the Contact with the ID 1, but it won't hit the database: it will call the mock contact service. We need to specify what this pretend service should return, and this is exactly what is done in the preceding lines: specifically with EasyMock.expect. The EasyMock.replay line just switches the state, declaring, "We have finishing stating our expectations. Now we are ready to run the code we are testing." Then afterwards, EasyMock.verify is called to check that getContact was actually called on the mock object. Then we do the normal JUnit tests to see that the returned ModelAndView contains what it should.

Notice that the mock object approach is white box unit testing. It doesn't just test preconditions and postconditions; it also checks to see that the interactions between the object under test and other parts of the system are as expected.