如何用ClassPathScanningCandidateComponentProvider查找带指定注解的接口?
问题描述
因org.reflections:reflections库存在严重bug,计划用org.springframework:spring-context的ClassPathScanningCandidateComponentProvider替代。需要实现以下功能:查找指定包下所有带@Path注解的类、接口,以及这些类/接口的子类/实现类。
原Reflections实现代码:
var subTypeScanner = new SubTypesScanner( true ); var typeAnnotationScanner = new TypeAnnotationsScanner(); var configBuilder = new ConfigurationBuilder().setUrls( ClasspathHelper.forPackage( "com.example" ) ) .setScanners( subTypeScanner, typeAnnotationScanner ); Reflections reflections= new Reflections( configBuilder ); Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Path.class);
已实现子类/实现类的查找,但当前代码无法找到带@Path注解的接口,只能找到带注解的类,以及继承/实现带注解类/接口的类。现有代码片段:
protected Set< Class< ? > > findClasses( ClassPathScanningCandidateComponentProvider aScanner ) { return aScanner.findCandidateComponents( "com.example" ) .stream() .map( BeanDefinition::getBeanClassName ) .map( e -> { try { return Class.forName( e ); } catch( ClassNotFoundException aE ) { throw new RuntimeException( aE ); } } ) .collect( Collectors.toUnmodifiableSet() ); } protected Set< Class< ? > > findSubClasses( Set< Class< ? > > aClasses ) { return aClasses.stream() .map( aClass -> { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( false ); scanner.addIncludeFilter( new AssignableTypeFilter( aClass ) ); return scanner.findCandidateComponents( "com.example" ); } ) .flatMap(Collection::stream) .map( BeanDefinition::getBeanClassName ) .map( e -> { try { return Class.forName( e ); } catch( ClassNotFoundException aE ) { throw new RuntimeException( aE ); } } ) .collect( Collectors.toUnmodifiableSet() ); }
解决方案
核心问题
默认的ClassPathScanningCandidateComponentProvider在不配置自定义过滤器时,只会扫描类,不会包含接口。需要自定义TypeFilter来匹配带@Path注解的类和接口,再结合子类/实现类的扫描,最终合并结果。
实现步骤
- 创建支持扫描注解接口的扫描器,自定义
TypeFilter检查类/接口上的@Path注解 - 扫描所有带注解的类和接口
- 扫描这些类/接口的子类/实现类
- 合并去重结果
完整代码示例
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.TypeFilter; import java.lang.annotation.Annotation; import java.util.*; import java.util.stream.Collectors; public class ComponentScanner { private final Class<? extends Annotation> targetAnnotation; private final String basePackage; public ComponentScanner(Class<? extends Annotation> targetAnnotation, String basePackage) { this.targetAnnotation = targetAnnotation; this.basePackage = basePackage; } public Set<Class<?>> scanAllMatchingTypes() { // 1. 扫描所有带目标注解的类和接口 Set<Class<?>> annotatedTypes = scanAnnotatedTypes(); // 2. 扫描这些类型的子类/实现类 Set<Class<?>> subTypes = scanSubTypes(annotatedTypes); // 3. 合并去重 Set<Class<?>> result = new HashSet<>(annotatedTypes); result.addAll(subTypes); return Collections.unmodifiableSet(result); } private Set<Class<?>> scanAnnotatedTypes() { // 创建不使用默认过滤器的扫描器 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // 添加自定义过滤器:匹配带目标注解的类或接口 TypeFilter annotationFilter = (metadataReader, metadataReaderFactory) -> metadataReader.getAnnotationMetadata().hasAnnotation(targetAnnotation.getName()) // 若需支持注解继承(如注解本身带@Inherited),可添加下面的判断 // || metadataReader.getAnnotationMetadata().hasMetaAnnotation(targetAnnotation.getName()) ; scanner.addIncludeFilter(annotationFilter); return scanner.findCandidateComponents(basePackage) .stream() .map(beanDef -> { try { return Class.forName(beanDef.getBeanClassName()); } catch (ClassNotFoundException e) { throw new RuntimeException("Failed to load class: " + beanDef.getBeanClassName(), e); } }) .collect(Collectors.toUnmodifiableSet()); } private Set<Class<?>> scanSubTypes(Set<Class<?>> parentTypes) { return parentTypes.stream() .flatMap(parentType -> { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // 添加过滤器:匹配继承/实现parentType的类(含接口实现类),排除父类型本身 scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> { try { Class<?> currentClass = Class.forName(metadataReader.getClassMetadata().getClassName()); return parentType.isAssignableFrom(currentClass) && !parentType.equals(currentClass); } catch (ClassNotFoundException e) { throw new RuntimeException("Failed to load class: " + metadataReader.getClassMetadata().getClassName(), e); } }); return scanner.findCandidateComponents(basePackage).stream(); }) .map(beanDef -> { try { return Class.forName(beanDef.getBeanClassName()); } catch (ClassNotFoundException e) { throw new RuntimeException("Failed to load class: " + beanDef.getBeanClassName(), e); } }) .collect(Collectors.toUnmodifiableSet()); } // 使用示例 public static void main(String[] args) { ComponentScanner scanner = new ComponentScanner(Path.class, "com.example"); Set<Class<?>> allMatchingTypes = scanner.scanAllMatchingTypes(); allMatchingTypes.forEach(type -> System.out.println("Found type: " + type.getName())); } }
关键说明
- 自定义TypeFilter:通过
metadataReader.getAnnotationMetadata()可获取类/接口的注解信息,不管是类还是接口都能被检测到。 - 子类/实现类扫描:使用
isAssignableFrom判断,同时支持类继承和接口实现,排除父类/接口本身避免重复。 - 结果合并:将带注解的类型和它们的子类/实现类合并,实现与原Reflections一致的效果。
内容的提问来源于stack exchange,提问作者Mariusz
相关产品推荐
相关产品推荐

