/spring-tutorial

Spring tutorial project using Spring Boot and Spring Cloud

GNU General Public License v3.0GPL-3.0

Spring Boot demo

How to create your own

Create skeleton

You have two options for this, either use the Spring Initializr website on Spring.io or use the Initializr plugin in your Java IDE. Both Eclipse and IntelliJ have this option.

Spring.io

Browse to start.spring.io and use the wizard to create a project.

Spring Initializr

IDE Plugin

Select the Spring Initializr and the JDK version. This is the wizard in IntelliJ.

Enter the Group and ArtifactId, language and Java Version along with application info. This is typically info entered in the Maven POM

Choose which Spring dependencies you would like to add, most commonly Web. You can add extra dependencies later if needed. For this example, we will add Web.

Enter the directories where the project should be created

This results in a project with the following content

Main SpringBootApplication class

The skeleton project contains an Application class annotated with @SpringBootApplication to bootstrap the application. In our example, this is the class DemoApplication. By placing this class in the main package, each class within that package and any sub-packages will be component-scanned and picked up by Spring to create Spring Beans.

Controller

Create a controller-class with annotation @RestController. Create a method with a mapping that serves the root-url:

@RestController
public class SimpleController {
    @GetMapping("/")
    String hello() {
        return "Hello from Spring Boot!";
    }
}

This completes the minimal Hello World web-app using Spring Boot. Let's startit.

How to Start

These commands are all run from your terminal application

Maven:

mvn spring-boot:run

Gradle:

gradle bootRun

Java:

  • Build a jar using
mvn package
  • Start the jar using
java -jar demo.jar
  • Or if you on *nix systems (Linux, Mac OSX or UNIX), you can instead run jars natively:
chmod 755 demo.jar
./demo.jar

Start the application and open the url to localhost.

Dev-tools

Let's add a plugin that adds support for several convenient tools, like code-insight in the application.properties. Add the following dependency to the POM:

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

Persistence

Here things start to get interesting. Most application have some kind of persistence. We will add support for an in-memory database and create classes that interact with this database. This will include Hibernate, JPA and H2.

Let's start by adding the following dependencies to the POM:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Notice that you might need to add JAXB-API for projects using JDK 9+, since this was removed / deprecated.

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>

Entity

Create a POJO class with the following annotations and generate simple getters and setters

@Entity
public class Movie {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private LocalDate release;
    private String imdb;

    public Movie(String name, LocalDate release, String imdb) {
        this.name = name;
        this.release = release;
        this.imdb = imdb;
    }
}

The @Entity annotation will tell Spring to use this class as a Model-class. @Id indicates the identifier field and @GeneratedValue is used for derived values, like primary keys. These will typically be filled in using database sequences.

Repository

Create an interface called MovieRepository. By extending the Generic CrudRepository and configuring the correct Entity and Id, Spring will create an implementation we can use with methods to find, create, save and delete entities of this type.

public interface MovieRepository extends CrudRepository<Movie, Long> {
  // No code needed
}

To explicitly create a find method that takes a specific entity field, you can add a method declaration like this:

  List<Movie> findByName(String name);

Note that you don't have to implement the method itself. The format of the method-call is used by Spring to inject the actual implementation. Spring JPA can also combine queries with methods like:

  List<Movie> findByNameAndAndRelease(String name, LocalDate release);

Finding entities by comparing a field with in-between:

  List<Movie> findByReleaseBetween(LocalDate start, LocalDate end);

For an overview of the options, review the Spring Query method reference.

Seed-data

Let's create some entities to pre-fill the database. You can create a class annotated with @Component, with the MovieRepository that is annotated with an @Autowired. This will auto-inject that bean into the class, so we can use it.

@Component
public class Bootstrap {
    @Autowired
    MovieRepository movieRepository;

    public void prefillDatabase() {
        movieRepository.saveAll(Arrays.asList(
                new Movie("Iron Man", LocalDate.of(2008, 5, 2), "https://www.imdb.com/title/tt0371746/"),
                new Movie("The Incredible Hulk", LocalDate.of(2008, 6, 13), "https://www.imdb.com/title/tt0800080/")
                // Etc
        ));
    }
}

Next you can add this Bootstrap class to the main Application and make sure it's called. Inject it with the @Autowired annotation. Make the main Application class implement the interface CommandLineRunner and implement the runmethod with the following body:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    Bootstrap bootstrap;

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

    @Override
    public void run(String... args) throws Exception {
        bootstrap.prefillDatabase();
    }
}

We are using this data both for testing purposes and as a starting point for demoing different aspects of the application. You could also call the prefill-code in a test-setup if you strictly use it for automated testing.

Testing

Let's create a Test-class. In folder main/test/groovy create a package com.spring.boot.demo.repositories. Within this package, create a Groovy-class called MovieRepositoryTest with the following code:

@ContextConfiguration
@SpringBootTest
class MovieRepositoryTest extends Specification {

    @Autowired
    MovieRepository movieRepository

    @Unroll
    def "findByNameContaining_should find #expected movies when searching for '#query'"() {
        given:
        movieRepository

        when:
        def result = movieRepository.findByNameContaining(query)

        then:
        result.size() == expected

        where:
        query       ||  expected
        "Iron Man"  ||  3
        "Hulk"      ||  1
        "Thor"      ||  3
        "Avengers"  ||  3
        "Thanos"    ||  0
        ""          ||  20
    }
}

Pay close attention to the annotations within this code. Since this is Groovy code, we can do some cute things. The method name is a string, with some replacement variables within it. Spock enables us to use labeled blocks, like given, when, then. These indicate different functional parts of the test.

label function
given setup
when stimulus: if this code is executed...
then condition: ... this should be true
where parameters: with these values

Refer to the Spock documentation for more details.

In our example, we execute the same code several times using a different query. This should always result in a list, but with different lengths. Each line in the where-table indicates a test with a different query and the corresponding expected size of the resulting list. And then there is the Groovy magic of the method name. You can see the variables, which will be replaced with the parameters in the where-table. Run the following Maven target:

Maven:

mvn clean test

Or run the specification from IntelliJ. This will give you the following test-results: