Articles

Spring Tutorial Two (Spring MCV)

In Tutorial on 13/02/2011 by pier0w

This next Spring tutorial is going to look at Spring MVC. This is Springs implementation of the Model View Controller architecture, with it you can configure your Model layer, configure the View template you wish to use, and create very simple and concise Controllers.

Now just like all Java web application frameworks Spring MVC must be wrapped up within a WAR file and run from within a Java web container. A web container can just be thought of as an environment that provides all the required boilerplate to allow the easy creation of web applications. It will automatically handle the creation of a thread pool to handle any requests, create sessions so that you can keep state across a users time on your web app, and lots of other great stuff.

There are quite a lot of Java web containers out there, there is Tomcat, Jetty, Glassfish, and JBoss just to name a few.

The one we are going to use is Jetty, this is a Java web app container that can be embedded within an application. It is not the only one of course, Tomcat can also be embedded, but Jetty was specifically created for this purpose.

This tutorial is going to build on the previous Spring tutorial, so open that within Intellij IDEA.

Since we are working with the project from the first tutorial lets make a tag to preserve the code in it’s current state. To do this navigate to the projects directory and type the following command.

#> git tag -a tutorial_one -m "A tag taken at the end of the first Spring tutorial."

You can check to make sure that tag was created with this command.

#> git tag -l -n1

The -l tells git to list the tags and the -n1 tells git to display one line of the tags message. You can use a higher number to display more lines.

The first thing we will do is add the Jetty maven dependency to our pom file, so open the file and add the dependency to the <dependencies> tag.

...
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty.aggregate</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>7.1.4.v20100610</version>
        </dependency>
    </dependencies>
...

Now lets remove pretty much all of the code we wrote in the first tutorial because it won’t be of any use to us in this new tutorial. Delete the context file in the resources directory, the MyClassOne, MyClassTwo, and MyHolder classes, all the code from within the MainTest leaving the shell of the test method, the toStuff method from within the Main class as well as all the code within the main method. Once that is done commit the changes into git.

package org.project;

public class Main {

    public static void main(String[] args) {
    }
}
package org.project;

import org.junit.Test;

public class MainTest {

    @Test
    public void testDoStuff() throws Exception {
    }
}

If you wish to get the old code back you can just checkout the tag.

#> git checkout tutorial_one

Note that when you commit changes to the tag they will not actually be saved if you then move off the tag. To save your changes you will have to create a branch from the tag with the git branch method.

If you wish to then get back to the code we just committed then us this command.

#> git checkout master

Now that we are back looking at our new code lets see if we can’t start up a Jetty server in preparation for our Spring code. First create an instance of a Jetty Server class, start the server, then join the server to the main thread so that the main thread doesn’t just exit right after starting the server.

package org.project;

import org.eclipse.jetty.server.Server;

public class Main {

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080); // Create a server instance that listens on port 8080.
        server.start(); // Start the server.
        server.join(); // Join the main thread to the server so that the main thread doesn't exit.
    }
}

Pro tip: Only copy the code from within the main method and past it into yours. Then use ALT-ENTER to get Intellij IDEA to import the jetty Server class for you. The start and join method calls will now be underlined in red so click within the code of one of them and in a moment a small red light build should show up off to the left. Click on it and select the Add Exception(s) to Method Signature option. This will make the main method throw the correct exception for the methods.

You can now run the main method and see the server start up. Though it is a rather useless application server because there is nothing running within it.

To get Jetty to run something he have to give it a Handler. It is possible to either create your own handler or use one of the pre-made handlers that Jetty provides. Since we are making a web application that will be running Spring MVC we will be using Jetty’s WebAppContext handler. To instantiate this handler you need to give it the path to where your web application configuration files will be and also the context of your web application. A context is simply some text that comes after the domain name of the web app where all of the web apps pages will fall under. So if a web app is found at the following URL.

http://localhost:8080/myapp/index.html

Then the context would be myapp and all the wep apps pages like index.html will be found under that context.

So lets instantiate a WebAppContext handler and give it the path webapp and the context / which means there actually will not be a context.

To get the proper path to the webapp directory we will need to get the resource URL and then get the external form from that. This is because we are running within the environment of a JAR file, so it is unfortunately not as simple as giving it the name of the directory.

We will also need to tell Jetty not to extract the web apps directory, this is again because we running the project from within a JAR environment.

package org.project;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

import java.net.URL;

public class Main {

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080); // Create a server instance that listens on port 8080.

        URL webAppURL = server.getClass().getClassLoader().getResource("webapp"); // Find the webapp dir from within the JAR.

        String webAppPath = webAppURL.toExternalForm(); // Then get it's actual path string.

        WebAppContext webAppContext = new WebAppContext(webAppPath, "/"); // The config dir will be in resources/webapp.
        webAppContext.setExtractWAR(false); // Make sure not to extract the web app directory because we will run this in a JAR file.

        server.setHandler(webAppContext); // Add the WebAppContext handler to the Jetty server.

        server.start(); // Start the server.
        server.join(); // Join the main thread to the server so that the main thread doesn't exit.
    }
}

