Gradle build Spring MVC application with jQuery AJAX example

Spring is a popular MVC framework for developing Java web application, its a powerful, flexible, community driven , easy to use and widely deployed framework that capable of implement all kinds of web application features. It will largely simplify your development process, take the pain out of coding, sometimes its just fun and interesting to play with it.

AJAX is a technology that can improve the user experience of you application in an interactive way, you application can act more like a desktop application, send data back and forth between browser and server , and update UI elements without refreshing web page. In a single page application, user never leave the current page, click on button doesn't have to load another page. AJAX also gets more popular in enterprise Java development, it may not be a fully AJAX based application, but in some corner of the page, using AJAX can be very helpful to the users.

As a general purpose framework, Spring has a good support for AJAX, actually with the declarative request mapping, annotation based configuration, url mapping and MVC facility, its fun and easy to add AJAX to your web application. This post will teach you how to do this with a simple Spring MVC example, built with Gradle.

The application is simple, in the home page, there will be a button, when clicked, it send an AJAX request to the server, the server just simply return a greeting message and the client will refresh the page with the response.

Step 1. Create project

Create a Gradle project in Eclipse, you should have the following application folder structure.

The component of the system is simple: a controller, annotation based MVC configuration files, a CSS style, a view JSP file and the Gradle build script, thats all we need.

You may want to look at how to create a basic Spring MVC application with Gradle: Gradle example to build Spring MVC application. This application has the same project infrastructure.

The following Gradle build script can download all the dependencies and also the servlet container, in this case, is Tomcat.

build.gradle

 
apply plugin: 'base'
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
apply plugin: 'com.bmuschko.tomcat'
apply plugin: 'com.bmuschko.cargo'
 
ext.tomcatVersion = '7.0.30'
ext.cargoVersion = '1.1.3'
 
 
 
buildscript {
    repositories {
        jcenter()
    }
 
    dependencies {
        classpath 'com.bmuschko:gradle-tomcat-plugin:2.2.2'
        classpath 'com.bmuschko:gradle-cargo-plugin:2.1.1'    
   }
 
}
 
 
 
 
repositories {
 
    mavenCentral()
 
}
 
dependencies {
 
 
   providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
   compile 'org.springframework:spring-webmvc:3.2.2.RELEASE'
   runtime "jstl:jstl:1.2"
 
   tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
   tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
       exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
       cargo "org.codehaus.cargo:cargo-core-uberjar:${cargoVersion}",  "org.codehaus.cargo:cargo-ant:${cargoVersion}"
   }
 
 
}
 
cargo {
    containerId = 'tomcat7x'
    port = 8080
    // 7.0.26 has problme with jdk8, using 7.0.30
    local {
        installer {
            //installUrl = "http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.26/bin/apache-tomcat-7.0.26.zip"
            installUrl = "http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.30/bin/apache-tomcat-7.0.30.zip"
            downloadDir = file("$buildDir/download")
            extractDir = file("$buildDir/extract")
        }
        outputFile = file('container/apache-tomcat-7.0.30/output2.log')
    }
}
 
 
war {   
    version = '' 
 }
 
 
task finalize << {
    println('after build')
    copy{
        from "${buildDir}\\libs"
        into "$buildDir/extract\\apache-tomcat-7.0.30\\apache-tomcat-7.0.30\\webapps"
        include '*.war'
      }
}
 
build.finalizedBy(finalize)
 
 

You can deploy the application by running task cargoRunLocal, but I suggest not to do in this way, you can manually start and stop Catalina Server, see Gradle Cargo tomcat deployment problem.

The script will deploy the WAR file after build. If there are no JSP file change, you don't even need to restart Tomcat, because Tomcat 7 can auto redeploy WAR if its updated. See How to force update jsp file in war in Tomcat.

Step 2. Configure Spring MVC

From Spring 3, there is another way to configure Spring without XML file, instead we use annotation.

ExampleWebApplicationInitializer.java

 
 
package com.example.springcontext;
 
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 
public class ExampleWebApplicationInitializer implements WebApplicationInitializer {
 
    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
 
        registerDispatcherServlet(servletContext);
    }
 
    private void registerDispatcherServlet(ServletContext servletContext) {
 
        AnnotationConfigWebApplicationContext dispatcherContext = createContext(WebConfig.class);
        ServletRegistration.Dynamic dispatcher;
 
        dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
        dispatcher.addMapping("*.do");
 
    }
 
    private AnnotationConfigWebApplicationContext createContext(final Class<?>... annotatedClasses) {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(annotatedClasses);
        return context;
    }
 
}
 

What is do is exactly the same as a regular web.xml file. In this case, Spring will handle the home page and any url ends with .do

Next is configuration for Spring MVC components, basically it will tells Spring where to look for components like controllers, views, or models

WebConfig

 
 
package com.example.springcontext;
 
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.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
 
