The new Certification Class of Learn Spring Security is out:

>> CHECK OUT THE COURSE

1. Overview

Spring Boot is a significant addition to the Spring ecosystem. In this tutorial, we’ll discuss how to bootstrap a simple application using Spring Boot.

This tutorial is a starting point for Boot – a way to get started in a simple manner, with a basic web application.

We will go over some core configuration, a front-end, quick data manipulation, and exception handling.

2. Setup

First, let’s use Spring Initializr to generate the base for our project.

The generated project relies on the Boot parent:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
    <relativePath />
</parent>

The initial dependencies are going to be quite simple:

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

3. Application Configuration

Next, we’ll configure a simple main class for our application:

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

Notice how we’re using @SpringBootApplication as our primary application configuration class; behind the scenes, that’s equivalent to @Configuration, @EnableAutoConfiguration and @ComponentScan together.

Finally, we’ll define a simple application.properties file – which for now only has one property:

server.port=8081

server.port changes the server port from the default 8080 to 8081; there are of course many more Spring Boot properties available.

4. Simple Front-End

Let’s now add a simple front end using Thymeleaf.

First, we need to add the spring-boot-starter-thymeleaf dependency to our pom.xml:

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

That enables Thymeleaf by default – no extra configuration is necessary.

We can now configure it in our application.properties:

spring.thymeleaf.cache = false
spring.thymeleaf.enabled=true 
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

spring.application.name = Bootstrap Spring Boot

Next, we’ll define a simple controller and a basic home page – with a welcome message:

@Controller
public class SimpleController {
    @Value("${spring.application.name}")
    String appName;

    @RequestMapping("/")
    public String homePage(Model model) {
        model.addAttribute("appName", appName);
        return "home";
    }
}

Finally, here is our home.html:

<html>
<head><title>Home Page</title></head>
<body>
<h1>Hello !</h1>
<p>Welcome to <span th:text="${appName}">Our App</span></p>
</body>
</html>

Note how we used a property we defined in our properties – and then injected that so that we can show it on our home page.

5. Security

Next, let’s add security to our application – by first including the security starter:

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

By now, you’re hopefully noticing a pattern – most Spring libraries are easily imported into our project with the use of simple Boot starters.

Once the spring-boot-starter-security dependency on the classpath of the application – Basic Authentication is enabled by default.

And, just as before, we’re going to do some simple configuration in our application.properties:

security.basic.enabled=true
security.user.name=john
security.user.password=123

If we don’t specify credentials explicitly – a default username will be used and password will be randomly generated by Boot at startup.

Of course, Spring Security is an extensive topic and one not easily covered in a couple of lines of configuration – so I definitely encourage you to go deeper into the topic.

6. Simple Persistence

On to persistence.

Let’s start by defining our data model – a simple Book entity:

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(nullable = false, unique = true)
    private String title;

    @Column(nullable = false)
    private String author;
}

And its repository, making good use of Spring Data here:

public interface BookRepository extends CrudRepository<Book, Long> {
    List<Book> findByTitle(String title);
}

Finally, we need to of course configure our new persistence layer:

@EnableJpaRepositories("org.baeldung.persistence.repo") 
@EntityScan("org.baeldung.persistence.model")
@SpringBootApplication 
public class Application {
   ...
}

Note that we’re using:

  • @EnableJpaRepositories to scan the specified package for repositories
  • @EntityScan to pick up our JPA entities

To keep things simple, we’re using an H2 in-memory database here – so that we don’t have any external dependencies when we run the project.

Once we include H2 dependency, Spring Boot auto-detects it and sets up our persistence with no need for extra configuration, other than the datasource properties:

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:bootapp;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=

Of course, like security, persistence is a broader topic than this basic set here, and one you should certainly explore further.

7. Web and the Controller

Next, let’s have a look at a web tier – and we’ll start that by setting up a simple controller – the BookController.

We’ll implement basic CRUD operations exposing Book resources with some simple validation:

@RestController
@RequestMapping("/api/books")
public class BookController {

    @Autowired
    private BookRepository bookRepository;

    @GetMapping
    public Iterable findAll() {
        return bookRepository.findAll();
    }

    @GetMapping("/title/{bookTitle}")
    public List findByTitle(@PathVariable String bookTitle) {
        return bookRepository.findByTitle(bookTitle);
    }

