How to createa Spring MVC web application using maven. We will start from scratch creating the project with maven, then we will add some depencencies and create the java code. Finally we will build the .war file.
Project initial setup
First of all we are going to start creating the project with maven.
mvn archetype:generate -DgroupId=com.testspringmvc.app -DartifactId=TestSpringMVC -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
This creates a project called TestSprintMVC with the following file structure:
TestSpringMVC/ ├── pom.xml └── src └── main ├── resources └── webapp ├── index.jsp └── WEB-INF └── web.xml 5 directories, 3 files
Now we are going to convert it to an eclipse project. This step is not necessary if you are not going to use eclipse as IDE:
cd TestSpringMVC/ mvn eclipse:eclipse -Dwtpversion=2.0
With the parameter -Dwtpversion=2.0 we change the pom.xml to generate a .war instead a .jar file as output.
Library configuration
All the library configuration is done in the POM.xml file. We are going to add the following libraries:
groupId | artifactId | version | scope |
---|---|---|---|
org.springframework | spring-core | 4.2.6.RELEASE | |
org.springframework | spring-web | 4.2.6.RELEASE | |
org.springframework | spring-webmvc | 4.2.6.RELEASE | |
org.springframework | spring-context | 4.2.6.RELEASE | |
org.slf4j | slf4j-nop | 1.7.21 | |
jstl | jstl | 1.2 | |
junit | junit | 4.12 | test |
org.springframework | spring-test | 4.2.6.RELEASE | test |
javax.servlet | javax.servlet-api | 3.1.0 | test |
You can download this pom.xml file fom: Download pom.xml
You can check the latest versions of this libraries in http://search.maven.org/
Coding with spring
We are going to create different packages and java classes. Inside src/main we are going to create the java folder. Then we are going to create two packages com.testspringmvc.app.data and com.testspringmvc.app.web.
TestSpringMVC/src/main/java/com/testspringmvc/app/ ├── data │ ├── ApplicationFactoryBean.java │ └── BasicModelBean.java └── web └── BaseController.java
Now in the com.testspringmvc.app.data package we are going to create our data model. It is a simple data model which only stores an integer value:
package com.testspringmvc.app.data; public class BasicModelBean { int data; public void init() { data = 1; } public void destroy() { data = 0; } public void setData(int data) { this.data = data; } public int getData() { return data; } }
We are going to register this class as a Bean in order to make it available in different parts of the application. We will do it with the help of the class ApplicationFactoryBean.
package com.testspringmvc.app.data; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationFactoryBean { @Bean(initMethod = "init", destroyMethod = "destroy") public BasicModelBean basicModelBean(){ return new BasicModelBean(); } }
Then we need the controller, the class which will receive the http requests and will use the data model accordingly. This class will be in the com.testspringmvc.app.web package:
package com.testspringmvc.app.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.testspringmvc.app.data.BasicModelBean; @Controller public class BaseController { static Logger log = Logger.getLogger(BaseController.class); @Autowired BasicModelBean basicModel; @RequestMapping(value = "/", method = RequestMethod.GET) public String indexPage(ModelMap model) { log.debug("Called indexPage"); int v = basicModel.getData(); model.addAttribute("data", ++v); basicModel.setData(v); return "index"; } }
What this controller does is to get the basicModel singlenton and increments in one the value of the data. Then it puts this new value in the web model attribute (ModelMap class) and save it in the singleton.
The return “index” of this method just tells the ViewResolver implementation which view return. In this case it will be the file index.jsp that we will create in the next section.
Spring MVC configuration
Now we have to update the file main/webapp/WEB-INF/web.xml:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Test Spring MVC</display-name> <servlet> <servlet-name>webmvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>webmvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/webmvc-dispatcher-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
We have updated the servlet version to 3.1. What we say here to the servlet container is that we have a servlet called webmvc-dispatcher that will be called when we request a URL under /, which in effect means all the URLs. This servlet will be implemented by the spring class DispatcherServlet. And the configuration parameters for the DispatcherServlet class are found in the file: /WEB-INF/webmvc-dispatcher-servlet.xml.
Now the content of the file /WEB-INF/webmvc-dispatcher-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.testspringmvc.app" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/views/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>
This file contains all the Spring required configuration. The context:component-scan tag is used for Spring to find all the Spring relevant annotation like @Controller, @Bean,…
The tag bean creates one bean based on the class InternalResourceViewResolver. This is the class incharged of providing the view redering. It will search for this pages with extension .jsp in the directory /WEB-INF/views/.
Now we need to create a file called index.jsp in the directory /WEB-INF/views/. This is because our BaseController.java will always return “index”.
The file index.jsp will be something like this:
<html> <body> <h2>Hello World!</h2> <h2>Data : ${data}</h2> </body> </html>
The value ${data} is the one that we defined in BaseController.indexPage(ModelMap) with the line:
model.addAttribute(&amp;amp;amp;quot;data&amp;amp;amp;quot;, ++v);
We can remove the original file src/main/webapp/index.jsp, we do not need it.
Testing
It is recommended to create unitary tests for all the classes we create. Here we are going to write some simple tests just to show how should be implemented.
All the tests will be under the folder src/test/java. There we will have two packages and two classes to test our implementation. The test directory tree will be something like this:
TestSpringMVC/src/test/ └── java └── com └── testspringmvc └── app ├── data │ └── BasicModelBeanTest.java └── web └── BaseControllerTest.java
First we are going to write the class BasicModelBeanTest:
package com.testspringmvc.app.data; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.testspringmvc.app.data.BasicModelBean; import com.testspringmvc.app.data.ApplicationFactoryBean; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = ApplicationFactoryBean.class, loader = AnnotationConfigContextLoader.class) public class BasicModelBeanTest { @Autowired BasicModelBean basicModel; @Test public void testSetDataValue() { Assert.assertEquals(basicModel.getData(), 1); basicModel.setData(2); Assert.assertEquals(basicModel.getData(), 2); } }
In this simple class we load the BasicModelBean class and check that the initial value of getData is 1 and that we can change that value. It is a simple test.
Now we want to test the controller and see if the view returned is correct. To do that we will implement the class BaseControllerTest.
package com.testspringmvc.app.web; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration // This provides a mock ServletContext @ContextConfiguration({ "file:src/main/webapp/WEB-INF/webmvc-dispatcher-servlet.xml" }) public class BaseControllerTest { private MockMvc mockMvc; @Autowired private WebApplicationContext context; @Before public void init() throws NoSuchFieldException { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); } @Test public void get_correct_data() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk()) .andExpect(view().name("index")) .andExpect(model().attribute("data", 2)); mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk()) .andExpect(model().attribute("data", 3)); } }
In BaseControllerTest we mock http calls and see how the class BaseController responds.
Se puede probar los tests en linea de comandos ejecutando:
TestSpringMVC$ mvn test
Logging
In the class BaseController we added some basic logging using log4j, here we are going to configure in the files called log4j.properties. We are going to create two, one for testing and the other for a real installation.
The one for testing will go in src/test/resources/log4j.properties:
log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c{1}:%L - %m%n
The one deployed will go in src/main/resources/log4j.properties:
log4j.rootLogger=INFO, file log4j.appender.file.layout.ConversionPattern=%d %-5p %c{1}:%L - %m%n log4j.appender.file.File=${catalina.base}/logs/testspringmvc.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.MaxBackupIndex=1 log4j.appender.file.MaxFileSize=100KB log4j.appender.file=org.apache.log4j.RollingFileAppender
Compiling and publishing
To compile you only need to execute:
mvn package
This will generate the file target/TestSpringMVC.war which can be deployed in a servlet container.
References:
More about Spring MVC: Tutorialspoint
More about Sprint MVC testing: MVC unit test
Source Code: https://github.com/calabozo/TestSpringMVC
Pingback: JGiven with Spring | Small tips that I don't want to forget