trendingzones
← Back to the Introduction level

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:

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.

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:

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 →