Spring Boot Internals Explained with Simple, Fun Example Like You're 5

Harsh MangeHarsh Mange
10 min read

Hello, curious minds! Today, we're going on an exciting adventure into the world of Spring Boot. Imagine we're exploring a magical toy factory that can create any toy you want, almost instantly. That's what Spring Boot is like in the world of software! Let's dive in and see how this amazing factory works.

The Grand Entrance: Starting Up

When you first enter our Spring Boot toy factory, you're greeted by a big, friendly robot. This robot is like the main entrance to our factory, and in Spring Boot, we call it the @SpringBootApplication.

@SpringBootApplication
public class MagicalToyFactoryApplication {
    public static void main(String[] args) {
        SpringApplication.run(MagicalToyFactoryApplication.class, args);
    }
}

This robot does three important jobs:

  1. It's the boss of the factory (@Configuration)

  2. It can guess what toys you want to make (@EnableAutoConfiguration)

  3. It knows where to find all the toy-making machines (@ComponentScan)

The Magical Assembly Line: Auto-Configuration

As you walk further into the factory, you see a incredible assembly line. This is where the real magic happens - it's called Auto-Configuration!

Imagine you want to make a toy car. In a normal factory, you'd have to tell the workers exactly how to make every part. But in our magical factory, you just say "I want a toy car," and the assembly line automatically sets up everything needed to make one!

Here's how it works:

  1. Toy Detectors: The factory has special sensors that can detect what kind of toy you want to make. In Spring Boot, these are like @ConditionalOnClass annotations.

     @ConditionalOnClass(name = "org.springframework.jdbc.core.JdbcTemplate")
    
  2. Missing Piece Finders: If a important piece is missing, the factory will add it automatically. This is like @ConditionalOnMissingBean in Spring Boot.

     @ConditionalOnMissingBean(JdbcOperations.class)
    
  3. Customization Stations: You can easily customize your toys at special stations. In Spring Boot, you can customize auto-configuration with properties.

     @ConfigurationProperties(prefix = "spring.datasource")
    

The Toy Blueprint Library: Starter Dependencies

Next, we come to a huge library full of toy blueprints. These blueprints are like recipe books that tell the factory how to make different toys. In Spring Boot, we call these "Starter Dependencies".

For example, if you want to make a web toy, you'd use the "spring-boot-starter-web" blueprint:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

This one blueprint brings in all the instructions needed to make web toys, like how to handle web requests, how to show web pages, and how to keep web toys safe.

The Control Room: Application Context

In the heart of the factory is a big control room. This is where all the decisions are made about how to make your toys. In Spring Boot, this is called the Application Context.

The control room works in stages:

  1. Warming Up: The factory gets ready to make toys (Bootstrap phase)

  2. Checking the Inventory: It looks at what materials and machines are available (Environment preparation)

  3. Planning: It decides the best way to make your toys (Bean definition loading)

  4. Action: It starts all the machines to make your toys (Context refresh)

Here's a peek at how this looks in code:

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        // Warming Up
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();

        try {
            // Checking the Inventory
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

            // Planning
            ConfigurableApplicationContext context = createApplicationContext();
            prepareContext(context, environment, listeners, applicationArguments);

            // Action
            refreshContext(context);
            afterRefresh(context, applicationArguments);

            return context;
        } catch (Throwable ex) {
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }
    }
}

The Toy Testers: Spring Boot Testing

Before any toy leaves the factory, it goes through rigorous testing. Spring Boot has a special testing room for this!

  1. Whole Toy Testing: This is like testing the entire toy at once. In Spring Boot, we use @SpringBootTest for this.

     @SpringBootTest
     class MagicalToyFactoryApplicationTests {
         @Test
         void contextLoads() {
         }
     }
    
  2. Part Testing: Sometimes, we only need to test one part of a toy. Spring Boot has special annotations for this, like @WebMvcTest for testing web parts.

     @WebMvcTest(ToyController.class)
     class ToyControllerTests {
         @Autowired
         private MockMvc mockMvc;
    
         @Test
         void shouldReturnToyDetails() throws Exception {
             mockMvc.perform(get("/toy/1"))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.name").value("Teddy Bear"));
         }
     }
    

