Spring MVC framework is widely used for java web applications. Just like Struts Framework, Spring MVC is also based on Java EE Servlet and JSP technologies and implement Model–View–Controller design pattern.
We have earlier seen how Spring Dependency Injection works and in this tutorial we will learn how to create a simple web application using Spring MVC framework.
We can use Eclipse or IntelliJ IDE for the Spring projects development, but SpringSource provides Spring Tool Suite (STS) that is an IDE based on Eclipse and comes with in-built VMware vFabric tc Server that is built on top of Apache Tomcat container and optimized for Spring based applications.
I would use STS for this tutorial and other future tutorials because it makes a developers life easier by providing following features:
- Support for creating skeletal Spring applications (MVC, Rest, Batch etc), good for starting project from scratch.
- Provides useful features such as creating Spring Configuration files, parsing config files and classes to provide useful information about them.
- Automatic validation of Spring application
- Refactoring support to easily make project changes, the changes gets reflected in config files too.
- Code assist for not only classes but configuration files too, I like this feature a lot because most of the times we need to know what we can use and it’s details.
- Best support for Aspect Oriented Programming (AOP) through integration of AspectJ.
Looking at all the features STS provide, I was sold for this and decided to use it for Spring application and till now I am very happy with it.
Just Download the STS from STS Official Download Page and install it. I am using STS 3.4.0.RELEASE that is based on Eclipse 4.3.1 release.
If you don’t want to use STS and want to get it’s facilities in existing Eclipse, then you need to install it’s plugin from Eclipse Marketplace. Use below image as reference and make sure to chose the correct STS version for installation. Below plugin is good for Eclipse Kepler.
If you don’t want to use SpringSource server, you can deploy the application in any other Java EE container such as Tomcat, JBoss etc. For this tutorial, I will use the server that ships with STS but I have tested the application by exporting it as WAR file into separate tomcat server and it’s working fine.
Now that our server environment and IDE is ready, let’s proceed to create our first Spring MVC project. Below steps are valid for STS as well as Eclipse with STS plugins.
Creating Spring MVC Application in STS or Eclipse
Step 1: Create New Spring Project from the menu.
Step 2: In the new project window, give name as “SpringMVCExample” and chose template as “Spring MVC Project”. If you are using this template for the first time, STS will download it from SpringSource website. If you want, you can add the project to any working set.
Step 3: When the template is downloaded, in next screen you need to provide the top-level package name. This package will be used as the base-package for Spring components.
Step 4: Once the project is created by Spring MVC template, it will look like below image.
Don’t worry if you don’t see User.java class, login.jsp and user.jsp files, they have been added by me later on.
If your project is not compiled and you see some errors, run Maven/Update Project. Make sure to check the “Force update of Snapshots/Releases” options, refer below image.
Overall project looks just like any other maven based web application with some Spring configuration files. Now it’s time to analyze the different part of the projects and extend it a little.
Spring Dependencies
Our generated pom.xml file looks like below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
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
>com.journaldev</
groupId
>
<
artifactId
>SpringMVCExample</
artifactId
>
<
name
>SpringMVCExample</
name
>
<
packaging
>war</
packaging
>
<
version
>1.0.0-BUILD-SNAPSHOT</
version
>
<
properties
>
<
java-version
>1.6</
java-version
>
<
org.springframework-version
>4.0.0.RELEASE</
org.springframework-version
>
<
org.aspectj-version
>1.7.4</
org.aspectj-version
>
<
org.slf4j-version
>1.7.5</
org.slf4j-version
>
</
properties
>
<
dependencies
>
<!-- Spring -->
<
dependency
>
<
groupId
>org.springframework</
groupId
>
<
artifactId
>spring-context</
artifactId
>
<
version
>${org.springframework-version}</
version
>
<
exclusions
>
<!-- Exclude Commons Logging in favor of SLF4j -->
<
exclusion
>
<
groupId
>commons-logging</
groupId
>
<
artifactId
>commons-logging</
artifactId
>
</
exclusion
>
</
exclusions
>
</
dependency
>
<
dependency
>
<
groupId
>org.springframework</
groupId
>
<
artifactId
>spring-webmvc</
artifactId
>
<
version
>${org.springframework-version}</
version
>
</
dependency
>
<!-- AspectJ -->
<
dependency
>
<
groupId
>org.aspectj</
groupId
>
<
artifactId
>aspectjrt</
artifactId
>
<
version
>${org.aspectj-version}</
version
>
</
dependency
>
<!-- Logging -->
<
dependency
>
<
groupId
>org.slf4j</
groupId
>
<
artifactId
>slf4j-api</
artifactId
>
<
version
>${org.slf4j-version}</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.slf4j</
groupId
>
<
artifactId
>jcl-over-slf4j</
artifactId
>
<
version
>${org.slf4j-version}</
version
>
<
scope
>runtime</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>org.slf4j</
groupId
>
<
artifactId
>slf4j-log4j12</
artifactId
>
<
version
>${org.slf4j-version}</
version
>
<
scope
>runtime</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>log4j</
groupId
>
<
artifactId
>log4j</
artifactId
>
<
version
>1.2.15</
version
>
<
exclusions
>
<
exclusion
>
<
groupId
>javax.mail</
groupId
>
<
artifactId
>mail</
artifactId
>
</
exclusion
>
<
exclusion
>
<
groupId
>javax.jms</
groupId
>
<
artifactId
>jms</
artifactId
>
</
exclusion
>
<
exclusion
>
<
groupId
>com.sun.jdmk</
groupId
>
<
artifactId
>jmxtools</
artifactId
>
</
exclusion
>
<
exclusion
>
<
groupId
>com.sun.jmx</
groupId
>
<
artifactId
>jmxri</
artifactId
>
</
exclusion
>
</
exclusions
>
<
scope
>runtime</
scope
>
</
dependency
>
<!-- @Inject -->
<
dependency
>
<
groupId
>javax.inject</
groupId
>
<
artifactId
>javax.inject</
artifactId
>
<
version
>1</
version
>
</
dependency
>
<!-- Servlet -->
<
dependency
>
<
groupId
>javax.servlet</
groupId
>
<
artifactId
>servlet-api</
artifactId
>
<
version
>2.5</
version
>
<
scope
>provided</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet.jsp</
groupId
>
<
artifactId
>jsp-api</
artifactId
>
<
version
>2.1</
version
>
<
scope
>provided</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet</
groupId
>
<
artifactId
>jstl</
artifactId
>
<
version
>1.2</
version
>
</
dependency
>
<!-- Test -->
<
dependency
>
<
groupId
>junit</
groupId
>
<
artifactId
>junit</
artifactId
>
<
version
>4.7</
version
>
<
scope
>test</
scope
>
</
dependency
>
</
dependencies
>
<
build
>
<
plugins
>
<
plugin
>
<
artifactId
>maven-eclipse-plugin</
artifactId
>
<
version
>2.9</
version
>
<
configuration
>
<
additionalProjectnatures
>
<
projectnature
>org.springframework.ide.eclipse.core.springnature</
projectnature
>
</
additionalProjectnatures
>
<
additionalBuildcommands
>
<
buildcommand
>org.springframework.ide.eclipse.core.springbuilder</
buildcommand
>
</
additionalBuildcommands
>
<
downloadSources
>true</
downloadSources
>
<
downloadJavadocs
>true</
downloadJavadocs
>
</
configuration
>
</
plugin
>
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-compiler-plugin</
artifactId
>
<
version
>2.5.1</
version
>
<
configuration
>
<
source
>1.6</
source
>
<
target
>1.6</
target
>
<
compilerArgument
>-Xlint:all</
compilerArgument
>
<
showWarnings
>true</
showWarnings
>
<
showDeprecation
>true</
showDeprecation
>
</
configuration
>
</
plugin
>
<
plugin
>
<
groupId
>org.codehaus.mojo</
groupId
>
<
artifactId
>exec-maven-plugin</
artifactId
>
<
version
>1.2.1</
version
>
<
configuration
>
<
mainClass
>org.test.int1.Main</
mainClass
>
</
configuration
>
</
plugin
>
</
plugins
>
</
build
>
</
project
>
|
artifactId will be the servlet-context for the web application, so you can change it if you want something else.
There are few properties defined for Spring Framework, AspectJ and SLF4j versions, I found that they were not reflecting the latest versions, so I have changed them to the latest stable version as of today.
The project dependencies that I am interested in are;
- spring-context: Spring Core dependency. Notice the exclusion of commons logging in favor of SLF4J.
- spring-webmvc: Spring artifact for MVC support
- aspectjrt: AspectJ API reference
- SLF4J and Log4j: For logging purposes, Spring is very easy to configure for log4j or Java Logging APIbecause of SLF4J integration.
- javax.inject – JSR330 API for dependency injection
There are some other dependencies added, such as Servlet, JSP, JSTL and JUnit API but for starter application, we can overlook them.
Log4j Configuration
Generated log4j.xml file looks like below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!
DOCTYPE
log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<
log4j:configuration
xmlns:log4j
=
"http://jakarta.apache.org/log4j/"
>
<!-- Appenders -->
<
appender
name
=
"console"
class
=
"org.apache.log4j.ConsoleAppender"
>
<
param
name
=
"Target"
value
=
"System.out"
/>
<
layout
class
=
"org.apache.log4j.PatternLayout"
>
<
param
name
=
"ConversionPattern"
value
=
"%-5p: %c - %m%n"
/>
</
layout
>
</
appender
>
<!-- Application Loggers -->
<
logger
name
=
"com.journaldev.spring"
>
<
level
value
=
"info"
/>
</
logger
>
<!-- 3rdparty Loggers -->
<
logger
name
=
"org.springframework.core"
>
<
level
value
=
"info"
/>
</
logger
>
<
logger
name
=
"org.springframework.beans"
>
<
level
value
=
"info"
/>
</
logger
>
<
logger
name
=
"org.springframework.context"
>
<
level
value
=
"info"
/>
</
logger
>
<
logger
name
=
"org.springframework.web"
>
<
level
value
=
"info"
/>
</
logger
>
<!-- Root Logger -->
<
root
>
<
priority
value
=
"warn"
/>
<
appender-ref
ref
=
"console"
/>
</
root
>
</
log4j:configuration
>
|
Notice that it’s printing everything to console, we can easily add appenders to redirect logging to files.
Deployment Descriptor Configuration
Let’s see our web.xml and analyze it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
web-app
version
=
"2.5"
xmlns
=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
>
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<
context-param
>
<
param-name
>contextConfigLocation</
param-name
>
<
param-value
>/WEB-INF/spring/root-context.xml</
param-value
>
</
context-param
>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<
listener
>
<
listener-class
>org.springframework.web.context.ContextLoaderListener</
listener-class
>
</
listener
>
<!-- Processes application requests -->
<
servlet
>
<
servlet-name
>appServlet</
servlet-name
>
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet</
servlet-class
>
<
init-param
>
<
param-name
>contextConfigLocation</
param-name
>
<
param-value
>/WEB-INF/spring/appServlet/servlet-context.xml</
param-value
>
</
init-param
>
<
load-on-startup
>1</
load-on-startup
>
</
servlet
>
<
servlet-mapping
>
<
servlet-name
>appServlet</
servlet-name
>
<
url-pattern
>/</
url-pattern
>
</
servlet-mapping
>
</
web-app
>
|
ContextLoaderListener
ties the ApplicationContext
lifecycle to ServletContext
lifecycle and automate the creation of ApplicationContext
. ApplicationContext
is the place for Spring beans and we can provide it’s configuration through contextConfigLocation context parameter. root-context.xml file provides the configuration details for WebApplicationContext.
DispatcherServlet
is the controller class for Spring MVC application and all the client requests is getting handled by this servlet. The configuration are being loaded from servlet-context.xml file.
Spring Configuration Files
1
2
3
4
5
6
7
8
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
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"
>
<!-- Root Context: defines shared resources visible to all other web components -->
</
beans
>
|
We can define shared beans here, as of now there is nothing in it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
beans:beans
xmlns
=
"http://www.springframework.org/schema/mvc"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans
=
"http://www.springframework.org/schema/beans"
xmlns:context
=
"http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<
annotation-driven
/>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<
resources
mapping
=
"/resources/**"
location
=
"/resources/"
/>
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<
beans:bean
class
=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
>
<
beans:property
name
=
"prefix"
value
=
"/WEB-INF/views/"
/>
<
beans:property
name
=
"suffix"
value
=
".jsp"
/>
</
beans:bean
>
<
context:component-scan
base-package
=
"com.journaldev.spring"
/>
</
beans:beans
>
|
This is how the standard Spring configuration file looks like, just imagine writing all this on your own and you will start liking STS tool.
annotation-driven element is used to let Controller servlet know that annotations will be used for bean configurations.
resources element defines the location where we can put static files such as images, html pages etc that we don’t want to get through Spring framework.
InternalResourceViewResolver
is the view resolver, we can provide view pages location through prefix and suffix properties. So all our JSP pages should be in /WEB-INF/views/ directory.
context:component-scan element is used to provide the base-package location for scanning Controller classes. Remember the value of top-level package given at the time of project creation, it’s the same value getting used here.
Spring Controller Class
HelloController is created automatically with the home() method, although I have extended it little bit by adding loginPage() and login() methods.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package
com.journaldev.spring;
import
java.text.DateFormat;
import
java.util.Date;
import
java.util.Locale;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
org.springframework.stereotype.Controller;
import
org.springframework.ui.Model;
import
org.springframework.validation.annotation.Validated;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public
class
HomeController {
private
static
final
Logger logger = LoggerFactory.getLogger(HomeController.
class
);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping
(value =
"/"
, method = RequestMethod.GET)
public
String home(Locale locale, Model model) {
logger.info(
"Welcome home! The client locale is {}."
, locale);
Date date =
new
Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute(
"serverTime"
, formattedDate );
return
"home"
;
}
@RequestMapping
(value =
"/login"
, method = RequestMethod.GET)
public
String loginPage(Locale locale, Model model) {
return
"login"
;
}
@RequestMapping
(value =
"/home"
, method = RequestMethod.POST)
public
String login(
@Validated
User user, Model model) {
model.addAttribute(
"userName"
, user.getUserName());
return
"user"
;
}
}
|
@Controller annotation is used to indicate that it’s a web controller class.
@RequestMapping is used with classes and methods to redirect client request to specific handler method. Notice that handler methods are returning String, this should be the name of view page to be used as response.
As you can see that we are having three methods returning different strings, so we need to create JSP pages with same name.
Notice that login() method will get called with HTTP method as POST, so we are expecting some form data here. So we have User model class and it’s marked for validation using @Validated annotation.
Every method contains Model as argument and we can set attributes to be later used in the JSP response pages.
Model Classes
Model classes are used to hold form variables, our User model bean looks like below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package
com.journaldev.spring;
public
class
User {
private
String userName;
public
String getUserName() {
return
userName;
}
public
void
setUserName(String userName) {
this
.userName = userName;
}
}
|
A simple java bean with variable name and it’s getter and setter methods.
View Pages
We have three JSP pages like below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<
html
>
<
head
>
<
title
>Home</
title
>
</
head
>
<
body
>
<
h1
>
Hello world!
</
h1
>
<
P
> The time on the server is ${serverTime}. </
P
>
</
body
>
</
html
>
|
Notice the use of JSP Expression Language to get the attribute values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!
DOCTYPE
html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<
html
>
<
head
>
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=UTF-8"
>
<
title
>Login Page</
title
>
</
head
>
<
body
>
<
form
action
=
"home"
method
=
"post"
>
<
input
type
=
"text"
name
=
"userName"
><
br
>
<
input
type
=
"submit"
value
=
"Login"
>
</
form
>
</
body
>
</
html
>
|
A simple JSP page for user to provide the userName as input. Notice that form variable name is same as User class variable name. Also form action is “home” and method is “post”. It’s clear that HomeController login() method will handle this request.
1
2
3
4
5
6
7
8
9
10
11
12
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!
DOCTYPE
html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<
html
>
<
head
>
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=UTF-8"
>
<
title
>User Home Page</
title
>
</
head
>
<
body
>
<
h3
>Hi ${userName}</
h3
>
</
body
>
</
html
>
|
Simple home page for user where username is displayed, notice that we are setting this attribute in the login method.
Running Spring MVC Application
Our application is ready for execution, just run it on the VMware tc Server or your choice of any other servlet container, you will get below pages as response.
That’s it, you can see that how easy it is to create Spring MVC application using STS plugins. The code size is very less and most of the configuration is handled by Spring so that we can focus on business logic. Download project from below link and play around with it.