Wednesday, October 30, 2013

Exploring Spring Controller with Spring Form Tag

In the past article, I have shown you how to process an plain HTML form with Spring controller. But a more powerful way to process form is to use Spring’s @ModelAttribute and its spring:form tags. I will show you how to get that started here by modifying last article’s project setup. We will simply modify the Comment form and controller to use this feature.

In the same project, modify the src/webapp/comment.jsp view file into this:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags/form" %>
<spring:form modelAttribute="comment">
  <table>
    <tr>
        <td><spring:textarea path="text" rows="20" cols="80"/></td>
    </tr>
    <tr>
        <td colspan="2">
        <input type="submit" value="Post"/>
        </td>
    </tr>
  </table>
</spring:form>

This view now use the spring:form tag to render the comment form instead of plain HTML. I have only shown you one element here, but the spring:form tag library also comes with all the matching HTML form elements that helps you quickly render the form with data binded. This automatically triggers the CommentController when you submit. We will need to modify that to capture the form.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import springweb.data.Comment;
import springweb.data.CommentService;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
public class CommentController {

    @Autowired
    private CommentService commentService;

    @RequestMapping(value="/comments")
    public ModelAndView comments() {
        List<Comment> comments = commentService.findComments();
        ModelAndView result = new ModelAndView("/comments");
        result.addObject("comments", comments);
        return result;
    }

    @ModelAttribute("comment")
    public Comment createFormModelAttribute() {
        return Comment.create("");
    }

    @RequestMapping(value="/comment")
    public String comment() {
        return "comment";
    }

    @RequestMapping(value="/comment", method = RequestMethod.POST)
    public ModelAndView postComment(HttpServletRequest req,
                                @ModelAttribute("comment") Comment comment) {
        String fromUrl = req.getRequestURI();
        String user = req.getRemoteUser();
        String userIp = req.getRemoteAddr();
        comment.setFromUserIp(userIp);
        comment.setFromUser(user);
        comment.setFromUrl(fromUrl);
        commentService.insert(comment);
        ModelAndView result = new ModelAndView("comment-posted");
        result.addObject("comment", comment);
        return result;
    }
}

The difference in this controller compare to the old one is we used @ModelAttribute with an form object (or Spring calls it a command object.) We can name it, which I called comment here. It’s just a java POJO class, nothing special. But it is used to capture all the form input and then pass to Controller, which is called data binding. Notice that it will instanciate by createFormModelAttribute() method as you request the form view first. If you pre-populate the pojo with text, then it will automatically shows in the form! When user submit the controller will process in postComment() method, and the form object is re-populated with new form input again for processing. This allows you to work with form in pure Object style, and in many ways, it’s shorter and cleaner compare to plain HTML form.

There are much to the Spring MVC form processing. One powerful feature is it able to help you orgainze the form object validation and collect the error messages. Spring also helps localize your error message text etc. You may read more on their reference doc.

Thursday, October 17, 2013

Exploring Spring Controller with JSTL view

Let’s improve our previous Spring JDBC application with some more exploration on Spring MVC’s Controller development. I will show another exercise of writing a new Controller that processes a HTML form and use JSTL tags in JSP view pages.

To enable JSTL in Spring MVC application, you would need to add the following to the WebAppConfig config class. Let’s move it outside of WebApp.java and into it’s own top level class file in src/main/java/springweb/WebAppConfig.java.

package springweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("springweb.controller")
public class WebAppConfig {
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver result = new InternalResourceViewResolver();
        result.setPrefix("/");
        result.setSuffix(".jsp");
        return result;
    }
}

Inside the InternalResourceViewResolver bean, you define where to find your JSP pages that may have JSTL tags in them. The prefix setter is a path in relative to your src/webapp location. This allow you to hide your JSP files completely if you want to. For example, by setting it to "/WEB-INF/jsp" then you may move and store all JSP files into src/webapp/WEB-INF/jsp which is private in the web application. The suffix is just the file extension. These two values allow you to return a view name inside the controller with just the basename of your JSP file, which can be short as "/myform" or "/index" etc.

If you are to use Tomcat as your web container, you would need to add JSTL jar dependency as well, since the Tomcat server doesn’t come with standard tag library! So add this into the pom.xml file now.

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