The Toy Hospital: Actuator

Even the best toys sometimes need a check-up. Spring Boot has a special hospital called Actuator that can tell us how our toys are doing.

To use the Actuator, you need to add it to your toy blueprint:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Now you can check on your toys' health:

GET /actuator/health
{
    "status": "UP"
}

You can even see how hard your toy factory is working:

GET /actuator/metrics/system.cpu.usage
{
    "name": "system.cpu.usage",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 0.237384
        }
    ]
}

The Customization Workshop: Properties and Profiles

Sometimes, you want to make special toys for different occasions. Spring Boot has a customization workshop for this, using Properties and Profiles.

  1. Toy Settings: You can change how your toys work by adjusting their settings. In Spring Boot, we do this with properties:

     # application.properties
     toy.color=red
     toy.size=large
    

    In your toy-making code:

     @Configuration
     @ConfigurationProperties(prefix = "toy")
     public class ToyProperties {
         private String color;
         private String size;
         // getters and setters
     }
    
  2. Different Toy Designs: You can have different designs for different situations. In Spring Boot, we use Profiles for this:

     # application-summer.properties
     toy.color=yellow
    
     # application-winter.properties
     toy.color=white
    

    To use a specific profile:

     @Profile("winter")
     @Configuration
     public class WinterToyConfiguration {
         // winter-specific bean definitions
     }
    

The Magical Conveyor Belt: Embedded Server

In a normal toy factory, you'd need to ship your toys to a store before kids can play with them. But in our magical factory, we have a special conveyor belt that can instantly deliver toys to kids! This is like Spring Boot's embedded server.

By default, Spring Boot uses a server called Tomcat. It's like a super-fast delivery truck that comes built-in with your toy factory. You don't need to do anything special - it just works!

If you want to use a different delivery truck, you can! For example, to use Jetty:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

The Toy Inventor's Workshop: DevTools

For toy inventors who are always coming up with new ideas, Spring Boot has a special workshop called DevTools. It helps you make changes to your toys really quickly!

To use DevTools, add it to your toy blueprint:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

Now, when you make changes to your toy designs, the factory will automatically update the toys for you!

The Factory's Secret Recipe Book: Spring Boot Autoconfiguration Deep Dive

Remember our magical assembly line that could guess what toys you wanted to make? Let's peek into its secret recipe book to understand how it works so well!

The Magical Sensors: @Conditional Annotations

Our toy factory has super smart sensors that can detect what kind of toy you want to make. In Spring Boot, these sensors are called @Conditional annotations. Here are some of the most important ones:

  1. @ConditionalOnClass: This is like a sensor that checks if a certain toy-making machine is in the factory. If it is, it sets up everything needed to use that machine.

     @ConditionalOnClass(DataSource.class)
     public class DataSourceAutoConfiguration {
         // Configuration for database toys
     }
    
  2. @ConditionalOnMissingBean: This sensor checks if a certain toy part is missing. If it is, it adds that part automatically.

     @ConditionalOnMissingBean
     public DataSource dataSource() {
         // Create a default DataSource if none exists
     }
    
  3. @ConditionalOnProperty: This sensor checks the toy's settings (properties) to decide whether to add certain features.

     @ConditionalOnProperty(prefix = "toy", name = "color", havingValue = "red")
     public class RedToyConfiguration {
         // Configuration for red toys
     }
    

The Factory's Diary: Autoconfiguration Report

Our toy factory keeps a detailed diary of all its decisions. This is like Spring Boot's autoconfiguration report. To see this report, you can add a special setting when you start your factory:

java -jar your-spring-boot-app.jar --debug

This will show you all the decisions the factory made, like:

   Positive matches:
   -----------------
   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required class 'javax.sql.DataSource' (OnClassCondition)

   Negative matches:
   -----------------
   RabbitAutoConfiguration did not match:
      - required class not found: org.springframework.amqp.rabbit.core.RabbitTemplate (OnClassCondition)

This report is super helpful when you're trying to figure out why certain toy-making machines (configurations) were or weren't set up!

The Toy Security Guard: Spring Boot Security

