Mathias Hauser

My opinions and thoughts on various things and experiences of software development

Bootstrap an application with Spring Boot – Part2 Web application

Welcome to part 2 of “Bootstrap an application with Spring Boot” which is about web applications with Spring Boot. I use the source code from part 1 as basis. You might want to read part1 before you start with this post, because I don’t repeat explanations from part1.

Web application with Spring Boot

As mentioned in part1, most examples for Spring Boot are web based applications. I’ve read many different ones. Most of them tell a different story. I try to give an easy setup for your application which works out for me. I use AngularJS in this post, but keep in mind that this post is not about AngularJS and I just use a minimal approach as an example for possible front end technologies.

I recommend downloading the source of this example before you start. Link to the repository blog_springBoot-web direct link to the source zip source-zip

Key points of this post

  1. Build- and Dependencymanagement -> build.gradle -> in the project root
  2. Application configuration -> Application.java; WebXml.java; application.yml
  3. REST-Interface -> HelloController
  4. Testing -> HelloControllerTests
  5. Deployment

Build- and Dependencymanagement -> build.gradle

Spring Boot uses per default a jar, but I think it’s a better to use a war which is also supported. The buildscript block plus the spring-boot plugin is from the Spring Boot documentation which extends the build process of gradle and allows starting the application with an embedded Tomcat from the command-line (Deployment section).
One Dependency has changed and there are some additional dependencies for testing. The “spring-boot-starter” dependency is replaced by “spring-boot-starter-web” which is kind of obviouse responsible for web features. The other new dependencies are for testing purpose – “spring-boot-starter-test” integrates everything which is required to test an application with spring boot the other two are hsqldb and junit which are common for test purpose.

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        maven { url "http://repo.spring.io/libs-snapshot" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.0.RC1")
    }
}

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
apply plugin: 'idea'
apply plugin: 'spring-boot'

version = '1.0'
group = 'mh.dev.blog'
description = 'bootstrap-spring-boot'

war {
    baseName = 'hello'
    version =  '0.1.0'
}

project.ext {
	springBootVersion = '1.0.0.RC1'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/libs-snapshot" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion")
    compile("org.apache.commons:commons-lang3:3.2.1")
    compile("com.google.guava:guava:16.0.1")
    compile("org.yaml:snakeyaml:1.13")
    compile("mysql:mysql-connector-java:5.1.28")
	
    testCompile("org.springframework.boot:spring-boot-starter-test:$springBootVersion")
    testCompile("org.hsqldb:hsqldb:2.3.1")
    testCompile("junit:junit:4.11")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.10'
}

Application configuration -> Application.java, WebXml.java and application.yml

The configuration part does not change that much compared to the command-line application. The only change of the Application.java class is that I added @Configuration to this class. It’s not need in every situation, but to be honest I have no idea why it works in some situations without it and in some not. Just add it to be safe.

package mh.dev.blog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@ComponentScan
@EnableJpaRepositories
@EnableAutoConfiguration
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

This file is completely new and replaces the xml based web.xml file in the WEB-INF folder. I added the @Configuration annotation to this class. This allows the Spring context to find it. The important part is extends SpringBootServletInitializer, nothing more is needed at this location. Seems too easy, but welcome to the magic of Spring Boot.

package mh.dev.blog.config;

import mh.dev.blog.Application;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebXml extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(Application.class);
	}
}

There are larger changes in the application.yml file. The main reason for this is that we need multiple database configurations for development and testing. This file contains 3 different configurations which are separated with “—“. The first part is the general configuration, which defines that development is the default configuration. The other configuration is the test profile which is used for testing. The only additional attribute in this configuration is the “web-environment: true” which enables web capabilities.

## based on example application.yml -> https://github.com/spring-projects/spring-boot/blob/6b83e0ad5de5f127cad5b12ab1738923c4a58a9f/docs/application.yml

spring:
   application.name: word
   profiles.active: development
   main:
     web-environment: true

---

spring:
  profiles: development

  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/blog
    username: root
    password: root
  
  jpa:
     show-sql: true
     hibernate:
       ddl-auto: update
       
---

spring:
  profiles: test

  datasource:
    driverClassName: org.hsqldb.jdbc.JDBCDriver
    url: jdbc:hsqldb:mem:blog
    username: sa
    password:
  
  jpa:
    open-in-view: true
    show-sql: true
    hibernate:
      ddl-auto: update

REST-Interface

The trend in web development goes to more and more applications which use a REST-Interface as backend and an independent technology for the front-end like AngularJS.
I think it is not required to give a deeper explanation of this class because it’s standard REST with Spring you can find many different tutorials out there which explain it in more detail.
What we have here are two methods one to create new words and one to return all with JSON strings as response.

package mh.dev.blog.controller;

import java.util.List;

import mh.dev.blog.model.Word;
import mh.dev.blog.service.WordService;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "hello")
public class HelloController {

	@Autowired
	private WordService wordService;

	private Logger log = Logger.getLogger(HelloController.class);

	@RequestMapping(method = RequestMethod.POST)
	@ResponseBody
	public Word create(@RequestBody String text) {
		log.info(String.format("Try to add a Word with text %s to the database", text));
		Word word = wordService.byText(text);
		if (word == null) {
			word = wordService.createWord(text);
		}
		return word;
	}