While you’re at the pom.xml file, you might want to add the Tomcat maven plugin so you can type less in command line when running your web application.

<project>
...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
            </plugin>
        </plugins>
    </build>
...
</project>

With that, you should able to run mvn tomcat7:run in root of your project without plugin prefix.

So what does JSTL bring to your application? Well, quite a bit actually. It lets you use some standard JSP tags that’s frequently used when writing JSP views. I will demonstrate this with a set of Controller and views to capture user comments from the application. Note that I will try to show you how to work with Spring Controller in the most basic way only. The Spring actually comes with a custom form JSP tag that is much more powerful to use. I will reserve that as another article in another time. Today let us focus on learning more about basic Spring Controller and JSTL, and a bit more on Spring JDBC data service as well.

We want to capture user comment, so let’s add a database table to store that information. Add the following DDL into your src/main/resources/schema.sql file. Again, this is for H2 database per last article project setup.

CREATE TABLE COMMENT (
  ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  TEXT VARCHAR(10240) NOT NULL,
  FROM_USER VARCHAR(15) NULL,
  FROM_USER_IP VARCHAR(15) NULL,
  FROM_URL VARCHAR(1024) NULL,
  TAG VARCHAR(1024) NULL,
  TS DATETIME NOT NULL
);

This time, we will write a data model class to match this table. Let’s add src/main/java/springweb/data/Comment.java

package springweb.data;

import java.util.Date;

public class Comment {
    Long id;
    String text;
    String fromUrl;
    String fromUser;
    String fromUserIp;
    String tag;
    Date ts;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getFromUrl() {
        return fromUrl;
    }

    public void setFromUrl(String fromUrl) {
        this.fromUrl = fromUrl;
    }

    public String getFromUser() {
        return fromUser;
    }

    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    public String getFromUserIp() {
        return fromUserIp;
    }

    public void setFromUserIp(String fromUserIp) {
        this.fromUserIp = fromUserIp;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public Date getTs() {
        return ts;
    }

    public void setTs(Date ts) {
        this.ts = ts;
    }

    private String getTrimedComment(int maxLen) {
        if (text == null)
            return null;
        if (text.length() <= maxLen)
            return text;
        return text.substring(0, maxLen);
    }

    @Override
    public String toString() {
        return "Comment{" +
                "id=" + id +
                ", ts=" + ts +
                ", text='" + getTrimedComment(12) + '\'' +
                '}';
    }

    public static Comment create(String commentText) {
        Comment result = new Comment();
        result.setText(commentText);
        result.setTs(new Date());
        return result;
    }
}

Just as previous artcicle, we will write a data service to handle insert and retrieve of the data model. We add a new src/main/java/springweb/data/CommentService.java file

package springweb.data;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class CommentService {
    public static Log LOG = LogFactory.getLog(CommentService.class);

    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcInsert insertActor;
    private RowMapper<Comment> commentBeanRowMapper = new BeanPropertyRowMapper<Comment>(Comment.class);

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.insertActor = new SimpleJdbcInsert(dataSource)
            .withTableName("COMMENT")
            .usingGeneratedKeyColumns("ID");
    }

    public void insert(Comment comment) {
        LOG.info("Inserting Comment + " + comment);

        Map<String, Object> parameters = new HashMap<String, Object>(2);
        parameters.put("TEXT", comment.getText());
        parameters.put("FROM_USER", comment.getFromUser());
        parameters.put("FROM_USER_IP", comment.getFromUserIp());
        parameters.put("FROM_URL", comment.getFromUrl());
        parameters.put("TAG", comment.getTag());
        parameters.put("TS", comment.getTs());
        Number newId = insertActor.executeAndReturnKey(parameters);
        comment.setId(newId.longValue());

        LOG.info("New Comment inserted. Id=" + comment.getId());
    }

    public List<Comment> findComments() {
        String sql = "SELECT " +
                "ID as id, " +
                "TEXT as text, " +
                "TAG as tag, " +
                "TS as ts, " +
                "FROM_USER as fromUser, " +
                "FROM_USER_IP as fromUserIp, " +
                "FROM_URL as fromUrl " +
                "FROM COMMENT ORDER BY TS";
        List<Comment> result = jdbcTemplate.query(sql, commentBeanRowMapper);
        LOG.info("Found " + result.size() + " Comment records.");
        return result;
    }
}

