Spring MVC 3.x with STS Tutorial – Part II

Studio_71a

Ok, I finally got around to do this. Sorry for the wait, but I was pre-occupied with, well – life, work and everything in between.

In the first part of this tutorial, I walked through setting STS up, and dealing with the default project you create with the Spring-MVC template. In this part – I want to try and go a bit deeper – so I’ll touch a bit on mapping requests and on displaying data to the user.

To make this tutorial a bit closer to real life, how about we try and manage New Zealand’s DOC (Department of Conservation) – well, not all of it, but let’s try for the walks – or tramps as they are called in New Zealand.

The tramps of New Zealand

For starters – how about we create a controller that handles the tramps? Open your project from part I, and create the class com.duckranger.goodproject.controllers.TrampsController :
(Note that all the code can be found on github)

package com.duckranger.goodproject.controllers;

import java.util.HashMap;
import java.util.Map;
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;

@Controller
public class TrampsController {

  private static final Logger logger = LoggerFactory.getLogger(TrampsController.class);

  private Map tramps;

  public TrampsController() {
tramps = new HashMap();
    populate(tramps);
  }

  @RequestMapping(value = "/tramps", method = RequestMethod.GET)
  public String list(Model model) {
    logger.info("Listing Walks");
    model.addAttribute("tramps",tramps);
    return "tramps/list";
  }

private void populate(Map tramps) {
    tramps.put(1L, "Lake Waikaremoana Great Walk");
    tramps.put(2L, "Tongariro Northern Circuit");
    tramps.put(3L, "Whanganui Journey");
    tramps.put(4L, "Abel Tasman Coast Track");
    tramps.put(5L, "Heaphy Track");
    tramps.put(6L, "Kepler Track");
    tramps.put(7L, "Milford Track");
    tramps.put(8L, "Routeburn Track");
    tramps.put(9L, "Rakiura Track");
  }
}

Ok, so what’s happening here?

Nothing very special really. This is a very basic controller, and we’ll extend and change it as the tutorial progresses.

  1. Lines 24 – 29 define a simple method: list. This method will be used to provide a list of all the tramps managed by the application.
    • The @RequestMapping in line 24 tells the DispatcherServlet to use this method when a GET request arrives for the path /GoodProject/tramps.
    • In line 27, the method adds the tramps object (which is a Map containing tramps) to the Model. This means that the map will be available in the view under the name “tramps”.
    • Line 28 returns the name of the view to render. I touched on the flexibility of method signatures on controllers in this post, and the code takes advantage of the magic that Spring MVC provides us for object creation. Anyway – the string this method returns is “tramps/list”. If you recall from part 1, we are using the InternalResourceViewResolver to resolve view names returned from controller methods. The resolver’s configuration (in servlet-context.xml) tells Spring to look for jsp files inside the /WEB-INF/views directory. So “tramps/list” resolves to WEB-INF/views/tramps/list.jsp – which is the view name to be rendered. (See the servlet-context.xml file below, lines 16-20)
  2. Because I want to keep things fairly simple for now, I’ll let the controller handle its own data repository rather than delegate it onwards (don’t worry, I’ll get to this very soon). Almost all of the code in TrampsController outside lines 24-29 deals with creating and populating a list of tramps.

Side note: STS visualises Spring Components

Package structure with s marks

As you create and save the class – a funny thing should happen if you are using STS. In your package explorer view, the class should have a little decoration attached to it – a tiny blue ‘s’. This is excellent, and signifies that Spring picks up your controller and understands that this is a Spring component. But how does STS know that? Well – it knows that because you annotated the class with the @Controller annotation.
The @Controller annotation is a marker annotation, which is itself annotated as a @Component.

@Component is a marker-annotation for Spring. Remember that in Spring you can configure beans either manually inside XML files, or use auto-detection and let Spring find the beans itself. To use auto-detection you need to do the following inside src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml:


	<!-- Enables the Spring MVC @Controller programming model -->
	<mvc:annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<mvc:resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<context:component-scan base-package="com.duckranger.goodproject" />
  1. Line 2: Tell Spring that you are using an annotation-driven model for configuring your controllers and beans container
  2. line 13: provide Spring with pointers to the package(s) you want it to scan for components. (By default, Spring will also scan sub-packages)
  3. Annotate classes that you want to use as components with either a @Component annotation or one of its specializations – e.g. @Controller or @Service.

