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.
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 {} 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()));
}
}
}Output shows both A and B are registered.
2.2 Specifying basePackages
@ComponentScan(basePackages = {"com.pack.ioc.scan_component.child"})
public class AppConfig {}Only beans in the specified sub‑package ( B) are registered.
2.3 Specifying basePackageClasses
@ComponentScan(basePackageClasses = {B.class})
public class AppConfig {}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:
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}2.4.1 ANNOTATION
@ComponentScan(includeFilters = @Filter(type = FilterType.ANNOTATION, classes = Pack.class))
public class AppConfig {}Custom annotation @Pack is detected and its beans are registered.
2.4.2 ASPECTJ
@ComponentScan(includeFilters = @Filter(type = FilterType.ASPECTJ, pattern = {"com.pack.*Pack*"}))
public class AppConfig {}2.4.3 ASSIGNABLE_TYPE
@ComponentScan(includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DAO.class}))
public class AppConfig {}All classes implementing DAO are scanned.
2.4.4 REGEX
@ComponentScan(includeFilters = @Filter(type = FilterType.REGEX, pattern = {"com\\.pack\\..*Pack.*"}))
public class AppConfig {}2.4.5 CUSTOM
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 {}2.5 Specifying lazyInit
@ComponentScan(lazyInit = true)
public class AppConfig {}Beans are created lazily; initialization callbacks are not executed at context startup.
2.6 Specifying useDefaultFilters
@ComponentScan(useDefaultFilters = false, includeFilters = @Filter(type = FilterType.CUSTOM, classes = {PackTypeFilter.class}))
public class AppConfig {}Only custom filters are applied; default @Component detection is disabled.
2.7 Specifying nameGenerator
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 {}All bean names receive the pack prefix.
2.8 Specifying scopedProxy
@Component
@Scope
public class A implements InitializingBean {}
@ComponentScan(scopedProxy = ScopedProxyMode.TARGET_CLASS)
public class AppConfig {}The bean A is proxied using CGLIB.
2.9 Specifying scopeResolver
@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 {}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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