You can now try and run the Jetty server again but as you might expect it will crash, this is because we haven’t created the webapp directory under the resources directory. Also we haven’t created the web.xml file that is required by the WebAppContext handler.

Lets do both of those things now, first create the webapp directory under the resources directory, then create another directory under that called WEB-INF, lastly under that directory create an empty file called web.xml. In the end you should have a path to the web.xml file as follows.

src/main/resources/webapp/WEB-INF/web.xml

Now past the following into the web.xml file.

<web-app id="WebApp_9" 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">

    <display-name>Spring Tutorial Two</display-name>
</web-app>

Now run the main method again and Jetty should start up happily.

It is also a good idea to add a thread pool to Jetty, this will make it run a lot more efficiently and faster. We will start up a queued thread pool with a minimum of 10 threads and a maximum of 50 threads.

package org.project;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;

import java.net.URL;

public class Main {

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080); // Create a server instance that listens on port 8080.

        URL webAppURL = server.getClass().getClassLoader().getResource("webapp"); // Find the webapp dir from within the JAR.

        String webAppPath = webAppURL.toExternalForm(); // Then get it's actual path string.

        WebAppContext webAppContext = new WebAppContext(webAppPath, "/"); // The config dir will be in resources/webapp.
        webAppContext.setExtractWAR(false); // Make sure not to extract the web app directory because we will run this in a JAR file.

        server.setHandler(webAppContext); // Add the WebAppContext handler to the Jetty server.

        QueuedThreadPool threadPool = new QueuedThreadPool(); // Instantiate the thread pool.
        threadPool.setMinThreads(10); // Set the minimum threads to 10.
        threadPool.setMaxThreads(50); // Set the maximum threads to 50.

        server.setThreadPool(threadPool); // Add the thread pool to the Jetty server.

        server.start(); // Start the server.
        server.join(); // Join the main thread to the server so that the main thread doesn't exit.
    }
}

Right, so now the programmatical configuration of the Jetty server is complete. All the rest of our configuration will be done within the web.xml file.

The web.xml is where we will configure our Spring context. Remember in the first tutorial we started the Spring context within the main method. This time by configuring the Spring context object within the web.xml file we will get the web app container to start it.

Now to return a successful response to an HTTP request a Java Web Application Container needs to contain a Servlet. Servlets are J2EE classes that contain all the logic to handle request for a given URL pattern. That is all you will ever need to know about them because no one ever manually programs Servlets any more and all we will be doing is configuring the Spring context so that it behaves like a Servlet.

So as always when starting a Spring project the first thing we need to do is create a XML context configuration file and place it in the resources directory.

<?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-3.0.xsd">
</beans>

Now that we have the context file we need to configure the Spring context class. We will use is the Spring DispatcherServlet class which configures the Spring context and wraps it in a J2EE Servlet. We will need to add the Spring MVC dependency to the pom file so that we have access the DispatcherServlet class and all the other classes within the Spring MVC module.

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty.aggregate</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>7.1.4.v20100610</version>
        </dependency>
    </dependencies>

To correctly configure this class we need to give it a name, the full name of the DispatcherServlet class, the path to the context file, and lastly the order in which it should be started. For our project we will want it to be the very first thing the web container starts.

<web-app id="WebApp_9" 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">

    <display-name>Spring Tutorial Two</display-name>

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:context.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

We can now run the main method again and we should see the Jetty server start as well as the Spring framework.

It is now possible to start actually writing some code for a very simple Spring MVC web app. The first place to start is the Model or the M in MVC. The model is the data layer of the application, we shall make our model exceptionally simple, it will just be a class that contains some data. So lets create a Model class.

package org.project.model;

import java.util.Date;
import java.util.List;

public class Model {

    private String name;
    private Date date;
    private List data;
    private String message;

    public Model() {
    }