Once you do all that, Spring will happily scan the relevant packages for you and register the annotated classes as beans. This can save a lot of XML wiring, and will also result in immediate feedback inside STS (the small blue ‘s’).

Listing tramps: The view

One little thing before I discuss the view. The default styles are ugly as hell, so I have added a css file to the project. There are several methods of adding css files to a Spring MVC application, and there are a few discussions on the web regarding where it should go. Since I am actually using Spring 3.1 (well, you really need 3.0.4 or higher for this trick) – I am using the simplest way to serve a CSS file in Spring-MVC, and I am not going to worry about “correctness” of the method. (Which is political-speak for I’m lazy and can’t be bothered with using a bit more sophisticated approach for a simple tutorial).

To achieve this, I use the resources mapping element (line 14 in servlet-context.xml above) – that maps any request that starts with /resources to src/main/webapp/resources in your package explorer. (this was not available before Spring 3.0.4.RELEASE).

Why do you need it? Well, because you don’t really want your DispatcherServlet to serve static resources. There’s no need for it, and anyway, the DispatcherServlet only works on resources found under the WEB-INF directory. These resources are not accessible over the web (for clients) – but css and image files should actually be accessible directly.
Additionally, if you later want to split out your application to serve static resources from the web server (e.g. to reduce load on the application server, or to be able to serve them unencrypted) – then this works out better.

Long story short – I created the following file: src/main/webapp/resources/css/tramps.css. If you don’t care – you don’t have to create it – the application will still run alright.

* { font-family:Arial}
table, th, td {border:1px solid green;padding:15px}
table {border-collapse:collapse;width:100%}
th {height:50px;text-align:center;background-color:green;color:white;}
td {text-align:left;height:50px;vertical-align:bottom;}
.container {width:80%;margin:30px auto}

That’s it. (Naturally, you can do whatever you want here. No one is judging).

Right, let’s talk about the view file

Side-notes completed, and now we’re back on track. The controller is ready, and it’s on to the very simple view:
WEB-INF/views/tramps/list.jsp:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
    <link href="<c:url value="/resources/css/tramps.css" />" rel="stylesheet" type="text/css" media="screen" />
    <title>Listing Tramps</title>
</head>
<body>
<div class="container">
<h1>Tramps of New Zealand</h1>
<c:if test="${not empty tramps}">
 
    <table class="normal-table">
        <thead>
            <tr>
                <th>Key</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
        <c:forEach var="tramp" items="${tramps}">
            <tr>
                <td>${tramp.key}</td>
                <td>${tramp.value}</td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
</c:if>
 
</div>
</body>
</html>
  1. Line 5: Links to the CSS file. Note that I use the c:url tag. This is the way you should do it, so that your views always find the correct /resources/ folder. Otherwise – you may need to experiment a lot with directory traversing (e.g. href=”../../resources/css/tramps.css”)
  2. Line 11: The view tests whether it was given tramps to display. It is usually safer to do it this way rather than risk an exception in rendering, which your user is going to see.
    You can be nicer to your user and display a nice message when there are no tramps to display, but funnily enough there is no c:else tag in JSTL, and you’ll need to use c:choose or two sets of c:if to achieve this. (JSTL is a tag library for JSPs).
  3. Lines 21-26 deal with displaying the tramps information. In line 21 we define a loop, using c:forEach. The items attribute tells Spring to use the tramps object mapped on the model returned from the controller. The loop iterates over tramps, and each iteration receives a handle to a Tramp in the tramp variable.
  4. In lines 23 and 24, we display the key and value of the current tramp. Arguably, there’s not much value in providing the key (pun unintentional, but I am quite proud of myself anyway) – but this is what we can get with the current settings. Later on in the tutorial, we’ll create a class for a Tramp. Then we’ll be able to provide more information.

The actual object we get with each iteration is a java.util.HashMap$Entry (you can verify it by having the view display ${tramp.getClass()} somewhere). This is why we need to use the key and value properties and don’t have more descriptive names. We’ll deal with this in a bit. Note: you may need to restart your server to pick up the new controller.

