JAVA + SPRING AI — INTERMEDIATE
@SpringBootApplication, Auto-Configuration & @RestController: The Real Mechanics
The Introduction level covered whySpring Boot exists — so you don’t wire up a web server and its plumbing by hand. Here’s how: the one annotation that turns it all on, the conditions Spring Boot actually checks before configuring anything, and a real, working REST controller.
The Quick Answer
@SpringBootApplication is a single annotation that combines three others: @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan. Under the hood, auto-configuration works by activating conditional configuration classes only when specific requirements are met — most commonly, a relevant library being present on the classpath, and you not already having defined your own bean of that type. And a REST endpoint gets built with @RestController, which Spring’s own documentation describes as a convenience annotation combining @Controller and @ResponseBody.
One Annotation, Three Jobs
Spring Boot’s own reference documentation states it plainly: @SpringBootApplication is “a convenience annotation that is equivalent to declaring @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan.” This exact composition is current as of Spring Framework 7.x / Spring Boot 4.x, the same version this pillar uses throughout. Each piece has its own job:
- 1
@SpringBootConfigurationmarks the class as a source of bean definitions — it’s Spring Boot’s own specialized version of the plain@Configurationannotation covered in the previous topic. - 2
@EnableAutoConfigurationis the switch that turns on classpath-based auto-configuration itself — this is the specific annotation Spring Boot’s documentation names as the one that opts an application into auto-configuration. - 3
@ComponentScantells Spring to scan the current package and everything beneath it for annotated classes —@Component,@Service, and, as covered below,@RestController— and register each one as a bean, the exact mechanism from the Dependency Injection topic.
Put a single @SpringBootApplication class at the root of your project, and all three of those jobs happen automatically — which is exactly why nearly every Spring Boot application’s main class carries this one annotation and nothing else.
How Auto-Configuration Actually Decides
“Auto-configuration” isn’t one big block of hardcoded setup — it’s a large collection of ordinary @Configurationclasses, each one gated by conditions that decide whether it actually activates. Spring Boot’s own documentation names the two conditions auto-configuration classes “usually” use together: @ConditionalOnClass and @ConditionalOnMissingBean.
@ConditionalOnClasslets a configuration class be included based on whether a specific class is present on the classpath. Add a database driver dependency, and any auto-configuration gated on “is this driver’s class present?” switches on. Don’t add it, and that configuration class is simply skipped.@ConditionalOnMissingBeanlets a configuration class back off if you’ve already supplied your own bean of that type. Spring Boot’s documentation puts this directly: auto-configuration “applies only when relevant classes are found and when you have not declared your own” configuration for that piece. (The Advanced level covers exactly what it looks like to override an auto-configured default this way.)
Put together: Spring Boot ships dozens of these conditional configuration classes, one per piece of common infrastructure, and at startup only the ones whose conditions are actually satisfied by your specific application do anything at all.
@RestController: A Controller That Returns Data, Not Pages
Spring’s own reference documentation describes @RestController exactly as: “a convenience annotation that is itself annotated with @Controller and @ResponseBody.” @Controller marks the class as a bean whose methods handle incoming web requests; the @ResponseBodypart means whatever a handler method returns gets written directly into the HTTP response body, instead of being treated as the name of an HTML page to render. Based on that documented behavior, here’s the practical takeaway: the entire difference between building a REST API and building a traditional server-rendered website in Spring MVC comes down to that one annotation’s worth of behavior.
Turning a returned Java object into the actual JSON that goes out over the wire is handled by auto-configuration too. Spring Boot’s documentation states that “objects can be automatically converted to JSON” by using the Jackson library, one of the sensible defaults Spring Boot includes out of the box whenever Jackson is on the classpath — which it is by default in a standard Spring Boot web project.
Building the Endpoints: Mappings, Path Variables, Request Bodies
Four annotations do most of the work of turning a class into a real REST API:
@GetMapping— maps an HTTP GET request at a given URL to a method. Used for reading/fetching data.@PostMapping— maps an HTTP POST request to a method. Used for creating something new.@PathVariable— pulls a value straight out of the URL itself, like the42in/books/42, and binds it to a method parameter.@RequestBody— takes the JSON body a client sent along with its request and converts it into a Java object, again via the same Jackson conversion mentioned above, just running in reverse.
Here’s all of it together — a small, real BookController, with a GET-by-id endpoint and a POST endpoint for creating a new book. Notice the constructor: this is the exact same constructor injection pattern from the Dependency Injection topic — the controller declares that it needs a BookService, and Spring supplies it. A @RestController is, underneath, just another Spring bean.
public record Book(Long id, String title, String author) {}
@Service
public class BookService {
private final Map<Long, Book> books = new HashMap<>();
public Book findById(Long id) {
Book book = books.get(id);
if (book == null) {
// BookNotFoundException is a small custom exception — see
// the Advanced level for its definition and how Spring
// turns it into a real HTTP response.
throw new BookNotFoundException(id);
}
return book;
}
public Book save(Book newBook) {
books.put(newBook.id(), newBook);
return newBook;
}
}
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
// Constructor injection again — this controller only
// declares what it needs. Spring supplies the BookService.
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("/{id}")
public Book getBook(@PathVariable Long id) {
return bookService.findById(id);
}
@PostMapping
public Book createBook(@RequestBody Book newBook) {
return bookService.save(newBook);
}
}A GET request to /books/1 runs getBook, with id bound to 1, and Jackson serializes the returned Book record straight into JSON. A POST request to /books with a JSON body runs createBook, with Jackson doing the same conversion in reverse to build the newBook parameter.
Try the Routing Yourself
Pick a request below and see exactly which method in BookController handles it, and what the simulated JSON response looks like:
Fun Fact
Before @RestController existed, building a JSON API in Spring MVC meant using a plain @Controller and adding @ResponseBody to every single handler method individually. Bundling both into one annotation removed a small but constant piece of repetition that showed up on practically every method in a REST-only class.
Test Yourself
According to Spring Boot’s own documentation, what three annotations does @SpringBootApplication combine?
What two conditions typically gate whether an auto-configuration class activates?
What is @RestController, according to Spring’s own documentation?
In the BookController example, why is the service dependency passed in through the constructor rather than built with "new" inside the controller?
Ready for what happens when things go wrong — overriding an auto-configured default, centralized error handling for a REST API, and validating incoming request data? Continue to the Advanced level →