Spring Framework Essentials: Understanding @ComponentScan
@ComponentScan is an annotation that tells Spring where to find classes annotated as Spring beans. Using this annotation, we can instruct Spring to create beans only for the specified location. Spring will use the package provided in the annotation to look for beans to create. If no package is defined in the annotation, Spring will scan recursively from the class where this annotation is declared. You can also use this annotation to scan multiple packages and configure exclusions to avoid creating certain beans. If this annotation is not specified, Spring will scan all the classes and jars present on the classpath. Let’s take a look at a code example. In this example, we are creating a component scan without a parameter, so Spring will scan from this package and all the subpackages from this declaration. package org.spring.mastery.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class AppConfig { } package org.spring.mastery.service; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @Service public class MyService { @PostConstruct public void init() { System.out.println("MyService initialized"); } } package org.spring.mastery.repository; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; @Repository public class MyRepository { @PostConstruct public void init() { System.out.println("MyRepository initialized"); } } package org.spring.another; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @Service public class ExcludedService { @PostConstruct public void init() { System.out.println("ExcludedService initialized"); } } In this setup, MyService and MyRepository will be created as beans because they are in sub-packages of org.spring.mastery. However, ExcludedService will not be loaded because it is outside the package hierarchy. This is the log line: 2025-04-10T11:09:56.876-03:00 INFO 19296 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' MyRepository initialized MyService initialized We can also scan specific packages. Remember, we can use multiple packages in the @ComponentScan annotation. package org.spring.mastery.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository" }) public class AppConfig { } In this example, we are scanning org.spring.mastery.service and org.spring.mastery.repository. Any beans in these packages will be created, while ExcludedService will still not be loaded. The logs look like this: 2025-04-10T11:14:26.887-03:00 INFO 17868 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' MyRepository initialized MyService initialized If we want to add components outside the org.spring.mastery structure, we can specify it in the @ComponentScan annotation like this: package org.spring.mastery.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository", "org.spring.another" }) public class AppConfig { } Now, the components from the other subpackages will also be loaded: 2025-04-10T11:15:49.313-03:00 INFO 3080 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' MyRepository initialized MyService initialized ExcludedService initialized If we do not want to load some specific beans or packages, we can create exclusions like this: package org.spring.mastery.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Service; @Configuration @ComponentScan( basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class)) public class AppConfig { } In this configuration, we are excluding any classes annotated with @Service within the specified packages. The result log will be: 2025-04-10T14:59:53.710-03:00 INFO 21828 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' MyRepository initialized MyService initialized Now you have learne

@ComponentScan
is an annotation that tells Spring where to find classes annotated as Spring beans. Using this annotation, we can instruct Spring to create beans only for the specified location. Spring will use the package provided in the annotation to look for beans to create.
If no package is defined in the annotation, Spring will scan recursively from the class where this annotation is declared.
You can also use this annotation to scan multiple packages and configure exclusions to avoid creating certain beans.
If this annotation is not specified, Spring will scan all the classes and jars present on the classpath.
Let’s take a look at a code example.
In this example, we are creating a component scan without a parameter, so Spring will scan from this package and all the subpackages from this declaration.
package org.spring.mastery.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class AppConfig {
}
package org.spring.mastery.service;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
public class MyService {
@PostConstruct
public void init() {
System.out.println("MyService initialized");
}
}
package org.spring.mastery.repository;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
@Repository
public class MyRepository {
@PostConstruct
public void init() {
System.out.println("MyRepository initialized");
}
}
package org.spring.another;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
public class ExcludedService {
@PostConstruct
public void init() {
System.out.println("ExcludedService initialized");
}
}
In this setup, MyService
and MyRepository
will be created as beans because they are in sub-packages of org.spring.mastery
. However, ExcludedService
will not be loaded because it is outside the package hierarchy.
This is the log line:
2025-04-10T11:09:56.876-03:00 INFO 19296 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
MyRepository initialized
MyService initialized
We can also scan specific packages. Remember, we can use multiple packages in the @ComponentScan
annotation.
package org.spring.mastery.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository" })
public class AppConfig {
}
In this example, we are scanning org.spring.mastery.service
and org.spring.mastery.repository
. Any beans in these packages will be created, while ExcludedService
will still not be loaded.
The logs look like this:
2025-04-10T11:14:26.887-03:00 INFO 17868 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
MyRepository initialized
MyService initialized
If we want to add components outside the org.spring.mastery
structure, we can specify it in the @ComponentScan
annotation like this:
package org.spring.mastery.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository", "org.spring.another" })
public class AppConfig {
}
Now, the components from the other subpackages will also be loaded:
2025-04-10T11:15:49.313-03:00 INFO 3080 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
MyRepository initialized
MyService initialized
ExcludedService initialized
If we do not want to load some specific beans or packages, we can create exclusions like this:
package org.spring.mastery.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Service;
@Configuration
@ComponentScan(
basePackages = {"org.spring.mastery.service", "org.spring.mastery.repository"},
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class))
public class AppConfig {
}
In this configuration, we are excluding any classes annotated with @Service
within the specified packages.
The result log will be:
2025-04-10T14:59:53.710-03:00 INFO 21828 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
MyRepository initialized
MyService initialized
Now you have learned how @ComponentScan
works. Remember, if not set, Spring will scan all classes and jars in your classpath. Have you ever forgotten to add the package to scan and wondered why Spring is not recognizing your new bean (believe me, it happens)?
Explore this configuration and share your thoughts in the comments or on social media.
If you like this topic, make sure to follow me. In the following days, I’ll be explaining more about Spring annotations! Stay tuned!