    @GetMapping("/{id}")
    public Book findOne(@PathVariable Long id) {
        Book book = bookRepository.findOne(id);
        if (book == null)
          throw new BookNotFoundException();
        return book;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Book create(@RequestBody Book book) {
        return bookRepository.save(book);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        Book book = bookRepository.findOne(id);
        if (book == null)
            throw new BookNotFoundException();
        bookRepository.delete(id);
    }

    @PutMapping("/{id}")
    public Book updateBook(@RequestBody Book book, @PathVariable Long id) {
        if (book.getId() != id) {
            throw new BookIdMismatchException();
        }
        Book old = bookRepository.findOne(id);
        if (old == null) {
            throw new BookNotFoundException();
        }
        return bookRepository.save(book);
    }
}

Given this aspect of the application is an API, we made use of the @RestController annotation here – which equivalent to a @Controller along with @ResponseBody – so that each method marshalls the returned resource right to the HTTP response.

Just one note worth pointing out – we’re exposing our Book entity as our external resource here. That’s fine for our simple application here, but in a real-world application, you will likely want to separate these two concepts.

8. Error Handling

Now that the core application is ready to go, let’s focus on a simple centralized error handling mechanism using @ControllerAdvice:

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ BookNotFoundException.class })
    protected ResponseEntity<object> handleNotFound(
      Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, "Book not found", 
          new HttpHeaders(), HttpStatus.NOT_FOUND, request);
    }

    @ExceptionHandler({ BookIdMismatchException.class, 
      ConstraintViolationException.class, 
      DataIntegrityViolationException.class })
    public ResponseEntity<object> handleBadRequest(Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, ex.getLocalizedMessage(), 
          new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
    }
}

Beyond the standard exceptions we’re handling here, we’re also using a custom exception:

BookNotFoundException:

public class BookNotFoundException extends RuntimeException {

    public BookNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
    // ...
}

This should give you an idea of what’s possible with this global exception handling mechanism. If you’d like to see a full implementation, have a look at the in-depth tutorial.

Note that Spring Boot also provides an /error mapping by default. We can customize its view by creating a simple error.html:

<html lang="en">
<head><title>Error Occurred</title></head>
<body>
    <h1>Error Occurred!</h1>    
    <b>[<span th:text="${status}">status</span>]
        <span th:text="${error}">error</span>
    </b>
    <p th:text="${message}">message</p>
</body>
</html>

Like most other aspects in Boot, we can control that with a simple property:

server.error.path=/error2

9. Testing

Finally, let’s test our new Books API.

We’ll immediately make use of @SpringBootTest to load the application context:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Application.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
public class LiveTest {

    private static final String API_ROOT
      = "http://localhost:8081/api/books";

    @Before 
    public void setUp() { 
        RestAssured.authentication = preemptive().basic("john", "123");
    }
    private Book createRandomBook() {
        Book book = new Book();
        book.setTitle(randomAlphabetic(10));
        book.setAuthor(randomAlphabetic(15));
        return book;
    }

    private String createBookAsUri(Book book) {
        Response response = RestAssured.given()
          .contentType(MediaType.APPLICATION_JSON_VALUE)
          .body(book)
          .post(API_ROOT);
        return API_ROOT + "/" + response.jsonPath().get("id");
    }
}

First, we can try to find books using variant methods:

@Test
public void whenGetAllBooks_thenOK() {
    Response response = RestAssured.get(API_ROOT);
 
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
}

@Test
public void whenGetBooksByTitle_thenOK() {
    Book book = createRandomBook();
    createBookAsUri(book);
    Response response = RestAssured.get(
      API_ROOT + "/title/" + book.getTitle());
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertTrue(response.as(List.class)
      .size() > 0);
}
@Test
public void whenGetCreatedBookById_thenOK() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    Response response = RestAssured.get(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals(book.getTitle(), response.jsonPath()
      .get("title"));
}

@Test
public void whenGetNotExistBookById_thenNotFound() {
    Response response = RestAssured.get(API_ROOT + "/" + randomNumeric(4));
    
    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

Next, we’ll test creating a new book:

@Test
public void whenCreateNewBook_thenCreated() {
    Book book = createRandomBook();
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .post(API_ROOT);
    
    assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());
}