Since we did not use any fancy ORM but just plain JDBC, we will have to write SQL in the data service. But thanks to Spring goodies, it makes life much easier with helpers such as SimpleJdbcInsert, which handles DB insert and retrieval of auto generated key etc. And also notice that in query, we use the Spring’s BeanPropertyRowMapper to automatically convert JDBC resultset into a java bean Comment object! Simple, direct and quick.

Now we add the Spring controller in src/main/java/springweb/controller/CommentController.java to handle web requests.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import springweb.data.Comment;
import springweb.data.CommentService;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
public class CommentController {

    @Autowired
    private CommentService commentService;

    @RequestMapping(value="/comments")
    public ModelAndView comments() {
        List<Comment> comments = commentService.findComments();
        ModelAndView result = new ModelAndView("/comments");
        result.addObject("comments", comments);
        return result;
    }

    @RequestMapping(value="/comment")
    public String comment() {
        return "comment";
    }

    @RequestMapping(value="/comment", method = RequestMethod.POST)
    public ModelAndView postComment(HttpServletRequest req, @RequestParam String commentText) {
        String fromUrl = req.getRequestURI();
        String user = req.getRemoteUser();
        String userIp = req.getRemoteAddr();
        Comment comment = Comment.create(commentText);
        comment.setFromUserIp(userIp);
        comment.setFromUser(user);
        comment.setFromUrl(fromUrl);
        commentService.insert(comment);
        ModelAndView result = new ModelAndView("comment-posted");
        result.addObject("comment", comment);
        return result;
    }
}

In this controller we map /comment URL to handle display of the HTML form, which returns the comment.jsp view. The method default to handle a HTTP GET. Note that we remapped same /comment URL to handle HTTP POST on a separated postComment() method! See how clean the Spring Controller can be in this demo to handle HTTP request. Pay very close attention to the parameters declared in the postComment() method. Spring automatically handles the HTTP request object and map to your method just based upon the types you declared! On some cases you need to be explicit with the help of annotation such as @RequestParam, but Spring does the parsing of HTTP request and extraction for you! This saves you tons of repeated boiler plate code if we were to write a direct Servlet code.

Now let’s see the view and how to use JSTL. The /comments URL is mapped to src/main/webapp/comments.jsp view file, which will list all Comment model objects.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:choose>
<c:when test="${empty comments}">
    <p>There are no comments in system yet.</p>
</c:when>
<c:otherwise>
    <table border="1">
    <tr>
        <td>INDEX</td>
        <td>TIME</td>
        <td>FROM</td>
        <td>COMMENT</td>
    </tr>
    <c:forEach items="${comments}" var="comment" varStatus="status">
    <tr valign="top">
        <td>${status.index}</td>
        <td>${comment.ts}</td>
        <td>${comment.fromUserIp}</td>
        <%-- The c:out will escape html/xml characters. --%>
        <td><pre><c:out value="${comment.text}"/></pre></td>
    </tr>
    </c:forEach>
    </table>
</c:otherwise>
</c:choose>

Pretty standard stuff on JSTL. Next is the HTML form to post comment in src/main/webapp/comment.jsp file.

<form action="comment" method="POST">
<textarea name="commentText" rows="20" cols="80"></textarea>
<br/>
<input type="submit" value="Post"/>
</form>

When form is posted and processed successful, we simply return to a new page in src/main/webapp/comment-posted.jsp file as output.

<p>Your comment has been posted. Comment ID=${comment.id}</p>

If you've done these right, you should able to run mvn tomcat7:run and browse http://localhost:8080/spring-web-annotation/comment to see the form. Go to /comments URL to verify all the comments posted.

Note that despite I used Spring Controller as backend, all the views are in basic JSTL, and even the form are just basic HTML elements! I did this so you can see how flexible Spring Controller can be.

I know these are lot of code to post as a blog article today, but I wanted to be complete and try to show a working demo with tutorial notes. I choose to make it into a single post with file content instead of having you download a project somewhere else. It makes my notes and explanation easier to match onto the code.