Every good toy factory needs a security guard to keep the toys safe. In our Spring Boot factory, this guard is called Spring Security.

The Default Security Uniform

When you first hire the security guard (by adding the spring-boot-starter-security dependency), they come with a standard uniform:

  • They check for a username and password before letting anyone into the factory (form-based authentication)

  • They make sure all communication with the factory is secret (HTTPS)

  • They protect against bad guys trying to pretend they're someone else (CSRF protection)

Here's how you add this guard to your factory:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Customizing the Security Guard

Sometimes, you need your security guard to do special tasks. You can customize their uniform and duties like this:

@Configuration
@EnableWebSecurity
public class ToyFactorySecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("{noop}password").roles("USER")
                .and()
                .withUser("admin").password("{noop}admin").roles("ADMIN");
    }
}

This configuration is like telling your security guard:

  • Let anyone visit the front of the factory ("/", "/home")

  • Only let managers (ADMIN role) into the admin offices ("/admin/**")

  • Check everyone's ID for all other areas

  • Use a special entrance for workers (custom login page)

  • Show people the exit when they're done (logout)

The Toy Life Cycle: Spring Bean Lifecycle

Every toy in our factory has a life cycle, from when it's first created to when it's packed away. In Spring Boot, we call these toys "beans", and understanding their life cycle is crucial for any toy maker (developer).

The Toy Creation Process

  1. Toy Design (Instantiation): Spring creates an instance of the bean.

  2. Painting the Toy (Populating Properties): Spring sets the values and references to other toys.

  3. Toy Awareness (Aware Interfaces): If the toy implements certain interfaces, Spring tells it about its environment.

  4. Toy Initialization (Initialization Methods): Spring calls any initialization methods.

  5. Toy Ready for Play (Bean is Ready for Use): The toy is now fully assembled and ready to be used.

  6. Toy Cleanup (Destruction): When the factory is closing, Spring calls any cleanup methods.

Here's how this might look in code:

@Component
public class TeddyBear implements InitializingBean, DisposableBean {

    @Autowired
    private Stuffing stuffing;

    public TeddyBear() {
        System.out.println("1. Teddy bear created");
    }

    @PostConstruct
    public void addBow() {
        System.out.println("3. Adding bow to teddy bear");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("4. Teddy bear initialized");
    }

    public void play() {
        System.out.println("5. Playing with teddy bear");
    }

    @PreDestroy
    public void removeBow() {
        System.out.println("6. Removing bow from teddy bear");
    }

    @Override
    public void destroy() {
        System.out.println("7. Teddy bear destroyed");
    }
}

In this example:

  • The teddy bear is created (constructor)

  • It gets its stuffing (autowiring)

  • A bow is added (@PostConstruct)

  • It's fully initialized (InitializingBean)

  • It's played with

  • When the factory closes, the bow is removed (@PreDestroy)

  • Finally, the teddy bear is cleaned up (DisposableBean)

Toy Scopes: How Long Toys Last

Toys in our factory can have different lifespans:

  1. Singleton (Default): Only one of this toy making template/process is made for every vendor. It's shared by everyone.

  2. Prototype: A new toy making template/process is made each time someone/vendor asks for it.

  3. Request: A new toy making template/process is made for each vendor order request (web request).

  4. Session: A new toy making template/process is made for each toy creation session, it could be 10 mins, 1 hour or even 24 hours (web session).

You can set a toy's lifespan like this:

@Component
@Scope("prototype")
public class BuildingBlock {
    // This toy will be created anew each time it's requested
}

Understanding these toy creation lifecycles helps you manage your factory's resources and ensure each toy behaves correctly throughout its life.

Conclusion: The Magic Never Ends

Our tour of the Spring Boot toy factory is coming to an end, but the magic never stops! Spring Boot is always coming up with new ways to make toy-making (or in the real world, app-making) easier and more fun.

0
Subscribe to my newsletter

Read articles from Harsh Mange directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Harsh Mange
Harsh Mange

This is Harsh Mange, working as a Software Engineer - Backend at Argoid. I love building apps. Working on contributing to open-source projects and the dev community.