@Configuration
@EnableWebMvc
@ComponentScan(basePackages="com.example.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
 
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver resolver =  new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
 
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }
 
}
 

This file tells Spring to look for controllers in package com.example.controller, look for views in /WEB-INF/jsp/.

It also specified that all static files stay in /resources directory.

Step 3. Controller

A controller will handle all request in this application.

 
package com.example.controller;
 
import java.util.Date;
 
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.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
 
@Controller
public class IndexController {
 
  @RequestMapping("/")
  public ModelAndView helloWorld() {
    return new ModelAndView("hello");
  }
 
  @RequestMapping(value = "/ajax", method = RequestMethod.GET)
  public @ResponseBody String getGreeting(@RequestParam String name) {
    String result = "Hello! " + name + ". Time for now is " + new Date().toString();
    return result;
  }
}
 

There are only two request uri here, one for home page, another for handling AJAX request. When you send request the uri should be ajax.do

The home page handler simply return a view, which will be resolved to /WEB-INF/jsp/hello.jsp

Step 4. Add a view

The view is a simple JSP file, contains a button and a div will accept AJAX response.

 
<html>
<head>
  <title>Spring MVC AJAX Example</title>
  <link href="resources/style.css" rel="stylesheet" type="text/css" /> 
  <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
  <script type="text/javascript">
    $(function() {
      $('.ajax-button').click(function() {
        $.ajax({
          url: 'ajax.do',
          data: ({name : 'sam'}),
          success: function(data) {
            $('#response').html(data);
          }
        });
      });
    });
  </script>
</div>
</head>
<body>
<div>
  <p  style="text-align: center; margin: 40px 0;">
      <a class="ajax-button makble_button_large_green">Send Ajax request</a></p>
</div>
<div id="textbox-style"> 
  <div id="response" style="text-align:center">
  </div>   
</div> 
</body>
</html>
 
 

And some css

 
 
.makble_button_large_green {
    padding: 13px 0;
    font-size: 20px;
    line-height: 20px;
}
 
.makble_button_large_green {
    background: #e76700;
    color: #FFF !important;
    text-align: center;
    font-family: 'Open Sans', sans-serif;
    font-size: 14px;
    line-height: 20px;
    text-transform: uppercase;
    font-weight: 600;
    border: 1px solid #3f9d00;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
    background-image: -moz-linear-gradient(bottom, #489801, #71d000);
    background-image: -ms-linear-gradient(bottom, #489801, #71d000);
    background-image: -o-linear-gradient(bottom, #489801, #71d000);
    background-image: linear-gradient(to top, #489801, #71d000);
    padding: 5px 10px;
    display: inline-block;}
 
.makble_button_large_green:hover {
    color: #FFF !important;
    background-image: -moz-linear-gradient(bottom, #71d000, #489801);
    background-image: -ms-linear-gradient(bottom, #71d000, #489801);
    background-image: -o-linear-gradient(bottom, #71d000, #489801);
    background-image: linear-gradient(to top, #71d000, #489801);
}
 
#textbox-style {
    width: auto!important;
    height: auto!important;
    margin: 0 auto;
    padding: 20px!important;
    border: solid rgba(255, 255, 255, .5)!important;
    border-width: 1px 0!important;
    overflow: hidden;
    text-align: left;
    font-size: 13px!important;
    line-height: 16px!important;
    background: transparent!important;
    box-shadow: 0 0 10px 5px rgba(0, 0, 0, .4)
}
 
#textbox-style .textbox-inner-style-wrap .textbox-inner-style-img {
  margin: 0!important;
  display: block;
  float: left;
}
 
#textbox-style .textbox-inner-style-wrap .textbox-inner-style-text,
#textbox-style .textbox-inner-style-poweredby {
  display: block!important;
  float: none!important;
  width: auto!important;
  height: auto!important;
  margin-left: 145px!important;
    font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif!important;
    color: white!important;
}
 
#textbox-style .textbox-inner-style-wrap .textbox-inner-style-text {
    padding-top: 0!important;
    color: rgba(255, 255, 255, .8)
}
 
#textbox-style .textbox-inner-style-poweredby {
    text-align: left!important;
    color: inherit!important;
  margin-top: 10px!important;
}
 
#textbox-style {
    width: 330px!important;
    margin: 0 auto;
    padding: 15px!important
}
 

Step 5. Build and run

In Eclipse, select Gradle Build dialog and select tasks::build and :war. Then go to the root directory of your Tomcat server and run catalina.bat run. If everything is OK, you will see this

When you click the button, the response is updated to the text block.

Conclusion

Now you should have a basic understanding about how to add AJAX feature in Spring framework, you need a basic configuration for Spring MVC and implement a handler in controller, and send request from page using jQuery's ajax method.

As you can see from this example, its easy to support AJAX in Spring.

See also