Mastering Spring Boot 3 @ComponentScan: Advanced Configurations & Examples
This article provides a comprehensive, step‑by‑step guide to using Spring Boot 3's @ComponentScan annotation, covering basic usage, package filtering, include/exclude filters, lazy initialization, custom name generators, scoped proxies, and custom scope resolvers, complete with runnable code samples and output screenshots.
1. Introduction
In Spring, the @ComponentScan annotation is a powerful tool for automatically detecting and registering beans annotated with @Component , @Service , @Repository , or @Controller . This article explores its hidden features and best practices.
2. Practical Cases
2.1 Basic Usage
With a simple @ComponentScan on the configuration class, Spring scans the current package and its sub‑packages, registering classes A , B , etc.
<code>package com.pack.ioc.scan_component;
@Component
public class A {}
package com.pack.ioc.scan_component.child;
@Component
public class B {}
@Configuration
@ComponentScan
public class AppConfig {}
</code> <code>public class ComponentScanTest {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.registerBean(AppConfig.class);
context.refresh();
System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
}
}
}
</code>Output shows both A and B are registered.
2.2 Specifying basePackages
<code>@ComponentScan(basePackages = {"com.pack.ioc.scan_component.child"})
public class AppConfig {}
</code>Only beans in the specified sub‑package ( B ) are registered.
2.3 Specifying basePackageClasses
<code>@ComponentScan(basePackageClasses = {B.class})
public class AppConfig {}
</code>Scanning is performed based on the package of the given class ( B ).
2.4 Using includeFilters
The excludeFilters and includeFilters attributes allow fine‑grained control. The FilterType enum provides the following values:
<code>public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
</code>2.4.1 ANNOTATION
<code>@ComponentScan(includeFilters = @Filter(type = FilterType.ANNOTATION, classes = Pack.class))
public class AppConfig {}
</code>Custom annotation @Pack is detected and its beans are registered.
2.4.2 ASPECTJ
<code>@ComponentScan(includeFilters = @Filter(type = FilterType.ASPECTJ, pattern = {"com.pack.*Pack*"}))
public class AppConfig {}
</code>2.4.3 ASSIGNABLE_TYPE
<code>@ComponentScan(includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DAO.class}))
public class AppConfig {}
</code>All classes implementing DAO are scanned.
2.4.4 REGEX
<code>@ComponentScan(includeFilters = @Filter(type = FilterType.REGEX, pattern = {"com\\.pack\\..*Pack.*"}))
public class AppConfig {}
</code>2.4.5 CUSTOM
<code>public class PackTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader mr, MetadataReaderFactory mf) throws IOException {
return mr.getAnnotationMetadata().hasAnnotation(Pack.class.getName());
}
}
@ComponentScan(includeFilters = @Filter(type = FilterType.CUSTOM, classes = {PackTypeFilter.class}))
public class AppConfig {}
</code>2.5 Specifying lazyInit
<code>@ComponentScan(lazyInit = true)
public class AppConfig {}
</code>Beans are created lazily; initialization callbacks are not executed at context startup.
2.6 Specifying useDefaultFilters
<code>@ComponentScan(useDefaultFilters = false, includeFilters = @Filter(type = FilterType.CUSTOM, classes = {PackTypeFilter.class}))
public class AppConfig {}
</code>Only custom filters are applied; default @Component detection is disabled.
2.7 Specifying nameGenerator
<code>public class PackBeanNameGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
String beanName = super.generateBeanName(definition, registry);
beanName = beanName.replaceFirst("^.", beanName.substring(0, 1).toUpperCase());
return "pack" + beanName;
}
}
@ComponentScan(nameGenerator = PackBeanNameGenerator.class)
public class AppConfig {}
</code>All bean names receive the pack prefix.
2.8 Specifying scopedProxy
<code>@Component
@Scope
public class A implements InitializingBean {}
@ComponentScan(scopedProxy = ScopedProxyMode.TARGET_CLASS)
public class AppConfig {}
</code>The bean A is proxied using CGLIB.
2.9 Specifying scopeResolver
<code>@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PackScope {
@AliasFor("scopeName") String value() default "";
@AliasFor("value") String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
public class PackAnnotationScopeMetadataResolver extends AnnotationScopeMetadataResolver {
public PackAnnotationScopeMetadataResolver() {
setScopeAnnotationType(PackScope.class);
}
}
@PackScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class A implements InitializingBean {}
</code>Custom scope annotation and resolver are applied successfully.
Conclusion
The article covered every aspect of the @ComponentScan annotation, including package selection, filter types, lazy initialization, custom bean name generation, scoped proxies, and custom scope resolution, providing a solid foundation for advanced Spring Boot configuration.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.