Introduction to Spring Boot

Spring Boot is the fastest way to build production-ready Java applications. It takes the powerful Spring Framework and removes the configuration headaches. You can go from zero to a running web application in minutes.

Before Spring Boot, setting up a Spring project meant writing dozens of XML configuration files. Spring Boot eliminates most of that. It auto-configures everything sensibly, embeds a web server, and lets you focus on your actual code.

This tutorial covers the basics: creating a project, building REST APIs, connecting to databases, and understanding how Spring Boot works under the hood.

What Spring Boot Provides

Spring Boot builds on the Spring Framework but adds several conveniences:

Auto-configuration: Spring Boot examines your classpath and configures beans automatically. Add a database driver, and it configures a DataSource. Add Spring Web, and it configures an embedded Tomcat server.

Starter dependencies: Instead of hunting for compatible library versions, you add a single starter. spring-boot-starter-web pulls in everything needed for web development.

Embedded server: No need to install Tomcat or deploy WAR files. Spring Boot embeds Tomcat, Jetty, or Undertow directly in your JAR.

Production-ready features: Health checks, metrics, and externalized configuration come built in.

Creating a Spring Boot Project

The easiest way to start is Spring Initializr at start.spring.io. It generates a project skeleton with your chosen dependencies.

Select these options:

  • Project: Maven
  • Language: Java
  • Spring Boot: 3.2.x (latest stable)
  • Group: com.example
  • Artifact: demo
  • Packaging: Jar
  • Java: 21

Add the “Spring Web” dependency for building REST APIs. Click Generate to download a zip file.

Extract and open the project in your IDE. The structure looks like this:

demo/
    src/
        main/
            java/
                com/example/demo/
                    DemoApplication.java
            resources/
                application.properties
                static/
                templates/
        test/
            java/
                com/example/demo/
                    DemoApplicationTests.java
    pom.xml

The Main Application Class

DemoApplication.java is the entry point:

package com.example.demo;

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

@SpringBootApplication
public class DemoApplication {

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

The @SpringBootApplication annotation combines three things:

  • @Configuration: This class can define beans
  • @EnableAutoConfiguration: Turn on auto-configuration
  • @ComponentScan: Scan for components in this package and below

Run the main method. Spring Boot starts an embedded Tomcat server on port 8080. You’ll see log output ending with “Started DemoApplication.”

Building a REST API

REST APIs are Spring Boot’s sweet spot. Create a controller to handle HTTP requests:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

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

@RestController marks this class as a web controller where every method returns data (not a view). @GetMapping maps HTTP GET requests to the method.

Run the application and visit http://localhost:8080/hello in your browser. You’ll see “Hello, World!”

Returning JSON

Spring Boot automatically converts objects to JSON. Create a simple class:

package com.example.demo;

public class User {
    private Long id;
    private String name;
    private String email;

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getters required for JSON serialization
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
}

Return it from a controller method:

@RestController
public class UserController {

    @GetMapping("/user")
    public User getUser() {
        return new User(1L, "Alice", "alice@example.com");
    }
}

Visit http://localhost:8080/user and you get JSON:

{"id":1,"name":"Alice","email":"alice@example.com"}

Spring Boot includes Jackson for JSON processing. It serializes your objects automatically based on their getter methods.

Path Variables and Request Parameters

Capture values from the URL:

@RestController
@RequestMapping("/users")
public class UserController {

    // GET /users/5
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // In reality, fetch from database
        return new User(id, "User " + id, "user" + id + "@example.com");
    }

    // GET /users?name=Alice
    @GetMapping
    public List<User> searchUsers(@RequestParam(required = false) String name) {
        // Filter users by name
        return List.of(
            new User(1L, "Alice", "alice@example.com"),
            new User(2L, "Bob", "bob@example.com")
        );
    }
}

@PathVariable extracts values from the URL path. @RequestParam extracts query string parameters.

Handling POST Requests

Accept JSON in the request body:

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public User createUser(@RequestBody User user) {
        // Save to database, assign ID
        System.out.println("Creating user: " + user.getName());
        return new User(99L, user.getName(), user.getEmail());
    }
}

@RequestBody tells Spring to deserialize the JSON request body into a User object. Test with curl:

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","email":"charlie@example.com"}'

Full CRUD Example

Here’s a complete controller with all HTTP methods:

@RestController
@RequestMapping("/api/users")
public class UserController {

    private Map<Long, User> users = new HashMap<>();
    private Long nextId = 1L;

    @GetMapping
    public List<User> getAllUsers() {
        return new ArrayList<>(users.values());
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        User user = users.get(id);
        if (user == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
        }
        return user;
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        user = new User(nextId++, user.getName(), user.getEmail());
        users.put(user.getId(), user);
        return user;
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        if (!users.containsKey(id)) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
        }
        user = new User(id, user.getName(), user.getEmail());
        users.put(id, user);
        return user;
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        if (users.remove(id) == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
        }
    }
}

Dependency Injection

Spring manages object creation and wiring through dependency injection. Instead of creating objects yourself, you declare dependencies and Spring provides them.

// Service class - business logic
@Service
public class UserService {

    public User findById(Long id) {
        // Database lookup would go here
        return new User(id, "User " + id, "user@example.com");
    }

    public List<User> findAll() {
        return List.of(
            new User(1L, "Alice", "alice@example.com"),
            new User(2L, "Bob", "bob@example.com")
        );
    }
}

