Skip to Content
Course content

299: When to Reach for Spring Boot

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I see this happen all the time with developers who are transitiong from core Java to building actual services. You start with a simple goal—maybe a REST API to manage a list of tasks—and you decide to "do it the right way" by writing everything yourself to understand how it works. But then, the "manual wiring" phase hits, and things start to break.

// A typical "manual" setup that eventually collapses
public class Main {
    public static void main(String[] args) {
        // I need a database connection for the repo
        DatabaseConfig dbConfig = new DatabaseConfig("jdbc:mysql://localhost:3306/mydb", "user", "pass");
        UserRepository userRepo = new UserRepository(dbConfig);
        
        // The service depends on the repo
        UserService userService = new UserService(userRepo);
        
        // The controller depends on the service
        UserController userController = new UserController(userService);

        // Start the server... wait, how do I actually map HTTP requests 
        // to these objects without writing a 200-line switch statement 
        // inside a Servlet?
        System.out.println("Server started on port 8080...");
    }
}

// Imagine this UserRepository class
public class UserRepository {
    private final DatabaseConfig config;
    public UserRepository(DatabaseConfig config) {
        this.config = config; 
    }
    public User findById(String id) {
        // If config was null because I forgot to pass it in Main, BOOM: NullPointerException
        return db.query("SELECT * FROM users WHERE id = " + id); 
    }
}

The Manual Wiring Nightmare

The code above looks fine when you have three classes. But what happens when you have thirty? Or when UserService suddenly needs a NotificationService, which needs a MailClient, which needs an ApiKeyConfig? You end up with a main method that is 100 lines of new Object(new OtherObject(new ThirdObject())). It's brittle, it's a nightmare to test, and if you miss one dependency in the chain, you're chasing a NullPointerException through five different files.

This is the "Dependency Hell" that Spring Boot is designed to kill. The problem isn't the logic of your code; it's the plumbing. You're spending more time wiring objects together than actually writing the business logic for your application.

Automating the Infrastructure with Spring Boot

When you move this to Spring Boot, you stop using the new keyword for your services and repositories. Instead, you use annotations to tell Spring: "Hey, this class is a component. Please manage its lifecycle and inject it wherever it's needed."

@Service
public class UserService {
    private final UserRepository userRepo;

    // Spring sees this constructor and automatically finds the UserRepository bean
    public UserService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }
}

@RestController
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable String id) {
        return userService.findById(id);
    }
}

The fix here is Inversion of Control (IoC). You've stopped being the plumber. Now, you just declare what you need in your constructor, and Spring Boot handles the instantiation and wiring behind the scenes. Plus, it embeds a server (like Tomcat) directly into your JAR, so you don't have to deal with external WAR deployments or complex web.xml files.

Knowing When Spring is Overkill

Now, a word of caution. I've seen developers put Spring Boot into every single project, and that's a mistake. Spring Boot is a heavyweight framework. It brings in a lot of "magic" (via reflection and proxying) that can make debugging harder if you don't understand what's happening under the hood.

You should reach for Spring Boot when:

  • You are building a production-grade web service or microservice.
  • You have a complex graph of dependencies that would make manual wiring tedious.
  • You need to integrate with common "enterprise" tools like JPA/Hibernate for databases, Security/OAuth2, or Kafka for messaging.

Don't use it if you're writing a simple CLI tool, a small library for other developers to use, or a basic algorithm project. In those cases, the overhead of the Spring container will actually slow you down and bloat your application for no real gain.




📋 Practical Task

Build a Minimalist Book Inventory API

Your task is to convert a manually wired "Book Inventory" system into a Spring Boot application. You will be provided with three plain Java classes: BookRepository, BookService, and BookController. Currently, they are wired manually in a main method.

Requirements:

  • Add the spring-boot-starter-web dependency to your project.
  • Annotate the classes correctly (@Repository, @Service, and @RestController) so that Spring manages them.
  • Remove the manual instantiation logic from the main method and replace it with a standard SpringApplication.run() call.
  • Implement a single @GetMapping("/books/{isbn}") endpoint in the controller that returns a book's details.
  • Verify that the application starts and the endpoint returns the expected data without any NullPointerExceptions.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.