How Component Scanning Works Behind the Scenes in Spring

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.

🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.

▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube

Spring makes bean registration ridiculously easy. You just add an annotation like @Component, @Service, or @Controller, and Spring picks it up. No XML, no manual config.

But how does this actually work? How does Spring know which classes to scan, and how does it register them as beans?

In this article, we’ll break down how Spring’s component scanning mechanism works behind the scenes — step-by-step — so you can understand what’s really going on when your app starts.

What Is Component Scanning?

Component scanning is a feature in Spring that automatically detects classes annotated with specific stereotypes (like @Component, @Service, @Repository, @Controller) and registers them as beans in the Spring container.

You activate component scanning using:

@ComponentScan("com.example")

Or implicitly via Spring Boot:

@SpringBootApplication // includes @ComponentScan(basePackages = "your.package")

What Spring Looks For

During scanning, Spring searches for classes with these annotations:

These are all meta-annotated with @Component, so Spring recognizes them as candidates.

How Component Scanning Works (Behind the Scenes)

Let’s break it down step-by-step:

1. Spring Boot / ApplicationContext Starts

When your app launches, Spring creates an ApplicationContext — the core container.

If you’re using Spring Boot:

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

👉 Behind the scenes, @SpringBootApplication includes:

  • @Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

This triggers component scanning in the current package and sub-packages.

2. Spring Finds the Base Package

@ComponentScan("com.example") tells Spring where to look for components.

If no package is defined, Spring uses the package of the class annotated with @SpringBootApplication.

3. Spring Uses ClassPathScanningCandidateComponentProvider

This internal class:

  • Walks through all classes in the package
  • Applies a filter to check if a class has @Component or meta-annotations
  • Marks it as a candidate for registration

Example filters used:

  • AnnotationTypeFilter(Component.class) — includes @Service, @Controller, etc.
  • Custom include/exclude filters if defined

4. Creates a BeanDefinition

For every eligible class found, Spring creates a BeanDefinition, which stores:

  • Bean class name
  • Scope
  • Lifecycle callbacks
  • Dependencies

These are blueprints that will be used to instantiate actual beans later.

5. Registers the BeanDefinition

Spring registers these definitions with the BeanDefinitionRegistry.

It doesn’t instantiate the beans yet — just prepares the metadata for later.

6. Bean Instantiation Happens Later

When the container is refreshed, Spring goes through all bean definitions and:

  • Instantiates the classes
  • Injects dependencies
  • Applies lifecycle annotations (@PostConstruct, etc.)
  • Adds them to the context

Now your beans are available via @Autowired, @GetBean(), etc.

Example Walkthrough

@Component
public class EmailService {
...
}

@Service
public class UserService {
...
}

If your main app class is in com.example:

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

Then Spring will:

  1. Look in com.example and sub-packages
  2. Find EmailService and UserService
  3. Register them as beans
  4. Make them available for injection

⚙️ Advanced: Customizing Component Scan

Include/Exclude filters:

@ComponentScan(
basePackages = "com.example",
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyCustomAnnotation.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.example\\.legacy\\..*")
)

Exclude default scanning behavior:

@ComponentScan(useDefaultFilters = false)

🔄 Summary Flowchart

@SpringBootApplication

@ComponentScan kicks in

Spring scans package(s)

Finds classes with @Component / @Service / ...

Creates BeanDefinitions

Registers them in BeanFactory

Beans are created, injected, and ready to use

Final Thoughts

Component scanning is one of Spring’s most powerful features — and now you know exactly how it works behind the curtain. With just a few annotations, Spring does all the heavy lifting to wire your app together.

The magic isn’t in the annotation — it’s in what Spring does when it sees that annotation.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare