Filed under: J2EE, Java, Maven, — Tags: MVC, Spring, TDD, jsp, xml — Thomas Sundberg — 2009-04-06
I want to to create a very simple web application using Spring. I want to create it using Maven as build tool.
Start with the usual Maven directory setup:
example |-- pom.xml `-- src |-- main | |-- java | | `-- se | | `-- somath | | `-- experimental | | `-- controller | | `-- SimpleFormController.java | `-- webapp | |-- index.html | `-- WEB-INF | |-- jsp | | `-- landingPage.jsp | |-- SimpleSample-servlet.xml | `-- web.xml `-- test `-- java `-- se `-- somath `-- experimental `-- controller `-- SimpleFormControllerTest.java
Let's start with the content of the Maven pom:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project> <modelVersion>4.0.0</modelVersion> <groupId>com.somath.experimental</groupId> <version>1.0-SNAPSHOT</version> <artifactId>MavenSpringWeb</artifactId> <name>Maven Spring Web</name> <packaging>war</packaging> <description>Demonstration on how to build a simple web app using Maven and Spring</description> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>2.5.6</version> </dependency> </dependencies> </project>
This is a fairly standard pom. The dependency list isn't anything exotic. We want JUnit, we always do. And we need spring-webmvc since this is the framework we want to use.
The next step is to develop a simple controller, connect it and create a form to call it from and a landing page to display the result.
Let's start with a test for the controller:
src/test/java/se/somath/experimental/controller/SimpleFormControllerTest.java
package se.somath.experimental.controller; import org.junit.Test; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; public class SimpleFormControllerTest { @Test public void AsserProperLandingPage() { SimpleFormController simpleFormController = new SimpleFormController(); Model model = new ExtendedModelMap(); String expectedLandingPageName = "landingPage"; String expectedName = "My Name"; String expectedHiddenParameter = "My Hidden Attribute"; String actualLandingPageName = simpleFormController.mirrorGet(expectedName, expectedHiddenParameter, model); String actualName = (String) model.asMap().get("name"); String actualHiddenParameter = (String) model.asMap().get("hiddenParameter"); assertThat(actualLandingPageName, is(expectedLandingPageName)); assertThat(actualName, is(expectedName)); assertThat(actualHiddenParameter, is(expectedHiddenParameter)); } }
This is a simple JUnit test. The only exotic here might be the annotation @Test
that is used to
indicate that this is a JUnit test that should be verified every time we build the project with Maven. We verify
that a model contains some expected values and that the return value from the controller is the name of a landing
page. In this case it will be a jsp.
The next step would be to compile the project with
mvn install
This will of course fail, we haven't implemented the controller. Let's do that in:
src/main/java/se/somath/experimental/controller/SimpleFormController.java
package se.somath.experimental.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/form") public class SimpleFormController { @RequestMapping(method = RequestMethod.GET) public String mirrorGet(String name, String hiddenParameter, Model model) { model.addAttribute("name", name); model.addAttribute("hiddenParameter", hiddenParameter); return "landingPage"; } @RequestMapping(method = RequestMethod.POST) public String mirrorPost(String name, String hiddenParameter, Model model) { return mirrorGet(name, hiddenParameter, model); } }
The things to notice here are the annotations
The @Controller
annotation will indicate to Spring that this is a controller, the C in a Model View
Controller, MVC, framework.
The @RequestMapping("/form")
indicates that this controller should be called when somebody sends a
request to web application with the name indicated in the pom earlier, MavenSpringWeb-1.0-SNAPSHOT/aServletName/form
.
AservletName isn't defined yet, it will be defined soon in web.xml
.
The @RequestMapping(method = RequestMethod.GET)
and @RequestMapping(method =
RequestMethod.POST)
indicates which method that should be called when a get or a post is received. To keep
things simple, I just forward all calls to post to get and forget about them.
The get method doesn't really do anything interesting. This is where you probably would place a call to your model, perform some computing and then return the control to the method and view some kind of answer to the user. All I do is mirror two parameters and instruct that the view to show the user should be something called 'landingPage'.
Ok, we have a test and we have some production code. Now we want to make sure that this can be used as a part of a
web application. Lets start with defining a web.xml
so we get a servlet that will help us to forward
any calls to our controller. Let's define it in
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app version="2.4" 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Simple Sample</display-name> <servlet> <servlet-name>SimpleSample</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SimpleSample</servlet-name> <url-pattern>/SimpleSample/*</url-pattern> </servlet-mapping> </web-app>
All we do here is stating that we want a servlet bundled with Spring to be used as a dispatcher, org.springframework.web.servlet.DispatcherServlet
.
Then we map this servlet so it catches any call to /SimpleSample with the row:
<url-pattern>/SimpleSample/*</url-pattern>
Spring requires that we define a file called ServletName-servlet.xml
which will be SimpleSample-servlet.xml
in our case. Define it in
src/main/webapp/WEB-INF/SimpleSample-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: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" default-autowire="byName"> <context:component-scan base-package="com.agical.experimental"/> <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
The things to note here are:
We will use jsp as view technology. This could be a number of other technologies, but jsp will be good enough in this case.
The prefix defines a location where we should locate our jsp pages and the suffix indicates that they will be called .jsp.
So now we have a test, a controller, we define a servlet that will dispatch calls to the controller and we have defined a way to display the result. Let's create a jsp page that will be used to view the result. Create it in:
src/main/webapp/WEB-INF/jsp/landingPage.jsp
<%--@elvariable id="name" type="String"--%> <%--@elvariable id="hiddenParameter" type="String"--%> <%@ page contentType="text/html;charset=ISO-8859-1" language="java" %> Name: ${name} <br> HiddenParameter: ${hiddenParameter}
The two first rows will define variables for Intellij Idea, it is only there as a convenience for Idea users. The two rows that may be a bit interesting are:
Name: ${name}
HiddenParameter: ${hiddenParameter}
But the only thing we do here is to expand two variables so we can see the values we sent to the controller.
Finally we want to call the web application. This could be done from a browser. But it will be a bit easier if we define a web page with a form. This could be the subject for some automated testing using a tool like Selenium, but that is a bit out of scope today. Let's create a simple form in:
src/main/webapp/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <form action="./SimpleSample/form" method="GET"> Name: <input type="text" name="name"/> <br> <input type="hidden" name="hiddenParameter" value="My hidden value"/> <input type="submit" value="Submit"/> </form> </body> </html>
If we build and deploy the web application, we should be able to see that what ever name we entered in the form will show up on the landing page.
Build the project with Maven:
mvn clean install
Deploy the result
targetMavenSpringWeb-1.0-SNAPSHOT.war
on a servlet container and try to access is with a url similar to http://localhost:8080/MavenSpringWeb-1.0-SNAPSHOT/
And the result should look something like this: