Spring Boot runs on top of the Spring Framework. Spring's IoC container creates your objects and wires them together, and Spring Boot adds starters and auto-configuration so that most of the setup disappears. This chapter covers beans and dependency injection, auto-configuration, starters, the web layer and type-safe configuration.
Beans and dependency injection
An object managed by the container is called a bean. Annotate a class with a stereotype and component scanning registers it:
When a bean needs another bean, take it through constructor injection. With a single constructor, @Autowired is unnecessary, and final fields make the class easy to create by hand in tests.
@Service
public class GreetingService {
private final Clock clock;
public GreetingService(Clock clock) {
this.clock = clock;
}
public String greet(String name) {
int hour = LocalTime.now(clock).getHour();
return (hour < 12 ? "Good morning, " : "Hello, ") + name;
}
}
For objects you cannot annotate, such as library classes, declare a @Bean method in a @Configuration class:
@Configuration
public class AppConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
Auto-configuration
@SpringBootApplication combines three annotations: @SpringBootConfiguration (a configuration class), @ComponentScan (component scanning) and @EnableAutoConfiguration.
Auto-configuration inspects the classpath and the beans you already defined, then creates the beans you are likely to need. With Spring MVC and Tomcat on the classpath you get an embedded web server and a DispatcherServlet; with a JDBC driver and spring.datasource.url you get a DataSource. Every auto-configuration is guarded by conditions such as @ConditionalOnClass and @ConditionalOnMissingBean, so if you define a bean of the same type yourself, auto-configuration backs off. Your own beans always win.
To see which auto-configurations applied and why, run with --debug and read the condition evaluation report. To switch one off entirely, use exclude:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DemoApplication { /* ... */ }
Starters
A starter is a curated set of dependencies for one capability. A single dependency brings in the libraries and their matching auto-configuration:
spring-boot-starter-webmvc: Spring MVC, embedded Tomcat, JSON via Jacksonspring-boot-starter-data-jpa: Spring Data JPA with Hibernatespring-boot-starter-validation: Bean Validationspring-boot-starter-security: Spring Securityspring-boot-starter-actuator: health checks, metrics and other production features
REST controllers
Methods in a @RestController return objects that are serialized to JSON. Bind path variables with @PathVariable and request bodies with @RequestBody. Java records make good request and response types.
@RestController
@RequestMapping("/api/todos")
public class TodoController {
private final TodoService service;
public TodoController(TodoService service) {
this.service = service;
}
@GetMapping("/{id}")
public TodoResponse get(@PathVariable Long id) {
return service.find(id);
}
@PostMapping
public ResponseEntity<TodoResponse> create(@RequestBody CreateTodo request) {
TodoResponse created = service.create(request.title());
return ResponseEntity.created(URI.create("/api/todos/" + created.id())).body(created);
}
public record CreateTodo(String title) {}
public record TodoResponse(Long id, String title, boolean done) {}
}
Type-safe configuration with @ConfigurationProperties
You can inject single values with @Value("${...}"), but groups of related settings are safer as @ConfigurationProperties. Keys under the prefix bind to the record's components:
@ConfigurationProperties("app.mail")
public record MailProperties(String from, Duration timeout, boolean enabled) {}
app:
mail:
from: no-reply@example.com
timeout: 5s
enabled: true
Add @ConfigurationPropertiesScan to the entry-point class and these records become beans that other beans can receive through their constructors. Values like 5s are converted to Duration automatically.
A note for Kotlin
Kotlin classes are final by default, which prevents Spring from creating proxies. Kotlin projects from Initializr include the kotlin("plugin.spring") compiler plugin, which opens classes carrying Spring annotations. Constructor injection maps naturally onto Kotlin's primary constructor:
@Service
class GreetingService(private val clock: Clock) {
fun greet(name: String) = "Hello, $name"
}
Summary
- The container creates your objects as beans and wires them through constructor injection.
- Auto-configuration creates beans based on the classpath and conditions; beans you define always take precedence.
- One starter brings in the dependencies and auto-configuration for a capability.
- Bind related settings to
@ConfigurationProperties records for type safety.
0 comments
Sign in · Sign in to leave a comment.