The view in a browser

Hey, let’s get some service here!

The last thing I want to do in this part is introduce a service into the application. It is a good idea to layer your application, and use some separation of concerns. Your controllers should really just deal with the requests coming in, and not with logic around creating or retrieving data. It is healthier for them.
For small applications, it doesn’t matter too much, but small applications tend to get larger, and then you want to add transaction management, or batch operations, or a web-service API – and all of a sudden you have the entire world and its sister accessing your web controllers through your DispatcherServlet and the gods of Object Oriented programming (and Martin Fowler) are mad.
And if that doesn’t convince you to use a services layer – well, don’t. Ok?

To layer out the application a bit, I will use a TrampsService, whose job is to supply the controller with tramps to display. In this way – the TrampsController only has to worry about getting the tramps (from the service) and passing them on to the view. It doesn’t need to know where those tramps come from (and later – when we deal with creating new Tramps – where do they go).

So – com.duckranger.goodproject.service.TrampsService

package com.duckranger.goodproject.service;

import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class TrampsService {

	private Map tramps;
	private static final Logger logger = LoggerFactory.getLogger(TrampsService.class);

	public TrampsService() {
		tramps = new HashMap();
		populate(tramps);
	}
	public Map findAll() {
		logger.info("Retrieving Walks");
		return tramps;
	}

	private void populate(Map tramps) {
		tramps.put(1L, "Lake Waikaremoana Great Walk");
		tramps.put(2L, "Tongariro Northern Circuit");
		tramps.put(3L, "Whanganui Journey");
		tramps.put(4L, "Abel Tasman Coast Track");
		tramps.put(5L, "Heaphy Track");
		tramps.put(6L, "Kepler Track");
		tramps.put(7L, "Milford Track");
		tramps.put(8L, "Routeburn Track");
		tramps.put(9L, "Rakiura Track");
	}
}

This one doesn’t do much really. I mainly cut the code that creates the tramps map from TrampsController and moved it here. As you can see, the class isn’t very special – and is pretty much a POJO. The only magic happens at line 9 – with the @Service annotation.
Very much like the @Controller annotation, the @Service annotation tells Spring that this class here is a component and should be treated as such (If you’re using STS, you know you got it right if you get the little blue ‘s’ in the package explorer).

Right, so we took the code that provides the data into the service – but how do we hook the service to the controller? Have no fear:

@Controller
public class TrampsController {

	private static final Logger logger = LoggerFactory.getLogger(TrampsController.class);

	@Autowired
	private TrampsService trampsService;

	/**
	 * List all tramps
	 */
	@RequestMapping(value = "/tramps", method = RequestMethod.GET)
	public String list(Model model) {
		logger.info("Listing Walks");
		model.addAttribute("tramps",trampsService.findAll());
		return "tramps/list";
	}

}

See how the controller changed? the code to create and populate the tramps map disappeared, and was replaced with lines 6-7. The controller now gets a TrampsService it can use (on line 15) to find all of the tramps.

But how does the controller actually get a handle to the TrampsService?

Well, this is just normal Spring magic. Remember the TrampsService is annotated with the @Service annotation. This means that Spring picked it up when it did its auto-detection – and registered it as a bean in its beans container.
Inside the TrampsController, the TrampsService is annotated as @Autowired. As Spring fires up it scans the packages for components and creates them as beans inside its container. When it encounters the @Autowired annotation, it knows that the component expects it to inject a bean here. So – when Spring creates the TrampsController component, it sees that it needs to inject (or wire) a TrampsService bean into the TrampsController.

Spring will find (or create) a TrampsService component and give it to TrampsController. (By the way – the name of the attribute can be anything. You can name it quiteGoodTrampsService – and Spring will still give you one!).

This is really it. Try to deploy the application again (will happen automatically if you’ve already deployed on tc-server inside STS) – and see that it still works…

Part II Summary

This part followed on directly from the first part, adding a new controller, service and view to the application. We touched a bit more on how Spring MVC maps requests to controller methods, how Spring scans packages to find components and create them as beans, how those components get wired together, etc.

We also saw a pretty basic view that uses a loop to display a map provided to it by the controller (who, in turn, got it from its auto-wired service).

