1. The “magic” effect in Spring Boot
If you are seeing Spring Boot for the first time, the feeling is roughly this: you write one line SpringApplication.run(...), and suddenly a whole universe comes alive in the console — logs, a server, some port, a bunch of messages. That is a normal reaction. Our brain does not like it when a small action causes a big effect — it immediately suspects sorcery.
We already know how to assemble our own @Service, @Repository, and constructor wiring. But when a Boot application starts, much more than our code ends up in the context. So, besides application wiring, there is another assembly layer — the platform layer — and today it is important to understand how it works at all.
Spring Boot really does a lot of work for you, but not because it is “magical,” rather because it is a platform that assembles typical infrastructure for you. Imagine you are building a house. You can manually run wires, install a breaker panel, choose circuit breakers, and lay pipes. Or you can buy a good prefab house kit where all the standard decisions have already been thought through. The house does not become “magical” because of that — it just means some decisions were moved into a ready-made package.
We have already seen that Spring is not “a set of annotations” but a container. Now we add the next layer: Boot is not “another annotation,” but a platform-level configuration assembly that connects to your container.
2. Auto-configuration in simple terms
Now comes an important definition, and it is worth remembering in exactly this sense, not as an “exam-style” formulation. Auto-configuration in Spring Boot is a mechanism that adds a set of ready-made Spring configurations to your application so that typical infrastructure appears automatically, without you having to write it by hand every time.
The key point: auto-configuration is not a separate “secret Spring mode.” It is literally ordinary @Configuration classes, except they live inside Spring Boot libraries (and related modules) and are connected automatically when the application starts. So it is the same Spring code, just not yours.
It is very useful to keep a simple “word picture” in mind:
- Regular configuration is when you wrote @Configuration and @Bean yourself, and this code will definitely enter the context.
- Auto-configuration is when Spring Boot can connect a ready-made configuration if it fits the current application (based on a set of signals: dependencies, settings, context state). Important: “can,” not “must.”
Put even more simply: Boot is a “platform assembler.” It does not write your business logic, but it can deliver the ready-made infrastructure “building blocks” so that you can start doing useful work faster and in a more predictable form.
3. Auto-configuration in ApplicationContext
Let us carefully remove one very common illusion: that Boot has a “second container,” a “special memory,” or a “hidden layer.” No. Everything Boot configures is ultimately expressed as beans that end up in the same ApplicationContext where your @Service and @Repository live.
The most illustrative place where the life of a Boot application begins is your main class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // Enables component scanning and auto-configuration
public class CatalogServiceApplication {
public static void main(String[] args) {
// Application entry point: from here Boot will assemble the ApplicationContext and infrastructure
SpringApplication.run(CatalogServiceApplication.class, args);
}
}
From the point of view of “plain Java,” this looks like one call. But from the point of view of Boot, it is the start of a process in which the ApplicationContext is created, the environment is brought up, configurations are connected, beans are registered — and then the application enters the “ready” state.
To feel this with your hands, you can do a very simple experiment: after startup, print the number of bean definitions in the context. This is not the “number of classes,” but the number of registered components and settings the container knows about.
Throughout the day, we will run into short probe components. This is not a new permanent catalog-service architecture, but a one-time flashlight: keep one in the project, run it, look at the output, and remove it so that startup does not turn into a noisy chat of diagnostic runners.
Add a temporary runner to the project (for example, in the catalog.bootstrap package so it is logically close to the startup pieces):
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component // Make the class a bean so Boot runs it at startup
class ContextSummaryRunner implements ApplicationRunner {
private final ApplicationContext context;
// Inject the context so we can ask it questions about registered beans
ContextSummaryRunner(ApplicationContext context) {
this.context = context;
}
@Override
public void run(ApplicationArguments args) {
// Print the number of bean definitions in the container (the count depends on dependencies)
System.out.println("Bean definitions: " + context.getBeanDefinitionCount());
// For example: Bean definitions: 340
}
}
When you run this, you will almost certainly see a number that is much larger than the number of classes you wrote yourself. And that is good: it means Boot really assembled the infrastructure for you. The important idea is this: “many beans in the context” is not a bug, but a normal state for a Boot application.
4. Auto-configuration as platform assembly
Now it is useful to build a simple working picture that later saves a lot of nerves. Your application context is not “our application plus a little Spring.” It is the place where three bean sources meet: your components, your explicit configurations, and Boot's auto-configuration.
It is very convenient to imagine this as a “shared basket” into which different participants put their things:
flowchart TD
A["Your code (catalog-service) @Service, @Repository, @Component"] --> C["ApplicationContext"]
B["Your explicit configurations @Configuration + @Bean"] --> C
D["Spring Boot auto-configuration ready-made @Configuration classes from Boot"] --> C
C --> R["Running application"]
There is no “magic zone” in this diagram. Auto-configuration is simply a third source that puts infrastructure beans into the same container.
And here an important everyday question arises: how do you tell which beans are “ours” and which ones “came from Boot”? Not by who “created” them, but by the meaning of responsibility. And that is exactly what Boot teaches you: to separate application code and infrastructure.
5. Application and infrastructure beans
Now we will reach the boundary that separates a healthy Boot application from permanent chaos. Your project will contain two large classes of beans. They are both “real” and both important, but you treat them differently: you write and evolve application beans, while infrastructure beans are mostly accepted as part of the platform and touched carefully.
To keep the terminology from sprawling, let us define it in human terms.
Application beans are what belongs to your domain. In catalog-service, these are the catalog services and repositories, domain models, startup seeders (if you have them), and similar things. Boot is not supposed to “invent” them, because it does not know your business.
For example, our catalog service is an application bean:
import org.springframework.stereotype.Service;
@Service // Application-layer component: domain/business logic
public class CourseCatalogService {
private final CourseCatalogRepository repository;
public CourseCatalogService(CourseCatalogRepository repository) {
// Inject the application dependency (repository) through the constructor
this.repository = repository;
}
}
And the repository (in our educational version — in-memory) is also an application bean. It is not “Spring infrastructure,” it is part of the application:
import java.util.List;
import org.springframework.stereotype.Repository;
@Repository // Application repository (in the example — a simple in-memory implementation)
public class InMemoryCourseCatalogRepository implements CourseCatalogRepository {
@Override
public List<CourseCard> findAll() {
// Stub: in a real project, this is where data access would live
return List.of(); // an empty list is enough for the example
}
}
Infrastructure beans are what make the application “alive”: the embedded server, HTTP handling, JSON, converters, codec settings, error handlers, logging, metrics, and so on. Even without controllers, you may already have noticed that Boot writes to the logs about the server and ports. That is infrastructure in action.
Here is a short table to feel the boundary:
| Bean type | Examples in catalog-service | Who is the “main author” |
|---|---|---|
| Application (domain/application) | CourseCatalogService, InMemoryCourseCatalogRepository, your Runner classes | You (and the project team) |
| Infrastructure (platform) | embedded server, base web infrastructure, JSON mapping, internal Spring settings | Spring Boot + Spring Framework |
Important: infrastructure beans are not “foreign and scary.” They simply should not be your daily headache. If you rewrite the infrastructure every week, you are not building a product — you are building your own mini-framework. And that is very expensive and rarely justified, especially at junior level.
6. “Little code” and startup work
There is a subtle trap of thinking: since I wrote one line of run(...), the application “does almost nothing.” And then the student is surprised: “Why does startup take seconds? Why so many classes? Why such a wall of logs?” Here you need to switch the model: Boot saves your manual work, but it does not cancel the physics of the startup process.
The application still needs to:
- create the container;
- register and wire beans;
- bring up the server (if it is a web application);
- prepare the necessary settings;
- prepare the serialization and request-handling infrastructure (if the application works as a web service);
- and only then switch to the “ready” state.
Boot reduces not the “computer's work,” but the “developer's work.” It provides template solutions and “sensible defaults” so that you do not assemble the base platform from scratch in every project.
If you imagine an alternative reality without Boot, then in that world you would keep making dozens of the same decisions in every project: which server, how to configure JSON, how to organize basic error handling, how to arrange typical integrations. Sure, you can. But then your profession would be called “manual infrastructure assembler,” not “backend developer who solves business problems.” Boot was created precisely to save you from that.
7. Mini-check: “our own” beans
Let us reinforce the main feeling: in a Boot application there are many beans, but “ours,” in the semantic sense, are few. And that is normal: your application is usually small, while the infrastructure around it is quite thick.
You can print the names of beans of a certain type. For example, let us make sure that CourseCatalogService is really singular in the context (and that we did not accidentally get a “zoo” of ten services of the same type). This is again just a temporary probe: it is needed to ask the container one specific question and get an answer.
import java.util.Arrays;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
class AppBeansProbeRunner implements ApplicationRunner {
private final ApplicationContext context;
AppBeansProbeRunner(ApplicationContext context) {
this.context = context;
}
@Override
public void run(ApplicationArguments args) {
String[] names = context.getBeanNamesForType(CourseCatalogService.class);
System.out.println(Arrays.toString(names)); // [courseCatalogService]
}
}
This is a small but very useful way of thinking. You do not try to “guess” what happened. You ask the container a specific question: “Which beans of this type do you have?” If the answer is unexpected, then you have learned something important about the application assembly.
Notice one more thing: we are asking about our application type. And that is fundamentally easier than trying to sort out all the infrastructure beans at once. Your main area of responsibility is application beans. You learn to understand infrastructure gradually, but you are not required to control all of it at once.
8. The responsibility boundary between Boot and catalog-service
Now I will say something that helps a lot with stopping arguing with the framework (and with life). Boot does for you what can be done in a “typical” way, and you do what belongs to your domain. When you mix up these zones, two scenarios usually happen.
In the first scenario, you expect the impossible from Boot: “Let Boot create my service, repository, load the courses, set up filtering, and while it is at it, invent the business for me too.” Unfortunately, that is not yet part of Spring Boot's functionality (and thank goodness: otherwise we would all be working in Boot that writes startups and fires people).
In the second scenario, you, on the contrary, try to drag under manual control what Boot already does well, just because it is still unfamiliar. And then the “I will rewrite half the infrastructure because I do not understand where this bean came from” phase begins. That is also a bad strategy.
The healthy boundary for catalog-service right now looks like this: we explicitly describe application wiring (service → repository), keep the code readable, do not spread into god objects, and accept the infrastructure base as the platform that Boot assembles automatically. Our job today is to stop calling this magic and start calling it rule-based assembly.
9. Common mistakes when working with auto-configuration
Mistake #1: “Boot creates absolutely everything on its own, so I almost do not need to write anything.”
This usually ends with the person not writing application components or writing them “somehow, somewhere,” expecting the platform to “figure it out.” Boot really does create many beans, but that is almost always infrastructure. Your CourseCatalogService and your domain logic are the application's responsibility. If you did not describe them, Boot is not obliged to guess your business.
Mistake #2: “If there are lots of lines in the logs, then Spring is doing something wrong.”
In reality, most of startup is normal container work: registering beans, preparing infrastructure, bringing up the runtime. Lots of logs does not automatically mean “bad.” What is bad is when you do not understand what they mean. Today we are taking a step toward understanding: “this is not magic, this is platform assembly.”
Mistake #3: “Since I only have one line of run(...), the application should start instantly.”
Small code does not mean a small process. Boot removes manual setup, but it does not remove the need to create the context, prepare the infrastructure, and (if it is a web application) bring up the server. If you expect “zero work” at startup, you will keep being disappointed by any platform — not only Spring.
Mistake #4: “I want to immediately distinguish all infrastructure beans from my own, but I can't, so Spring is too complex.”
That is a normal stage. First, focus on application components: services, repositories, “bootstrap” code. The infrastructure will “grow meaning” gradually. The important thing is not to demand of yourself an understanding of all internal mechanisms in one day — otherwise you will turn learning into a sport of self-flagellation.
Mistake #5: “If something appeared in the context, then it was added by accident.”
In Boot, almost nothing happens by accident: it is the result of connected dependencies and platform settings. If something appeared, there is a reason for it, and that reason is usually explainable by classpath, conditions, and configuration.
GO TO FULL VERSION