And that will conclude our tutorial for today. Please leave a note if you find this helpful.

Thursday, October 10, 2013

Getting started with Spring JDBC in a web application

I have shown you how to setup a basic Spring 3 MVC web application in my previous article. Reusing that project setup as template, I will show you how to enhance it to work with JDBC. With this you can store and retrieve data from database. We will add a new controller and a data service through Spring so you can see how Spring injection and annotation configuration works together.

A direct JDBC based application is easy to setup in comparison to a full ORM such as Hibernate. You don’t need to worry AOP, TranactionManager, Entity mapping and full array of other configurations. On top of the JDK’s java.jdbc API, the Spring comes with spring-jdbc module that can boots your productively with their well known JdbcTemplate class. Let’s explore how this can be setup and run as web application.

Getting started and project setup

For demo purpose, I will use the in-memory version of H2Database as JDBC store. It’s simple to use and setup. And if you decided to use their FILE or TCP based database, you would simply have to re-set the datasource, and you may continue to explore more.

We will start by adding new dependencies to your existing spring-web-annotation/pom.xml file.

  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.3.163</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>3.2.4.RELEASE</version>
  </dependency>

With this, you will have access to Spring module classes for configuration. Find the previous src/main/java/springweb/WebApp.java file in your existing project and add what’s new compare to below:

package springweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.sql.DataSource;

public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{ RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ WebAppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan("springweb.controller")
    public static class WebAppConfig {
    }

    @Configuration
    @ComponentScan("springweb.data")
    public static class RootConfig {
      @Bean
      public DataSource dataSource() {
        DataSource bean = new EmbeddedDatabaseBuilder()
          .setType(EmbeddedDatabaseType.H2)
          .addScript("classpath:schema.sql")
          .build();
        return bean;
      }
    }
}

What’s new in here is we introduced a new RootConfig class that will be loaded inside getRootConfigClasses() method. The RootConfig is just another Spring annotation based configuration that creates a new Spring context for bean definitions. We’ve created a bean there that will run the in-memory database. The bean returned by the builder also conveniently implemented the javax.sql.DataSource interface, so we can actually inject this into any data service and start using it immediately.

One more cool thing about the Spring embedded database builder is that it also runs any SQL script as part of the startup! For this demo, we will create a PING table in the src/main/resources/schema.sql file. This file is visible to Spring as in root of the CLASSPATH due to Maven standard source structure.

CREATE TABLE PING (
  ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  TAG VARCHAR(1024) NOT NULL,
  TS DATETIME NOT NULL
);

That’s the datasource setup. Now notice that I did not add this datasource Spring bean definition into existing WebAppConfig class. The reason is that we want a separate Spring context to configure all service level beans, while reserving the WebAppConfig for all Spring MVC related beans (such as Controller, URL mapping etc). This helps organize your bean definitions in hierarchical order of Spring contexts; with RootConfig as parent and WebAppConfig as child layers. This also means that all service beans in RootConfig are automatically visible to WebAppConfig; for the purpose of injection etc.

Also notice that with separated config classes, we are able to specify two distinct packages to scan for service components; we use springweb.controller for WebAppConfig and springweb.data for RootConfig. This is important and it can save you some troubles by letting Spring auto detecting your annotation based components.

Creating Data Service

Now it’s time we use the JDBC, so let’s write a data service class under src/main/java/springweb/data/PingService.java file.

package springweb.data;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Repository
public class PingService {
    public static Log LOG = LogFactory.getLog(PingService.class);

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void insert(String tag) {
        LOG.info("Inserting Ping tag: " + tag);
        jdbcTemplate.update("INSERT INTO PING(TAG, TS) VALUES(?, ?)", tag, new Date());
    }

    public List<Map<String, Object>> findAllPings() {
        return jdbcTemplate.queryForList("SELECT * FROM PING ORDER BY TS");
    }
}

The service is very straight forward. I expose two methods: one for insert and one for retrieve all Ping data. Noticed that I used @Repository to indicate to Spring that this class is a component service that perform data service. Also note how we inject the DataSource using a setter method, and then instantiate the JdbcTemplate as member field. From that we can take full advantage of the Spring JDBC API for query and update.