// Controller depends on service
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    // Constructor injection - Spring provides the UserService
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }
}

@Service marks UserService as a Spring-managed component. When Spring creates the UserController, it sees the constructor needs a UserService and injects one automatically.

This pattern makes code testable. In tests, you can provide a mock UserService instead of a real one.

Configuration with application.properties

The application.properties file (or application.yml) configures your application:

# Server configuration
server.port=8081

# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret

# Logging
logging.level.com.example=DEBUG

# Custom properties
app.name=My Application
app.version=1.0

Access custom properties in your code:

@RestController
public class InfoController {

    @Value("${app.name}")
    private String appName;

    @Value("${app.version}")
    private String appVersion;

    @GetMapping("/info")
    public Map<String, String> getInfo() {
        return Map.of(
            "name", appName,
            "version", appVersion
        );
    }
}

Profiles

Different configurations for different environments:

application.properties          # Default
application-dev.properties      # Development
application-prod.properties     # Production

Activate a profile:

# Command line
java -jar app.jar --spring.profiles.active=prod

# Or in application.properties
spring.profiles.active=dev

Connecting to a Database

Spring Data JPA simplifies database access dramatically. Add these dependencies to your pom.xml:

<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>

Configure H2 (an in-memory database, good for development):

# application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create-drop

Entity Class

Mark your class as a JPA entity:

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(unique = true)
    private String email;

    // Default constructor required by JPA
    public User() {}

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

Repository Interface

Create an interface extending JpaRepository. Spring generates the implementation:

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

    // Spring generates these automatically based on method names
    List<User> findByName(String name);
    User findByEmail(String email);
    List<User> findByNameContaining(String namePart);
}

JpaRepository provides findAll(), findById(), save(), delete(), and more. Spring Data JPA parses method names like findByEmail and generates the query automatically.

Using the Repository

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("User not found"));
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(Long id) {
        userRepository.deleteById(id);
    }
}

Error Handling

Handle exceptions globally with @ControllerAdvice:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Map<String, String>> handleRuntimeException(RuntimeException ex) {
        Map<String, String> error = Map.of(
            "error", ex.getMessage(),
            "status", "500"
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }

    @ExceptionHandler(ResponseStatusException.class)
    public ResponseEntity<Map<String, String>> handleResponseStatusException(ResponseStatusException ex) {
        Map<String, String> error = Map.of(
            "error", ex.getReason(),
            "status", String.valueOf(ex.getStatusCode().value())
        );
        return ResponseEntity.status(ex.getStatusCode()).body(error);
    }
}

Running and Packaging

During development, run from your IDE or use Maven:

mvn spring-boot:run

For production, package as an executable JAR:

mvn clean package

This creates target/demo-0.0.1-SNAPSHOT.jar. Run it directly:

java -jar target/demo-0.0.1-SNAPSHOT.jar

The JAR contains your code, all dependencies, and an embedded Tomcat server. Deploy it anywhere Java runs.

Testing

Spring Boot includes testing support. Test your REST API:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldReturnHello() throws Exception {
        mockMvc.perform(get("/hello"))
            .andExpect(status().isOk())
            .andExpect(content().string("Hello, World!"));
    }

    @Test
    void shouldReturnUser() throws Exception {
        mockMvc.perform(get("/user"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Alice"));
    }
}

Run tests with:

mvn test

Common Starters

Spring Boot starters bundle related dependencies:

spring-boot-starter-web: REST APIs, Spring MVC, embedded Tomcat

spring-boot-starter-data-jpa: JPA, Hibernate, database access

spring-boot-starter-security: Authentication and authorization

spring-boot-starter-test: JUnit, Mockito, testing utilities

spring-boot-starter-validation: Bean validation with annotations

spring-boot-starter-actuator: Health checks, metrics, monitoring endpoints

Add them to your pom.xml as needed. Each starter pulls in sensible defaults that work together.

Project Structure Best Practices

As projects grow, organize by feature or layer:

com.example.demo/
    DemoApplication.java
    
    user/
        User.java
        UserController.java
        UserService.java
        UserRepository.java
    
    product/
        Product.java
        ProductController.java
        ProductService.java
        ProductRepository.java
    
    config/
        SecurityConfig.java
        WebConfig.java
    
    exception/
        GlobalExceptionHandler.java
        ResourceNotFoundException.java

Spring’s component scanning finds @Controller, @Service, @Repository, and @Component annotations anywhere under the main application package.

Next Steps

This tutorial covered the fundamentals. Spring Boot offers much more:

Spring Security for authentication and authorization. Protect endpoints, integrate with OAuth2, manage user sessions.

Spring Data for various databases: MongoDB, Redis, Elasticsearch, and more.

Spring Cloud for microservices: service discovery, configuration management, circuit breakers.

Spring WebFlux for reactive, non-blocking applications.

The official documentation at docs.spring.io covers everything in depth. The Spring Guides at spring.io/guides provide focused tutorials on specific topics.


Previous: Introduction to Maven

Related: Introduction to JDBC | Introduction to Object-Oriented Programming | Java Collections Framework Overview

Sources

  • Spring. “Spring Boot Reference Documentation.” docs.spring.io/spring-boot
  • Spring. “Spring Initializr.” start.spring.io
  • Spring. “Spring Guides.” spring.io/guides
  • Walls, Craig. “Spring Boot in Action.” Manning, 2016
Scroll to Top