Kim Rudolph

Getting Spring MVC up and Running

The goal of this tutorial is to create a web application using the Spring Web MVC framework. Of course there is a “Hello world!” involved.

Have a look at the following resources if you want more detailed and feature rich examples:

Setup the Environment

The tools used in the example are maven for dependency resolution and the IDE Eclipse.

If you already installed those, skip to the next step.

Installing maven via package manager often results in an outdated version. The latest version of maven can be found at the official maven download site. Untar maven in a folder of your choice (e.g. ~/maven) and create a symlink to have maven ready at your command line.

$ ln -s  ~/maven/bin/mvn /usr/bin/mvn

A Eclipse installation guide can be found at the official eclipse wiki.

The maven repository storing the dependencies has to be added as a classpath variable to make eclipse aware of the maven artifacts.

Add the variable M2_REPO=~/.m2/repository at

Window > Preferences > Java > build Path > Classpath Variables

Defining Dependencies

Create a bare pom.xml file in your folder of choice. For eclipse users that should be ~/workspace/hello-world as default.

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>de.kimrudolph.tutorials</groupId>
  <artifactId>hello-world</artifactId>
  <name>A Hello World Example With Spring MVC</name>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <dependencies>
    <!-- Dependencies should go here -->
  </dependencies>
</project>

First goes Spring and the MVC artifact.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>3.2.2.RELEASE</version>
</dependency>

A servlet api is needed to define communication with the actual servlet container (e.g. Apache Tomcat). The scope is set to provided to prevent a deployment of that artifact. The servlet container already provides that.

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
</dependency>

To make the project and its dependencies visible to eclipse, just run

$ mvn eclipse:eclipse

Eclipse now recognizes the project under

File > Import > General > Existing Projects into Workspace

Hello World Controller

As mentioned, every tutorial should have at least one “Hello world!”. Let’s get that running.

First part is the Controller. That should go in a “controllers”-package. The package naming is not mandatory but should follow the MVC convention. Create that package with the IDE or with the command line. Be aware of the maven Standard Directory Layout.

$ mkdir -p src/main/java/de/kimrudolph/tutorials/controllers
$ mvn eclipse:eclipse

In the created package goes the controller.

package de.kimrudolph.tutorials.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {

    @RequestMapping(value = "/hello")
    public @ResponseBody String hello() {
        return "Hello world!";
    }

}

The string “Hello world!” should be returned if a requests hits the application at /hello.

Testing with a Unit Test

We don’t make mistakes. Sometimes that statement needs proof. At least a small proof.

Tests go in the same namespace but in the test scope. As above the IDE or the command line can be used.

$ mkdir -p src/test/java/de/kimrudolph/tutorials/controllers
$ mvn eclipse:eclipse

Additional artifacts are needed for test executions. The scope of both is set to test to prevent a deployment of those artifacts. They are only needed and referenced during the execution of tests.

<!-- Test framework and helper methods provided by Spring -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.2.2.RELEASE</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

Using the spring-test helper methods, the test is easy to type and read.

package de.kimrudolph.tutorials.controllers;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

import org.junit.Test;

public class HelloControllerTest {

    @Test
    public void testHelloRequest() throws Exception {
        standaloneSetup(new HelloController()).build().perform(get("/hello"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
            .andExpect(content().string("Hello world!"));
    }
}

Now that test passes and the “Hello world!” should also work if deployt to a tomcat.

Building a War File

When it comes to the deployment of java web applications a WAR file is needed. The maven-war-plugin builds such files and has to be added after the dependencies section to your pom.xml:

  ...
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

The next step would be a web.xml file to tell a servlet container (e.g. Apache Tomcat) what to do with the web application. But there is also a way to do so without touching any xml files. Spring provides a WebApplicationInitializer class that allows a pure Java representation of a web.xml.

Those configuration files should be in a different package. As described above, add the following files in the new namespace de.kimrudolph.tutorials.configuration.

package de.kimrudolph.tutorials.configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext sc) throws ServletException {

        // Initialize spring application context
        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();

        // Define where to scan for needed components
        root.scan("de.kimrudolph.tutorials");

        // Lifecycle management of the root application context
        sc.addListener(new ContextLoaderListener(root));

        // Handles requests into the application
        ServletRegistration.Dynamic appServlet = sc.addServlet("appServlet",
            new DispatcherServlet(new GenericWebApplicationContext()));
        appServlet.setLoadOnStartup(1);
        appServlet.addMapping("/");
    }
}

The referenced root.scan() part is the most important configuration part as it defines to scan the given namespace for any components or configurations. That scan will find our controller (@Controller) and the needed WebConfig.class (@Configuration) which enables the MVC behaviour.

package de.kimrudolph.tutorials.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
}

Testing with Apache Tomcat

To keep it simple, the setup and configuration of a standalone tomcat is not needed for a quick manual integration test. The maven-t7-plugin is a plugin that can spawn a local tomcat.

Add that plugin to the plugins section to your pom.xml:

<!-- Tomcat 7 Plugin for development environment -->
<plugin>
  <groupId>com.googlecode.t7mp</groupId>
  <artifactId>maven-t7-plugin</artifactId>
  <version>0.9.13</version>
  <configuration>
    <!-- configuration of the path on 
    localhost http://localhost:8080/<path> -->
    <contextPath>hello-world</contextPath>
  </configuration>
</plugin>

To create the war file and start the tomcat instance, run the command

$ mvn package t7:run

The Result

A blink of an eye later the result can be viewed at your personal localhost hello page.

“Hello world!”, there you are.

The full application can be found at the example GitHub repository.