@Test
public void whenInvalidBook_thenError() {
    Book book = createRandomBook();
    book.setAuthor(null);
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .post(API_ROOT);
    
    assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode());
}

Update an existing book:

@Test
public void whenUpdateCreatedBook_thenUpdated() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    book.setId(Long.parseLong(location.split("api/books/")[1]));
    book.setAuthor("newAuthor");
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .put(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals("newAuthor", response.jsonPath()
      .get("author"));
}

And delete a book:

@Test
public void whenDeleteCreatedBook_thenOk() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    Response response = RestAssured.delete(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);
    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

10. Conclusion

This was a quick but comprehensive intro to Spring Boot.

We of course barely scratched the surface here – there’s a lot more to this framework that we can cover in a single intro article.

That’s exactly why we don’t just have a single article about Boot on the site.

The full source code of our examples here is, as always, over on GitHub.

Go deeper into Spring Security with the course:

>> LEARN SPRING SECURITY

Leave a Reply

17 Comments on "Bootstrap a Simple Application using Spring Boot"

Notify of
avatar
Sort by:   newest | oldest | most voted
Praveendra Singh
Guest

Great article Baeldung!
It covers most parts of the basic components a great microservice would need.

A simple correction needed:
“If we don’t specify credentials explicitly – a default username and password will be randomly generated by Boot at startup.”

Only password gets generated at startup time which is listed in the log. User name defaults to user.

Grzegorz Piwowarek
Guest

Actually, I think this is just a comma missing 🙂

Hendi Santika
Guest

Nice article!

Slava Semushin
Guest
As a novice to Spring I’d like to see a full paths to all files that we’re creating (main class, application.properties, both controllers and home.html). Otherwise it’s possible that I put these files to non-standard location and it won’t work. This part is really important because it leads to 404 errors when template is placed to a wrong location. Also AFAIR Spring Boot doesn’t recommend to create a main class in the default package (https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-structuring-your-code.html#using-boot-using-the-default-package). Please, don’t just post a sample of the code but also teach newbies about where and why they should be. Otherwise, we’ll have more questions… Read more »
Jeroen Wenting
Guest

all Java sources go into the package you selected when creating the application framework using Spring Initializer.
The html files go into the /resources/templates directory

Obakeng
Guest

Slava for president! Thanks for this post. I too was getting 404-like errors due to incorrect paths set for my files. In my case, Thymeleaf threw this error: Error resolving template “home”, template might not exist or might not be accessible by any of the configured Template Resolvers

My home.html was in the wrong place.

akuma8
Guest

Nice tuto as usual. How do you run it in the embedded tomcat server ?

Grzegorz Piwowarek
Guest

Actually, running Spring Boot jars using “java -jar” starts them by default in the embedded Tomcat. Or did you have something else in mind?

akuma8
Guest

I come from Spring MVC and try learning Spring Boot, so in a web project we usually configure 2 contexts specially the web context (with web.xml or full Java with Servlet 3) with the dispatcher, so how can I run if there isn’t that configuration? Thanks

Grzegorz Piwowarek
Guest

But here comes a very important question. Are you trying to do Spring Boot with Spring MVC or a REST-based Spring Boot application?

I am asking because Spring Boot is basically a tool that helps configuring Spring projects together. It does not force you to abandon Spring MVC.

akuma8
Guest

Both, I want to mix REST and MVC, the first controller “SimpleController” is MVC based and renders the “home” page but @RestController is fully REST so where can I configure it to take that MVC part?

Grzegorz Piwowarek
Guest

If you add the “spring-webmvc” dependency, Spring Boot will autoconfigure everything. So at this point, you can start creating views and returning their names in the @Controller-annotated classes. Also, it will run on embedded Tomcat by default so no struggling with this too. Not sure how to customize the MVC configuration, it’s been years since I did MVC last time.

Eugen Paraschiv
Guest

Hey @akuma8:disqus – here’s the best way to understand how you can deploy your application (including in an embedded Tomcat): http://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html
Hope that helps.
Cheers,
Eugen.

alltej
Guest

Do you have an article regarding use/difference of Spring Hystrix vs Spring Retry (Aspect)?

Eugen Paraschiv
Guest

No, we haven’t published anything on that exact topic, Alltej.
Cheers,
Eugen.

Grzegorz Piwowarek
Guest
wpDiscuz