A note on logging. Spring core itself uses Apache common-logging, so I reused that API without even explicitly declare them in my pom.xml. If you want to see more details from log output, you should add log4j logger implementation to the project, and it should automatically work. I will leave that as your exercise.

Next we will need to write a Controller that can bring data to web UI page. We will create this under src/main/java/springweb/controller/PingController.java file.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import springweb.data.PingService;

import java.util.List;
import java.util.Map;

@Controller
public class PingController {

    @Autowired
    private PingService pingService;

    @RequestMapping(value="/ping/{tag}", produces="text/plain")
    @ResponseBody
    public String pingTag(@PathVariable("tag") String tag) {
        pingService.insert(tag);
        return "Ping tag '" + tag + "' has been inserted. ";
    }

    @RequestMapping(value="/pings", produces="text/plain")
    @ResponseBody
    public String pings() {
        List<Map<String, Object>> result = pingService.findAllPings();
  if (result.size() == 0)
   return "No record found.";

        StringBuilder sb = new StringBuilder();
        for (Map<String, Object> row : result) {
            sb.append("Ping" + row).append("\n");
        }
        return sb.toString();
    }
}

In this controller, you can easily see that the Ping data is fetched and updated through our data service by injection. I’ve declared and map URL /ping/{tag} to insert Ping data into the database. Spring has this very nice shorthand syntax annotation that can extract parameter from your URL path. In this case it let user to pass a simple tag keyword to be inserted as Ping record; so we can identify the source in database.

The other controller handler /pings URL is very straight forward; it simply returns all the records from PING table.

For demo purpose, I choose to not use JSP as view, but to return plain text directly from the Controller. Spring let you do this by adding @ResponseBody to the handler method. Notice also we can specify the content type as text/plain as output directly using the annotation.

Testing

To see your hard labor with above, you simply need to run the Maven tomcat plugin. The previous article has shown you an command to do that. Once you restarted it, you should able to open a browser and use these URLS for testing.

Happing programming

From this simple exercise, you can quickly see Spring MVC brings you many benefits; and a lot of fun in developing web application. Spring, by design principles, tends to be developers friendly, boots productivity and un-intrusively in your environment. It’s one of the reason I like to work with it a lot. I hope you enjoy this tutorial and go further explore on your own.

Happy programming!

Saturday, October 5, 2013

Getting started with annotation based Spring MVC web application

Here is a minimal way to get a Spring 3 MVC project started with Maven.

First create spring-web-annotation/pom.xml file and include the Spring dependency:

<?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>spring-web-annotation</groupId>
    <artifactId>spring-web-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

Now create the Servlet 3 web initializer and the Spring annotation config for the MVC parts in spring-web-annotation/src/main/java/springweb/WebApp.java

package springweb;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ WebAppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan("springweb.controller")
    public static class WebAppConfig {
    }
}

The WebApp class extends Spring's built in Servlet3 web initializer code. It allows Servlet3 container such as Tomcat7 to auto detect this web application without the need of web.xml configuration setup. Because of we do not use web.xml, we need this class to allow Spring to hook into the Servlet container to bootstrap their dispatcher servlet. Also instead of typical Spring beans xml file configuration, we now can use all annotation based using WebAppConfig.

Noticed that I have combined the WebAppConfig as inner class, but you can easily move it out as top level class in a full scale application. This is the Spring annotation version of container configuration. You can easily customize the application by adding new @Bean here.

Note: Do not forget to overwrite getServletMappings method with "/", or else your URL request will not direct to the Spring dispatcher for processing! A step that can easily forgotten and you might find your self chasing why Spring controllers are not working.

Above are really the minmal setup you need to start a war project. Next you want to add at least one controller to have some output to verify. Create this controller file spring-web-annotation/src/main/java/springweb/controller/IndexController.java

package springweb.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping(value="/")
    public String index() {
        return "index";
    }
}

And now you would need the JSP view spring-web-annotation/src/main/webapp/index.jsp

Hello World.

Now cd into spring-web-annotation and execute mvn org.apache.tomcat.maven:tomcat7-maven-plugin:run. You should see your Spring application starting and be able to browse http://localhost:8080/spring-web-annotation URL.

There are lot of cool stuff you can do with Spring MVC. Checkout their awesome docs for more details.