IK.AM

@making's tech note


Getting Started with Spring Boot 1.3 & DevTools

🗃 {Programming/Java/org/springframework/boot}
🏷 Java 🏷 Spring 🏷 Spring Boot 
🗓 Updated at 2015-07-11T02:14:20Z  🗓 Created at 2015-07-11T02:14:20Z   🌎 English Page

Yesterday, Spring Boot 1.3.0.M2 has been released!

In this article, I'd like to introduce how to start 1.3 and super cool new feature called DevTools (Blog, Doc).

How to start

Most easy way to create a Spring Boot project must be using SPRING INITIALIZER. Check modules you need and click "Generate Project", then a blank project will be downloaded.

For example, this link produces a simple Spring Boot 1.3.0.M2 project including the following modules:

  • web
  • thymeleaf
  • devtools

Some IDEs support generating projects by SPRING INITIALIZER.

SPRING INITIALIZER with Spring Tool Suite

Choose "File" -> "New" -> "Other" -> "Spring" -> "Spring Starter Project".

image

Select "1.3.0.M2" as "Boot Version" and check some modules from "Style" panel. In this demo, "web" and "thymeleaf" are chosen.

image

Finally, click "Finish", then demo project will be imported!

image

Right click DemoApplication and run as "Spring Boot Application".

image

Demo Application will be started!

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v1.3.0.M2)

2015-07-11 11:43:20.564  INFO 2403 --- [           main] demo.DemoApplication                     : Starting DemoApplication on making.local with PID 2403 (/Users/maki/sts/demo/target/classes started by maki in /Users/maki/sts/demo)

...(omitted)...

2015-07-11 11:43:24.011  INFO 2403 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-07-11 11:43:24.019  INFO 2403 --- [           main] demo.DemoApplication                     : Started DemoApplication in 3.71 seconds (JVM running for 4.263)

SPRING INITIALIZER with IntelliJ IDEA

Choose "File" -> "New" -> "Project" -> "Spring Initializer".

image

Input project information (Defaults are enough, so far).

image

Select "1.3.0.M2" as "Boot Version" and check some modules from "Dependencies" panel. In this demo, "web" and "thymeleaf" are chosen.

image

After that, it's a normal procedure as same as you usually do.

Try "Devtools Support"

I think most significant feature of 1.3 is "DevTools Support".

To use devtools, simply add the dependency in your pom.xml as follows:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

Of course, you can choose spring-boot-devtools in Spring Initializer.

This nice module enables the following features to help us in dev-phase:

  • Disabling template cache
  • Auto restart Class Loader
  • Live reload browser
  • Remote debug & restart

Disabling template cache

Before 1.3, template engines cache template. From the viewpoint of performance, it's good. However, in developoment, it's unconvenient because it requires restarting the application to reflect the change of a template.

To avoid cache, we had to set spring.thymeleaf.cache=false in application.properties and enabled cache at runtime like java -jar app.jar --spring.thymeleaf.cache=true.

By adding org.springframework.boot:spring-boot-devtools, the properties bellow are automatically configured:

  • spring.thymeleaf.cache=false
  • spring.freemarker.cache=false
  • spring.groovy.template.cache=false
  • spring.velocity.cache=false
  • spring.mustache.cache=false

Wonderfully, features from devtools are automatically disabled via java -jar app.jar.

So, we no longer need to toggle spring.thymeleaf.cache!!

Auto restart Class Loader

Look the next tiny application:

package demo;

import javax.annotation.PostConstruct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    @PostConstruct
    void demo() {
        System.out.println("****hello!****");
    }

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

Run this app:

2015-07-11 16:04:37.750  INFO 2570 --- [  restartedMain] demo.DemoApplication                     : Starting DemoApplication on making.local with PID 2570 (/Users/maki/sts/demo/target/classes started by maki in /Users/maki/sts/demo)
2015-07-11 16:04:37.795  INFO 2570 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@222f3c6a: startup date [Sat Jul 11 16:04:37 JST 2015]; root of context hierarchy
... (omitted) ...
2015-07-11 16:04:39.838  INFO 2570 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2046 ms
2015-07-11 16:04:40.642  INFO 2570 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2015-07-11 16:04:40.648  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-07-11 16:04:40.649  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2015-07-11 16:04:40.649  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'resourceUrlEncodingFilter' to: [/*]
****hello!****
2015-07-11 16:04:41.003  INFO 2570 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: 
... (omitted) ...
2015-07-11 16:04:41.612  INFO 2570 --- [  restartedMain] demo.DemoApplication    

As you can see, thread name has been changed from main to restartedMain. Once the app start, it will be restarted according to the change of class file.

Let's change to System.out.println("****Spring Boot!****"); in demo method.

Save & Compile the source code, then you can see the console log is output and find the change are reflected.

2015-07-11 16:08:31.264  INFO 2570 --- [       Thread-4] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@222f3c6a: startup date [Sat Jul 11 16:04:37 JST 2015]; root of context hierarchy
2015-07-11 16:08:31.269  INFO 2570 --- [       Thread-4] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v1.3.0.M2)

2015-07-11 16:08:31.524  INFO 2570 --- [  restartedMain] demo.DemoApplication                     : Starting DemoApplication on making.local with PID 2570 (/Users/maki/sts/demo/target/classes started by maki in /Users/maki/sts/demo)
2015-07-11 16:08:31.528  INFO 2570 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6d20eb7f: startup date [Sat Jul 11 16:08:31 JST 2015]; root of context hierarchy
... (omitted) ...
2015-07-11 16:08:32.008  INFO 2570 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 480 ms
2015-07-11 16:08:32.165  INFO 2570 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2015-07-11 16:08:32.166  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-07-11 16:08:32.166  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2015-07-11 16:08:32.166  INFO 2570 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'resourceUrlEncodingFilter' to: [/*]
****Spring Boot!****
2015-07-11 16:08:32.220  INFO 2570 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: 
... (omitted) ...
2015-07-11 16:08:32.356  INFO 2570 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-07-11 16:08:32.359  INFO 2570 --- [  restartedMain] demo.DemoApplication                     : Started DemoApplication in 0.867 seconds (JVM running for 235.486)

Time to restart looks much faster than the initial spin up time (In this demo, it took only 0.867 sec). The reason why is described in the manual.

Demo

AutoRestart

Live reload browser

Amazingly, devtools embeds a LiveReload server which trigger a browser refresh when the resources are changed.

To use this functionality, LiveReload browser extension is required. You can install from http://livereload.com/extensions/ .

To show the demonstration, I use the following small web application:

  • DemoApplication.java
package demo;

import java.time.LocalDateTime;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@SpringBootApplication
@Controller
public class DemoApplication {

    @RequestMapping("/")
    String index(Model model) {
        model.addAttribute("hello", "Hello! @" + LocalDateTime.now());
        return "index";
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title></title>
<link rel="stylesheet" th:href="@{/style.css}"></link>
</head>
<body>
    <p>
        <span th:text="${hello}">Hello!</span>
    </p>
</body>
</html>
  • style.css
body {
    font-size: 12px;
}

Take a look at the demo :)

Note that I did not refresh the browser by myself at all.

LiveReload

The combination of AutoRestart & LiveReload is awesome!

AutoRestart & LiveReload

Remote debug & restart

Devtools enable us to access deployed remote application. You can debug and auto-restart the remote app via org.springframework.boot.devtools.RemoteSpringApplication.

To know the details, refer the manual :)


In Spring Boot 1.3, not only devtools, other interesting features are alse added.

Try Spring Boot 1.3 & DevTools and have a fun ;)


✒️️ Edit  ⏰ History  🗑 Delete