In the next part – I think I’ll introduce a little bit of JPA to make things somewhat more interesting.

44 thoughts on “Spring MVC 3.x with STS Tutorial – Part II

  1. tetoulas

    Your articles have been a great help for me. Thank you!
    But, I’m trying to use STS to setup an MVC project with JPA support. Which is the best way to do it? If I create an MVC project, then I cannot create JPA entities from Tables (this seems to be enabled only for JPA projects).

    Thank you.

  2. Duck Ranger Post author

    @tetoulas – you are correct. Generating JPA entities from tables is only for JPA projects (and IIRC – it is not STS specific, but rather an EclipseLink feature?)

    If this is what you’re after, I suggest you either:

    1. Create a standalone JPA project and generate the entities there
    2. Create a Spring-JPA template project, convert it to a JPA project (right click, configure…JPA)
    3. Use Hibernate tools
  3. tetoulas

    Thank you really much.

    Just one more further question. Can you explain a little bit option ii? Where can I find Spring-JPA template project?

    Also, one more question regarding part III. When you use Roo for JPA setup, does this leave any “footprint” into the code (any roo-specific annotations etc.)?

  4. tetoulas

    Thank you Duck Ranger again.

    Now, as for Roo, on second thought, I’m not convinced that it leaves any footprint, since you don’t create any source code, just xml configurations.
    Also, I removed Roo from STS (remove Roo nature). It might have the same effect with the manual removal in this specific case.

  5. Hanzel

    I’m having a hard time understanding why when i click “run as…” in the project it does work fine and it opens the default jsp, but when i click it in the home.jsp file inside the WEB-INF/views directory i get an 404 error, because of this i also can’t run the list.jsp. Is this the normal behavior?, if so, can you explain or point me to the explanation?

  6. Indrek

    Added the service, created a new package next to the controller package and named it com.duckranger.goodproject.service.
    Created the TrampsService inside it, cleaned and rebuilt project etc. The ‘s’ doesn’t show up and therefore when I run my app, it crashes with exception:

    javax.servlet.ServletException: Servlet.init() for servlet appServlet threw exception

    and root cause:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘trampController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: …

    What’s wrong?

  7. Dustin

    Awesome walkthrough and looking forward to more. Any thoughts on using standards based @Inject vs @Autowired?

  8. Duck Ranger Post author

    @Indrek – have a look at your web.xml and make sure that component scan is on as described in the post. If this is alright – make sure that your service is annotated as a @Service or @Component.

  9. Duck Ranger Post author

    @Dustin – thanks a lot. Hope you like part 3 (and when I get around to it – the next parts as well…)
    As for your question: @Inject is, as you said, “standard based” – in the sense that this is the Java EE6 approved method (JSR-299). @Autowired is Spring’s own version of it – which is trivially the samea. In fact, in Spring, both @Inject and @Autowired are implemented under the hood with the AutowiredAnnotationBeanPostProcessor class – so unless you are thinking of moving your code from Spring to some other framework – it doesn’t really matter which you use.

  10. Felix

    Thanks…
    Looking forward for #3 of your Tutorials.
    Maybe a little hint for those who started working with spring today (just like me ):
    Insert in your home.jsp
    a link anywhere around in order to view it from the page directly:

    Tramps View

  11. Rejwan

    Thanks a whole lot for these nice tutorials . Waiting for such awesome tutorials in a very short near future :)

    Thanks again .

  12. drakon13

    I am doing 2nd tutorial with Tramps and I cannot display the list.jsp. How do I point to the TrampsController and where? I restarted the server but it did not help. Thanks!

  13. Christian

    Excellent article! Helps me to understand much more of the Spring MVC structure. Please note that in my case the web.xml has to be enhanced:


    default
    *.css

    cause without this the .css isn´t used!

    Cheers
    Christian

  14. Anna

    that’s the best tutorial I have found so far, so thank you. It would be great to see next part.
    As I am the beginner with web apps and Spring MVC, I’m wondering where should I put model classes, let say class ‘Tramp’. Should I just create another package?
    I’d also like to know what is the purpose of src/test folder. Thanks in advance for answers.

  15. Duck Ranger Post author

    @Anna – see the next part of the tutorial for the model classes and package. As for src/test folder – I’ll probably get around to that a bit later in the series… cheers

  16. eric

    The code for the appServlet-context.xml as well as the tramps/list.jsp is not displaying properly for me. Even when I view the page source, it’s been cut off. Is there something the matter with the page now?

    In regards to Hanzel’s comments, you can’t run JSPs from out of the WEB-INF folder directly. That’s why the controller is there – it’s a security feature, and CAN forward you to these resources. This protects your pages so that the controller properly sets up the model for them (and can do other security/state checking).

  17. Manni

    Cheers Duck. I actually feel as though I’m learning a lot about Spring MVC through your tutorials. I have a little previous experience working with Spring in conjunction with the Struts framework, but it was all XML based configuration. The annotations seem to kick a$$ and just make everything so much less painful!

    Thanks for taking out the time to put together such a detailed set of tutorials.

    PS. The girls are hot; not sure what ‘Foo’ is complaining about. Maybe he’d prefer a pic of Robert Pattinson or something :-/ lol.

  18. Lin

    Duck, thank you for writing these wonderful tutorials. I am so glad that I found your posting. You have explained everything not only so clearly, but also consistently. I installed STS 3.1.0. But,I think it is really 3.1.1. I have followed through the first tutorial without any problem. Now, I have completed the second tutorial. However, my TrampsService.java does not have the little blue S on it (I even put your copy into my project, and the result is the same: no little blue s sign.) Therefore, it didn’t get picked up by the and registered in the contained. When I ran this project, I got an exception about Service/Autowired injection problem. See below:

    ERROR: org.springframework.web.servlet.DispatcherServlet – Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘trampsController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.myspring.exercise2.service.TrampsService com.myspring2.exercise2.controllers.TrampsController.trampsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.myspring.exercise2.service.TrampsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

    Do you think STS 3.1.1 has change the @Service annotation functionality?

    Thanks in advance for your help to solve this problem. I cannot wait to move on to next tutorial!!!

  19. Duck Ranger Post author

    @Lin – this shouldn’t have anything to do with STS. Check the configuration in your



    entry, and ensure that it contains the service package (which seems different to the controller package in the stack trace you posted).

    cheers

  20. Lin

    Sorry. Found problem and fix it. It was caused by a typo in TrampsService package name. I was surprised that STS didn’t complaint about not finding the TrampsService class while compiling TrampsController, or I don’t know where to look for compilation error. Do you know what is the best way to make sure that every classes compiles? Thanks!

  21. rvs

    For the service, the example above has @Service on the actual service implementation. Does this mean while using annotations, we no longer need to use interfaces. With xml based DI, we used to declare a service interface and the implementation would be injected into the controller by spring.
    The controller only knew the interface. Here it will know the implementation.

    If we want to use interfaces, how would we annotate the interface, implementation and the controller class. I apologize if the question is naive…. new to annotation driven spring.

  22. Duck Ranger Post author

    The stereotypes are for implementation classes. They server for scanning.

  23. mhtabak

    I got part 1 to work fine, but part 2 won’t. I get an error message No mapping found for HTTP request with URI [/goodproject/tramps] in DispatcherServlet with name ‘appServlet’ which is consistent with the fact that I don’t see a little blue ‘S’ in the upper right hand corner of my TrampsController class and I don’t see a message coming out about mapping a request to [/tramps] as I do with [/]. Evidently the TrampsController is not being mapped to /goodproject/tramps as it should be. The package com.duckranger.goodproject.controller is aprantly not being scanned. Suggestions?

  24. Alex

    For reason unknown I don’t have neither date with home.jsp, neither table with list.jsp. They are absent in html source code. Maybe something wrong with JSTL?
    Where can I see the full project? URL to github seems broken.

  25. Paulo Calçada

    Excellents tutorials, Duck ! Both the part I and II. You have the nice skill of transform dificults subjects in easy articles. Thank you very much !

  26. Czadam

    Hi, great tutorial! I got the first part working, but I can’t get the second part working. I copied your code. I have the little blue S next to my TrampsController. In the spring elements I can see that the controller is registered, the RequestMappings are also there. But still getting the error: No mapping found for HTTP request with URI [/test/tramps] in DispatcherServlet with name ‘appServlet’.

    Do you have any idea what else could be the problem?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>