Skip to main content

Spring Boot Basics

Spring Boot is a rapid development framework based on the Spring framework that simplifies the configuration and deployment process of Spring applications. This tutorial will introduce you to the fundamentals and core concepts of Spring Boot.

Learning Objectives

Through this tutorial, you will learn:

  • Basic concepts and advantages of Spring Boot
  • Project structure and configuration
  • Auto-configuration principles
  • RESTful API development
  • Data access and integration

What is Spring Boot?

Spring Boot is a new framework provided by the Pivotal team, designed to simplify the initial setup and development process of new Spring applications. The framework uses specific configuration methods, eliminating the need for developers to define boilerplate configurations.

Features of Spring Boot

  • Auto-configuration: Automatically configures Beans based on jar packages and classes in the classpath
  • Starter Dependencies: Simplifies project dependency management through starter dependencies
  • Embedded Servers: Built-in Tomcat, Jetty and other servers, no need to deploy WAR files
  • Production Ready: Provides production-level features such as health checks, external configuration, etc.

Creating a Spring Boot Project

Using Spring Initializr

  1. Visit Spring Initializr
  2. Select project settings:
    • Project: Maven or Gradle
    • Language: Java
    • Spring Boot version: Latest stable version
    • Group: com.example
    • Artifact: demo
  3. Add dependencies:
    • Spring Web
    • Spring Data JPA
    • H2 Database
  4. Generate and download the project

Project Structure

src/
├── main/
│ ├── java/
│ │ └── com/example/demo/
│ │ ├── DemoApplication.java
│ │ ├── controller/
│ │ ├── service/
│ │ ├── repository/
│ │ └── model/
│ └── resources/
│ ├── application.properties
│ └── static/
└── test/
└── java/
└── com/example/demo/

Basic Configuration

application.properties

# Server configuration
server.port=8080
server.servlet.context-path=/api

# Database configuration (H2)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA configuration
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

# Logging
logging.level.com.example.demo=DEBUG

application.yml (Alternative)

server:
port: 8080
servlet:
context-path: /api

spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create-drop
show-sql: true

logging:
level:
com.example.demo: DEBUG

Creating a RESTful API

Model Class

package com.example.demo.model;

import javax.persistence.*;

@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

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

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

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}

Service Class

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

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

public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}

public User createUser(User user) {
if (userRepository.existsByEmail(user.getEmail())) {
throw new RuntimeException("Email already exists");
}
return userRepository.save(user);
}

public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));

user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());

return userRepository.save(user);
}

public void deleteUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
userRepository.delete(user);
}
}

Controller Class

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/users")
@CrossOrigin(origins = "*")
public class UserController {

@Autowired
private UserService userService;

@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}

@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
Optional<User> user = userService.getUserById(id);
return user.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
try {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
} catch (RuntimeException e) {
return ResponseEntity.badRequest().build();
}
}

@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
try {
User updatedUser = userService.updateUser(id, userDetails);
return ResponseEntity.ok(updatedUser);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
try {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
}

Testing

Unit Test Example

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@Test
void getAllUsers_ShouldReturnAllUsers() {
// Given
List<User> users = Arrays.asList(
new User("John", "[email protected]"),
new User("Jane", "[email protected]")
);
when(userRepository.findAll()).thenReturn(users);

// When
List<User> result = userService.getAllUsers();

// Then
assertEquals(2, result.size());
verify(userRepository).findAll();
}

@Test
void createUser_WithValidData_ShouldReturnCreatedUser() {
// Given
User user = new User("John", "[email protected]");
when(userRepository.existsByEmail(user.getEmail())).thenReturn(false);
when(userRepository.save(user)).thenReturn(user);

// When
User result = userService.createUser(user);

// Then
assertNotNull(result);
assertEquals("John", result.getName());
assertEquals("[email protected]", result.getEmail());
}
}

Running the Application

Using Maven

# Run the application
mvn spring-boot:run

# Build the application
mvn clean package

# Run tests
mvn test

Using Gradle

# Run the application
./gradlew bootRun

# Build the application
./gradlew build

# Run tests
./gradlew test

API Testing

Once the application is running, you can test the API endpoints:

# Get all users
curl http://localhost:8080/api/users

# Create a new user
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"[email protected]"}'

# Get user by ID
curl http://localhost:8080/api/users/1

# Update user
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"John Smith","email":"[email protected]"}'

# Delete user
curl -X DELETE http://localhost:8080/api/users/1

Summary

This tutorial introduced the basics of Spring Boot, including:

  • Basic concepts and features of Spring Boot
  • Project creation and structure
  • Configuration management
  • RESTful API development
  • Data access with JPA
  • Testing basics

Spring Boot simplifies Spring application development and provides a solid foundation for building enterprise-level applications.

Next Steps