    public Model(String name, Date date, List data, String message) {
        this.name = name;
        this.date = date;
        this.data = data;
        this.message = message;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public List getData() {
        return data;
    }

    public void setData(List data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

And now lets wire it up within the Spring context file.

<?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-3.0.xsd">

    <bean id="model" class="org.project.model.Model">
        <property name="name" value="Model"/>
        <property name="date">
            <bean class="java.util.Date"/>
        </property>
        <property name="data">
            <list>
                <bean class="java.lang.Integer"><constructor-arg value="1"/></bean>
                <bean class="java.lang.Integer"><constructor-arg value="2"/></bean>
                <bean class="java.lang.Integer"><constructor-arg value="3"/></bean>
                <bean class="java.lang.Integer"><constructor-arg value="4"/></bean>
            </list>
        </property>
        <property name="message" value="Message from the model."/>
    </bean>
</beans>

Now that we have a model lets configure our view, the V in MVC. The view is the framework that we will use to present the data from our model to the user, this is generally done with HTML. We will use Freemarker as our view, so lets first add it’s dependency to our pom file.

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty.aggregate</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>7.1.4.v20100610</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.16</version>
        </dependency>
    </dependencies>

Now that we have access to the Freemarker classes we can configure it in the Spring context file. There are two classes that need to be configured, the first is the FreeMarkerConfigurer class, here we set the path to the Freemarker files. The next class is the FreeMarkerViewResolver which is the class that will actually parse the Freemarker files. We will configure this so that it doesn’t cache the pages, so that it knows what files are our Freemarker files, enable the Spring extensions, and lastly set the Freemarker resolver to always be the first resolver that is picked by Spring when it tries to resolve a view.

<?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-3.0.xsd">

    <bean id="model" class="org.project.model.Model">
        <property name="name" value="Model"/>
        <property name="date">
            <bean class="java.util.Date"/>
        </property>
        <property name="data">
            <list>
                <bean class="java.lang.Integer">
                    <constructor-arg value="1"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="2"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="3"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="4"/>
                </bean>
            </list>
        </property>
        <property name="message" value="Message from the model."/>
    </bean>

    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/pages/"/>
    </bean>

    <bean id="freemarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="false"/>
        <property name="prefix" value=""/>
        <property name="suffix" value=".ftl"/>
        <property name="exposeSpringMacroHelpers" value="true"/>
        <property name="order" value="0"/>
    </bean>

</beans>

Now that our view framework is configured we can create a simple view page. This page will display the name and message properties of the model class. This class will be passed to the view by the controller which we will write next. To created the page first create the pages directory under the WEB-INF directory. This is where we said the Freemarker files would be when we configured the FreeMarkerConfigurer above. Then create a file called view.ftl and passed the following HTML into it.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Spring Tutorial Two</title>
</head>
<body>
    <h1>${model.name}</h1>
    <p>${model.message}</p>
</body>
</html>

The ${model.name} and ${model.message} within the page are the very small snippets of Freemarker that we are going to use in this tutorial. It is this syntax that is used to access a domain class and it’s properties. A domain class is a class that has been placed into the domain of a view by the controller. We will see how this is done soon.

Now that the view file is created we can move onto the controller, the C in MVC. A controller is a class to simply take data classes from the model and sends them into the view. This is usually done by the controller calling on a service layer that abstracts away any logic and decouples the Controllers and Views from the Model layer, but for simplicities sake we are just going to pass the Model class straight into the controller.

The annotations we are going to use are:
@Controller – This is used to annotate a class to mark it as a Spring Controller.
@RequestMapping – This is used to annotate a class or method to map them to a specific request URL pattern. When the annotation is used on a class it is usually used to map a common context that all the methods in that class will then fall under. Though it is also possible to map a class to a URL pattern then just have a method that handles all the GET request for that class. Other wise methods can have their own unique request URL patterns mapped to them.
@Resource – This annotation is another way of telling Spring to inject a class, the difference between this and @Autowired is that with this annotation you can specify the name of the bean you would like to inject.

So after those explanations here is the controller.

package org.project.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.project.model.Model;

import javax.annotation.Resource;

@Controller
@RequestMapping(value = "/model")
public class ModelController {

    @Resource(name = "model")
    private Model model;

    @RequestMapping(value = "/view")
    public String view(ModelMap modelMap) {

        modelMap.addAttribute("model", model);

        return "view";
    }
}

For this controller we have used the combination of providing a request mapping for the class and the method. This means that Spring with route all request that begin with /model to the ModelController class, then it will finally route the complete pattern /model/view to the view method within that class.

We have given the method a ModelMap argument. Any ModelMap argument that you give to a request method will automatically be injected by Spring with the responses ModelMap. The ModelMap is a map that is forwarded by Spring to the view. So any classes you place into this map will become domain classes in the view. That is why we are placing the Model object into this map. It can get a bit confusing that the word Model is used by Spring to label classes that will be passed to the view, since this is also the name for the data layer of an application that uses the MVC pattern. This is also Spring MVC we are talking about so you would have thought they might have seen how that could be confusing, but oh well…

The last thing to explain about this controller is that the request method is returning a static String "view". If Spring sees that your request method returns a String it will assume that that String will be that name of a view file that it should use to render the response. It is possible to have your request methods return far more complicated things but that is outside the scope of this tutorial. So with that in mind you can see that Spring will select out view.ftl file to render the response.

Now the very last thing we have to do is to tell Spring were our controller is. This is done in the same way that we told it how to find the HyHolder class at the end of the last tutorial.

<?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-3.0.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="model" class="org.project.model.Model">
        <property name="name" value="Model"/>
        <property name="date">
            <bean class="java.util.Date"/>
        </property>
        <property name="data">
            <list>
                <bean class="java.lang.Integer">
                    <constructor-arg value="1"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="2"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="3"/>
                </bean>
                <bean class="java.lang.Integer">
                    <constructor-arg value="4"/>
                </bean>
            </list>
        </property>
        <property name="message" value="Message from the model."/>
    </bean>

    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/pages/"/>
    </bean>

    <bean id="freemarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="false"/>
        <property name="prefix" value=""/>
        <property name="suffix" value=".ftl"/>
        <property name="exposeSpringMacroHelpers" value="true"/>
        <property name="order" value="0"/>
    </bean>

    <context:component-scan base-package="org.project.controllers"/>
</beans>

With that complete we can finally run our web application and the simple view we created should be able to be seen at the following URL displaying the data that we configured in the model.

http://localhost:8080/model/view

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s