Spring Boot Intro
Spring Boot: opinionated Java framework for production apps. Auto-configuration, starters, embedded server.
Java — Spring Boot
EXAMPLE
// ===== Why Spring Boot =====
// - Convention over configuration (boring is good)
// - Embedded Tomcat / Jetty / Undertow (no app server install)
// - Massive ecosystem (Security, Data JPA, WebFlux, Cloud)
// - Production features (actuator, metrics, health)
// - First-class testing support
// ===== Create a project =====
// https://start.spring.io
// or:
spring init --build maven --dependencies web,data-jpa,postgresql,actuator myapp
// ===== Main class =====
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
// ===== A REST controller =====
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService users;
public UserController(UserService users) { this.users = users; } // constructor injection
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
return users.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User create(@Valid @RequestBody CreateUserDto dto) {
return users.create(dto);
}
}
// ===== Service =====
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { this.repo = repo; }
public Optional<User> findById(Long id) { return repo.findById(id); }
@Transactional
public User create(CreateUserDto dto) {
var user = new User(dto.email(), dto.name());
return repo.save(user);
}
}
// ===== Repository (Spring Data JPA) =====
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByCreatedAtAfter(Instant cutoff);
}
// ===== Entity =====
@Entity
@Table(name = "users")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String email;
private String name;
@CreationTimestamp
private Instant createdAt;
protected User() {}
public User(String email, String name) {
this.email = email;
this.name = name;
}
// getters
}
// ===== application.yml =====
spring:
application:
name: shop-api
datasource:
url: jdbc:postgresql://localhost:5432/shop
username: dev
password: dev
jpa:
hibernate:
ddl-auto: validate // never 'update' or 'create' in production
show-sql: false
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
// ===== Validation =====
public record CreateUserDto(
@Email String email,
@NotBlank @Size(max = 100) String name
) {}
// ===== Exception handling =====
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidation(MethodArgumentNotValidException ex) {
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
}
}
// ===== Tests =====
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired MockMvc mvc;
@Test
void getsUser() throws Exception {
mvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("a@x.io"));
}
}
// ===== Patterns =====
// - Constructor injection (no @Autowired needed since 4.3)
// - Spring Data JPA for boring CRUD
// - @Transactional on write paths
// - Validation via bean validation
// - Actuator + Micrometer + Prometheus for metrics
// ===== Pitfalls =====
// - ddl-auto: update in production
// - Field injection (use constructor)
// - N+1 queries (use @EntityGraph or fetch joins)
// - Catching Exception broadly in @ControllerAdvice
Why it matters
Spring Boot: opinionated, productive, batteries included. Controller + Service + Repository + Entity covers most CRUD. application.yml configures everything; Actuator + Prometheus expose metrics. Master DI + JPA + validation + tests and you can ship serious Java services.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
@RestController
class HiController {
@GetMapping("/hi")
String hi() { return "hello"; }
}
Try it Yourself »
Discussion
Loading…