	@RequestMapping
	@ResponseBody
	public List<Word> words() {
		log.info("Return all words");
		return wordService.all();
	}
}

Testing

Tests are a fundamental part of software development. There are not so much examples out there for testing with Spring Boot so I decided to include this in this post.
The test in this code snipped is an integration test, but keep in mind that this class does not test the REST-Interface. The annotations are the most important part on top of this class.

  1. @WebAppConfiguration – important because we have web project
  2. @ActiveProfiles(value = “test”) – tells spring that we want to use the test profile; another possibility would be a run argument
  3. @Transactional – we want transactions
  4. @TransactionConfiguration(defaultRollback = true) – each testcase should be independant -> rollback after each test
  5. @RunWith(SpringJUnit4ClassRunner.class) – junit runner
  6. @SpringApplicationConfiguration(classes = { Application.class }) – Use the configuration from our Application class
package mh.dev.blog.test.controller;

import java.util.List;

import mh.dev.blog.Application;
import mh.dev.blog.controller.HelloController;
import mh.dev.blog.model.Word;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;

@WebAppConfiguration
@ActiveProfiles(value = "test")
@Transactional
@TransactionConfiguration(defaultRollback = true)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class })
public class HelloControllerTests {

	@Autowired
	private HelloController helloController;

	@Test
	public void addTest() {
		Assert.assertEquals(0, helloController.words().size());
		Word word = helloController.create("hello");
		List>Word> words = helloController.words();
		Assert.assertEquals(1, words.size());
		Word actual = words.get(0);
		Assert.assertEquals(word.getId(), actual.getId());
		Assert.assertEquals(word.getText(), actual.getText());
	}
}

Deployment

A really importantpart is the question: “How can I run my application, and how do I get my war for a Tomcat?”.
There are different possibilities.

  • windows: gradlew bootRun unix: ./gradlew bootRun – starts an embedded Tomcat with your application which can be reached at http://localhost:8080/
  • Execution Application.java in Eclipse or IntelliJ – does the same as gradlew bootRun
  • Deploy the application in an dedicated Tomcat – deploys the application at http://localhost:8080/ with the context path in this case hello which is defined by the basename in the build.gradle -> http://localhost:8080/hello
  • windows: gradlew clean build unix: ./gradlew clean build – generates a war in build\libs\basename-version.war.original. For this application build\libs\hello-0.1.0.war.original. You should not use the none original version

How the application looks like

I didn’t explained the view part of this application. The reason for this is that you can use many different web technologies in combination with Spring Boot, I used AngularJS. Spring Boot automatically integrates the content of resources/static in the root of the war. For more details look at the code which is accessible at github.
blog_springBoot-web-view

Advertisements

5 responses to “Bootstrap an application with Spring Boot – Part2 Web application

  1. davidf76 March 17, 2014 at 7:13 pm

    Hi! Congrats, nice tutorial. But, how can we do for not to package inside war the application.properties file with username and password?. I prefer instead use jndi tomcat datasource. Is there any clean alternative? I suppose you can create three profiles: dev, prod with jar and prod with war. For the last one I don’t want database urls, password, etc.
    Thanks in advance!

  2. Pavel August 20, 2014 at 11:51 am

    So how to test REST interface?!

    • mhdevelopment August 20, 2014 at 12:20 pm

      I don’t have a blog post right now which explains this, but I try to give you an example code how you can do this with MockMvc from Spring.
      The following snipped is just a simple example how you can do this. It contains a simpleTest which sends a Post request to a given adress with a dto object as content converted to json.
      Feel free to ask if something is unclear.

      @RunWith(SpringJUnit4ClassRunner.class)
      @SpringApplicationConfiguration(classes = Application.class)
      @Transactional
      @TransactionConfiguration(defaultRollback = true)
      @ActiveProfiles(“test”)
      public class RestTest {

      protected MockMvc mockMvc;
      protected ObjectMapper mapper;

      @Autowired
      protected WebApplicationContext wac;
      @Autowired
      private ApplicationContext context;

      @Before
      public void before() throws Exception {
      this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
      this.mapper = new ObjectMapper();
      }

      @Test
      public void simpleTest() throws Exception {
      Dto dto = new Dto();
      dto.setSomeValue(“value”);
      mockMvc.perform(post(“apiPath”) .contentType(MediaType.APPLICATION_JSON).content(toJson(dto)).accept(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(content().contentType(expectedContentType))
      .andExpect(jsonPath(“$.somepath”).exists())
      .andExpect(jsonPath(“$.somepath”).value(dto.getSomeValue()));
      }

      public String toJson(Object object) throws JsonProcessingException {
      return mapper.writeValueAsString(object);
      }
      }

  3. Rockey 123 August 21, 2015 at 3:14 pm

    Hi ,very useful tutorials ,currently i am working on spring boot project can you provide me more resources like these (not documentation through ) thanks .

    • mhdevelopment August 21, 2015 at 4:55 pm

      Spring Boot on its own is not that complicated. What may makes it complicated is that you need to create the annotation setup ones for every peace of feature you need from spring. So it is hard to give advice to generic question. But the spring blog is usually really useful.

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

%d bloggers like this: