Spring Boot

Dave Syer, Phil Webb, 2013
Twitter: @david_syer, @phillip_webb
Email: [dsyer, pwebb]@gopivotal.com

(Introduction to Spring Boot)

Spring IO

Agenda

Focus Attention

Introduction

Spring Boot:

An opportunity for Spring to be opinionated

Installation

$ spring --help
...

(Or follow instructions on Github for GVM or Brew.)

Getting Started Really Quickly

@RestController
class Example {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

}
$ spring run app.groovy



... application is running at http://localhost:8080

What Just Happened?

// import org.springframework.web.bind.annotation.RestController
// other imports ...

@RestController
class Example {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

}

What Just Happened?

// import org.springframework.web.bind.annotation.RestController
// other imports ...

// @Grab("org.springframework.boot:spring-boot-web-starter:0.5.0")
@RestController
class Example {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

}

What Just Happened?

// import org.springframework.web.bind.annotation.RestController
// other imports ...

// @Grab("org.springframework.boot:spring-boot-web-starter:0.5.0")
// @EnableAutoConfiguration
@RestController
class Example {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

}

What Just Happened?

// import org.springframework.web.bind.annotation.RestController
// other imports ...

// @Grab("org.springframework.boot:spring-boot-web-starter:0.5.0")
// @EnableAutoConfiguration
@RestController
class Example {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

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

Getting Started in Java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;

@RestController
@EnableAutoConfiguration
public class MyApplication {

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

}

What Just Happened?

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class MyApplication {

  @RequestMapping("/")
  public String sayHello() {
    return "Hello World!";
  }

  ...

}

Starter POMs

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

SpringApplication

SpringApplication app = new SpringApplication(MyApplication.class);
app.setShowBanner(false);
app.run(args);

@SpringBootApplication and Auto Configuration

@SpringBootApplication
public class MyApplication {
}

Packaging For Production

Maven plugin (using spring-boot-starter-parent):

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
$ mvn package

Gradle plugin:

apply plugin: 'spring-boot'
$ gradle repackage

Packaging For Production

$ java -jar yourapp.jar

Spring Boot Modules

Spring Boot Module Relations

Spring Boot Modules

Not a Web Application?

@Component
public class Startup implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Hello World");
    }

}

SpringApplicationBuilder

Flexible builder style with fluent API for building SpringApplication with more complex requirements.

new SpringApplicationBuilder(ParentConfiguration.class)
    .profiles("adminServer", "single")
    .child(AdminServerApplication.class)
    .run(args);

Environment and Profiles

Command Line Arguments

@Value("${name}")
private String name;
$ java -jar yourapp.jar --name=Dave
$ java -jar target/*.jar --server.port=9000

Externalizing Configuration to Properties

Just put application.properties in your classpath or next to you jar, e.g.

application.properties

server.port: 9000

Properties can be overridden (command line arg > file > classpath)

Using YAML

Just include snake-yaml.jar and put application.yml in your classpath

application.yml

server:
  port: 9000

Both properties and YAML add entries with period-separated paths to the Spring Environment.

Binding Configuration To Beans

MyProperties.java

@ConfigurationProperties(prefix="mine")
public class MyPoperties {
    private Resource location;
    private boolean skip = true;
    // ... getters and setters
}

application.properties

mine.location: classpath:mine.xml
mine.skip: false

Data Binding to @ConfigurationProperties

Also binds to SpringApplication

Customizing Configuration Location

Set

e.g.

$ java -jar target/*.jar --spring.config.name=production

Spring Profiles

Logging

Adding some Autoconfigured Behavior

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
</dependency>

Extend the demo and see what we can get by just modifying the classpath, e.g.

Adding Static Resources

Easiest: use classpath:/static/**

Many alternatives:

Adding A UI with Thymeleaf

Currently Available Autoconfigured Behaviour

Please open an issue on github if you want support for something else

The Actuator

Adds common non-functional features to your application and exposes MVC endpoints to interact with them.

If embedded in a web app or web service can use the same port or a different one (management.port) and/or a different network interface (management.address) and/or context path (management.context_path).

Adding Security

Adding a Remote SSH Server

Building a WAR

We like launchable JARs, but you can still use WAR format if you prefer. Spring Boot Tools take care of repackaging a WAR to make it executable.

If you want a WAR to be deployable (in a "normal" container), then you need to use SpringBootServletInitializer instead of or as well as SpringApplication.

Customizing Business Content

Remember, it's just Spring...

Customizing the ApplicationContext

Customizing @EnableAutoConfiguration

Customizing the CLI

Uses standard Java META-INF/services scanning

E.g. can add script commands (written in Groovy)

$ spring foo ...

Looks for foo.groovy in ${SPRING_HOME}/bin and ${SPRING_HOME}/ext by default

Customizing Servlet Container Properties

Spring Boot Loader

Motivation: existing solutions for executable JAR are not very robust; executable WAR is very tricky to create.

Response: JarLauncher and WarLauncher with specialized ClassLoader and JarFile implementations that can find resources in nested JARs (e.g. lib/*.jar or WEB-INF/lib/*.jar)

How We Load Nested Jars

Each regular JAR file is sequence of JarEntries

yourapp.original.jar

+----+----+----+-------
| A1 | A2 | A3 | ...
+----+----+----+-----

spring-core.jar

+----+----+----+-------
| S1 | S2 | S3 | ...
+----+----+----+-----

How We Load Nested Jars

With nested JARs entries are contained within entries.

yourapp.jar

+----+----+----+-+-------------------------
|    |    |    | +----+----+----+-------
| A1 | A2 | A3 | | S1 | S2 | S3 | ...
|    |    |    | +----+----+----+-------
+----+----+----+-+--------------------

How We Load Nested Jars

We can scan nested JARs and simply seek to the correct part of the outer file when reading a nested entry.

yourapp.jar

+----+----+----+-+-------------------------
|    |    |    | +----+----+----+-------
| A1 | A2 | A3 | | S1 | S2 | S3 | ...
|    |    |    | +----+----+----+-------
+----+----+----+-+--------------------
^    ^    ^      ^    ^    ^

NOTE: In order to seek inside the nested JAR, the containing entry cannot be compressed.

Spring Boot Loader Limitations

You don't need to use it, consider shade or a classic WAR

Testing with Spring Test (and MVC)

SpringApplication is an opinionated creator of an ApplicationContext, but most of the behaviour is encapsulated in ApplicationContextInitializer implementations. To reproduce the behaviour of your app in an integration test it is useful to duplicate those features, so you can use the corresponding initializers, or you can use a context loader provided by Spring Boot.

Example with externalized configuration:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = IntegrationTestsConfiguration.class, 
    loader = SpringApplicationContextLoader.class)
public class IntegrationTests {

  // Normal Spring Test stuff

}

Hint: use spring-boot-starter-test

Spring Boot Intro - END
#