In this part, I’ll show how to get started with STS (SpringSource Tool Suite) and Spring MVC. We’ll only use the default project that STS generates, but it’s a good start to any development effort with Spring MVC you might get into.
STS is the easiest IDE to use when developing Spring, mainly because it bundles together almost everything you need to get started, and knows where to get the rest.
If you don’t have STS installed, just download it from SpringSource’s website here and install it on your machine.
When you get STS up and running for the first time, it will ask you (as all Eclipse distributions do) which workspace to use, and then open the welcome screen. You don’t need the welcome screen. What you want is the dashboard.
From the dashboard screen choose Spring Template Project. This will open the following view:
Choose a Spring MVC project.
The first time you do it, STS might ask you to download some extra elements from the Internet. Let it download – it will not bother you again.
You’ll need to give your project a name and a top-level package:
Click on finish – and STS will create the project for you. You can run the project by right-clicking it and selecting ‘run on server’. This will open the following dialog:
The Fabric tc server was created for you with STS. Later you might want to change it to Tomcat or another server – but for now this is alright.
Choose the server and click on next – you’ll see the following:
This dialogue lets you choose which projects you want to deploy on the server. As GoodProject is the only project in the workspace – it will be the only one here, and automatically selected for deployment because you selected it to run on the server.
Click on “Finish” to launch the server runtime. STS will ask you if you want to use Spring Insight. You can activate it if you like, but most of the times I tried that – you get an error and the server fails to start. To fix this error you’ll need to delete the server in STS and create a new one – so you might as well not activate it.
If everything went smoothly, STS will open the home page for GoodProject, Which displays anhello world message with the current server time.
Edit:See Asif’s comment at the bottom if you got “HTTP Status 404 – The requested resource () is not available”. You can either clean the project, or change and save the home.jsp file (e.g. add a space somewhere and save) – this will trigger a rebuild, and the error should go away.
Ok, so at this point – you know that you have everything working. Granted – you didn’t really have to do much – all you did is create a default Spring MVC project in STS and run it.
The New Spring MVC project template created all the things you need to have a Spring MVC project running. This comprises a few files, and the rest of this tutorial is dedicated to reviewing them.
home.jsp
The first file I want to discuss is the home.jsp file. This file is the default view generated by the new Spring MVC project wizard. Currently, this is the only thing a user will see when they access your application on the server, so it is a good place to start.
home.jsp is a JSP file. In Spring MVC, you don’t have to use JSP as your view technology. Most projects I worked on actually use JSF, but it doesn’t matter much as far as the Spring framework is concerned.
This is what home.jsp contains:
On the left side of the screenshot you can see the code in the JSP file. On the right side – you see how this code is displayed in a browser.
The first two lines of the generated JSP page are general JSP declarations: setting up the standard tag library, and declaring that the page does not use the HTTP session. Those lines are not important for us right now.
In fact, the entire JSP page is just some plain HTML which should be quite straight-forward to read. The only piece that’s interesting is the part that reads ${serverTime}.
As you can see on the right, Spring replaces the ${serverTime} phrase with a date and time string. Don’t go looking for it through the JSTL reference though – it’s not a tag. We’ll touch more on this when we talk about the controller, but for the completeness of the home.jsp discussion – it’s enough to say that serverTime is a property set by the controller and used by the view. (Just like CakePHP and other MVC frameworks, Spring MVC lets your controllers pass values to use in your views).
That’s it really for home.jsp. The only other thing worth noting at this point is that it doesn’t have a link to a css file. That’s alright.
Ok, so now you know what the home.jsp file contains. It is a simple view, that uses a value given to it by the controller. It is now time to look at this controller and see what it does.
HomeController.java
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
|
package
com.duckranger.goodproject;
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.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.toString());
Date date =
new
Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute(
"serverTime"
, formattedDate );
return
"home"
;
}
}
|
The first thing to note about the Controller is that it is really just a simple class. In Spring MVC 3 – you don’t need to make the controller extend or implement anything. However, you have to annotate it with the @Controller annotation. (line 17)
This annotation lets Spring know that this class is actually a controller, and set it up correctly at deployment time.
Line 20 shows you how to acquire a handle to a logger. This is a normal slf4j logger that Spring provides you with. Don’t fret about it.
Line 26 is where the interesting part begins. The generated controller has only one method – the home() method. There’s no mandated relationship between the name of the controller (HomeController) and the name of the method, or its return value (which we’ll get to soon) – Inside this class – all of them are more or less arbitrary. The return value could be any other String (however – you need to make sure it can be matched to a view. We’ll see in a bit how the “home” return value is mapped to the home.jsp).
The method home() is annotated with a @RequestMapping annotation. This tells Spring that this method handles requests coming in from the user. The @RequestMapping tells Spring that this method should be used to serve requests coming in to the ‘/’ context (or the home/root context for the application), but – it is restricted to HTTP GET requests only.
This restriction isn’t very important for the default home.jsp page, as there’s no way to change the server’s data status with this page. However, sometimes you’ll want to restrict specific methods to serve only POST requests (so, for example, to make sure the user fills out a form rather than craft a GET request to your website) – and that’s where this option comes in handy.
The home method generated by the template takes two arguments: A locale and a Model. Note that this is not set in stone. Just as the name of the method is flexible – so are the arguments and even the access modifier (it’s true. Try removing the Locale argument or changing the method to be private – your application will still deploy).
To understand why this works – we need to delve deep into the way Spring-MVC does things, and it’s probably a good subject for a different blog post altogether. For now it is enough to understand that because we don’t actually call this method directly (the container calls it for us when the relevant request comes in) – then it is the container’s responsibility to match the call it makes to the method’s signature. If we define a locale – the container (using the DispatcherServlet and its adapters) will find the locale sent by the HTTP headers, and pass the home() method a matching Locale object. If we were to change the method signature to only take a model – well, the Spring container will not do that (you’ll still be able to find the Locale, by the way, but if you need it inside the method – might as well add it to the signature).
So, that’s settled then, for now. Let’s agree that there are a multitude of possible method signatures that are able to serve as controller methods.
The first thing the home() method does (line 27) is log the Locale it discovered. You should be able to see this logged in the server’s console window inside STS:
As you can see, the last line in the server log is the INFO log message, detailing the locale I used to access the home() method.
Lines 29-32 create a formatted date String. This String is actually what the home.jsp page displays using the ${serverTime} directive we saw before. How does it do that?
Well – line 34 actually takes care of that:
1
|
model.addAttribute(
"serverTime"
, formattedDate );
|
Spring-MCV is (surprise?) an MVC framework. We’ve already seen the view (home.jsp) and the controller (HomeController.java) – surely you’d expect Spring-MVC to provide a model as well.
Remember home()’s method signature – it had a Model object inside it. This Model is supplied by the container to the controller method. The method uses this model to communicate with the view.
Model is a Spring interface:
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
|
* Copyright
2002
-
2009
the original author or authors.
package
org.springframework.ui;
import
java.util.Collection;
import
java.util.Map;
/**
* Java-5-specific interface that defines a holder for model attributes.
* Primarily designed for adding attributes to the model.
* Allows for accessing the overall model as a <code>java.util.Map</code>.
*
* @author Juergen Hoeller
* @since 2.5.1
*/
public
interface
Model {
/**
* Add the supplied attribute under the supplied name.
* @param attributeName the name of the model attribute (never <code>null</code>)
* @param attributeValue the model attribute value (can be <code>null</code>)
*/
Model addAttribute(String attributeName, Object attributeValue);
/**
* Add the supplied attribute to this <code>Map</code> using a
* {@link org.springframework.core.Conventions#getVariableName generated name}.
* <p><emphasis>Note: Empty {@link java.util.Collection Collections} are not added to
* the model when using this method because we cannot correctly determine
* the true convention name. View code should check for <code>null</code> rather
* than for empty collections as is already done by JSTL tags.</emphasis>
* @param attributeValue the model attribute value (never <code>null</code>)
*/
Model addAttribute(Object attributeValue);
/**
* Copy all attributes in the supplied <code>Collection</code> into this
* <code>Map</code>, using attribute name generation for each element.
* @see #addAttribute(Object)
*/
Model addAllAttributes(Collection<?> attributeValues);
/**
* Copy all attributes in the supplied <code>Map</code> into this <code>Map</code>.
* @see #addAttribute(String, Object)
*/
Model addAllAttributes(Map<String, ?> attributes);
/**
* Copy all attributes in the supplied <code>Map</code> into this <code>Map</code>,
* with existing objects of the same name taking precedence (i.e. not getting
* replaced).
*/
Model mergeAttributes(Map<String, ?> attributes);
/**
* Does this model contain an attribute of the given name?
* @param attributeName the name of the model attribute (never <code>null</code>)
* @return whether this model contains a corresponding attribute
*/
boolean
containsAttribute(String attributeName);
/**
* Return the current set of model attributes as a Map.
*/
Map<String, Object> asMap();
}
|
As you can see, the Model interface as far as the code is concerned, is a Map of attributes. The home() method uses the Model in line 34 and adds an attribute to it, using “serverName” as the key, and the formatted date string as the value.
This model is available to the home.jsp view we discussed earlier. In the view, you can use the ${…} notation to ask Spring to replace the attribute’s key with its value.
The JSP is processed on the server side before being sent to the client’s browser. During this processing, the server finds the ${serverTime} directive, and replaces it with the Model value corresponding to the serverTime key.
You can verify that this is what actually happens by trying to change the key to a non-existent one. You don’t have to redeploy the application if you only change a JSP. Just refresh the page, and see for yourself:
If you change ${serverTime} to something else – e.g. ${noTime} – the JSP processor will not find the key in the Model, and will silently fail – the time string is still there on the Model, but it isn’t found, and isn’t displayed on the page. Now change the bad value back to ${serverTime} and refresh the page – the time String should re-appear.
The last line in the home() method (line 36) returns a String to the caller: “home”. This String is used by the
container to decide which view it is supposed to render for the user, using the model.
So, to sum up so far: you define a controller using the @Controller annotation, and inside this controller you define a method that handles a specific request (an HTTP GET of the ‘/’ context).
Because of these annotations, when a request comes in to this URL, the Spring container knows that it needs to serve it using this controller and the specific method inside it.
The method does some work, and then returns a String. This String (“home”) lets the container know where to go to after the controller finished processing the request. In the case of the generated code, the container will use this String to decide which view (JSP file) to render back to the client.
Let’s see how this works then – the servlet-context.xml file.
servlet-context.xml
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-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.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.duckranger.goodproject"
/>
</
beans:beans
>
|
servlet-context.xml is used by Spring-MVC to configure navigation, beans and view resolvers. The file name does not have to be servlet-context, but this is the default configuration and it’s good enough for this tutorial.
The full picture is this: Spring-MVC uses a special servlet called DispatcherServlet to control and serve all requests to your application. To integrate with the servlet container, this DispatcherServlet is configured in the normal web.xml file like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<
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
>
|
As you can see, the servlet is configured very much like any other servlet you’d configure in a normal web application. It is given a default name: appServlet, and it is configured to serve all requests coming to the ‘/’ (root) context.
The org.springframework.web.servlet.DispatcherServlet is configured with an init-param that points to the location of its configuration file. This file Is the servlet-context.xml.
When the application is deployed, your servlet container scans web.xml for (among other things) servlets to load. It finds the DispatcherServlet with the highest priority for load-on-startup (load-on-startup tells the server container in what order it should initialize the servlets it encounters. The lower the number – the earlier the initialization will happen. The lowest meaningful value here is 1).
When the DispatcherServlet is initialized by the servlet container, it looks at its init-param, and finds its contextConfigLocation, which points to the servlet-context.xml file. As I mentioned before – you may change the name (or the location) of this file. As long as you configure it correctly in your web.xml – you should be fine.
Ok, back to servlet-context.xml:
There are a few important things to note in this configuration file:
- The <annotation-driven /> element.
This element tells the DispatcherServlet that it shouldn’t expect controllers to be configured in XML, rather – it will get its components via another method. This method can be found further down the file: <context:component-scan base-package=”com.duckranger.goodproject” /> - <context:component-scan base-package=”com.duckranger.goodproject” /> tells the Spring container where it can find components. In this case, the directive tells Spring to look for components in the com.duckranger.goodproject package. Spring will scan this package and will register as a component any class it finds annotated with the relevant annotations (e.g. @Controller).
So, together with <annotation-driven/> – these two directives ensure that Spring finds the HomeController class and registers it as a controller in the Spring-MVC application. - The final element worth noticing in this file is the definition of a ViewResolver:
12345<!-- 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
>
A ViewResolver is another Spring interface, which is responsible for directing a client to the right view in the application.
A bit about view resolvers
Generally, a controller method should not return just a String. It should actually return a ModelAndView object, which contains both the model we discussed earlier, and the view to resolve (or rather – a way to find the view).
However, as you can see, in our generated Spring-MVC project, the controller method returns only a String, which contains the view name. This is alright, as long as we tell the container how it should resolve the view and serve the correct page to the client.
The InternalResourceViewResolver does exactly that. It is configured as a bean, and since it is the only view resolver configured, the Spring container knows that this is the one it should use. In essence, the InternalResourceViewResolver receives the result of the controller method (the String returned) – and is then in charge of mapping this result to an actual view.
In the definition above, we define two properties on this resolver bean: a prefix and a suffix. The InternalResourceViewResolver concatenates the prefix, controller-return-value and suffix to come up with the actual view name to display. In our case, this will be /WEB-INF/views/home.jsp.
Using this information, it finds the right JSP page to load, processes it using the model object (same Model object in the home() method) – and sends a response back to the client.
You can verify that this is the way the InternalResourceViewResolver works by changing a few values and watching the result. Try the following:
Change the return value of the home() method to be “duckranger”, save the HomeController class, wait a few seconds until you see the server has redeployed (It will echo INFO: Reloading Context with name [/GoodProject] is completed in the console) – and refresh the STS browser page – it should show the error:
Which makes sense, as there’s no duckranger.jsp page.
You can also try and change the prefix or suffix in servlet-context.xml (you may have to manually redeploy for this change to take effect, though). If you change the suffix from value=”.jsp” to value=”.duck” and try to access your application, you’ll find that the server complains.
In the server log (STS console) you’ll see the following:
WARN : org.springframework.web.servlet.PageNotFound – No mapping found for HTTP request with URI [/GoodProject/WEB-INF/views/home.duck] in DispatcherServlet with name ‘appServlet’
So, now that we’re satisfied that the view resolver indeed resolves views – there are only a couple of files left in the project – the log4j.xml and pom.xml configuration files. These are your maven build and log4j configuration files, which are not strictly a part of the Spring-MVC and are beyond the scope of this tutorial. We don’t care much for them right now.
Part I Summary
In this part you just went through the real basics of Spring MVC 3.0. Set up the STS development environment, created the template Spring MVC project, and investigated the files that got created.
Generally speaking, the control structure of a Spring-MVC application is quite simple:
- You start by hooking up Spring’s DispatcherServlet as the main entry point to your servlet container. This is done in web.xml, like you define any other servlet, with the exception of specifying a configuration file in DispatcherServlet’s init-param.
- The DispatcherServlet’s configuration file tells the Spring container where it can find its controllers, and configures a view resolver to handle the values returned from the controller methods, and resolve them into the right views to display.
- Once you have this sorted, all that’s left to get a simple application up and running is to create a simple controller and a simple view.
- The DispatcherServlet finds the controller by its @Controller annotation, and by it being a part of the relevant package configured with the component-scan element in its configuration file.
- Using the @RequestMapping annotation, the DispatcherServlet knows which controller method it needs to call when a request comes in to the ‘/’ context.
The DispatcherServlet calls this method – (home() in the code above) – and gives it a Model to use for passing data to the view. - The home() method then returns a String – “home” – that’s used by the view resolver to resolve into a JSP page to render and return to the client.