搜索 K
Appearance
Appearance
ClassPathScanningCandidateComponentProvider 是 Spring Framework 自带的「按包扫描候选类」工具,底层基于 ASM 解析字节码,比手写反射或者引入 org.reflections 都更轻量;与 Spring 容器同源,启动时序、ClassLoader 行为天然契合。
配套常用类:
| 类 | 作用 |
|---|---|
ClassPathScanningCandidateComponentProvider | 扫描入口,可设置 includeFilters / excludeFilters |
AnnotationTypeFilter | 一个开箱即用的 filter,按注解筛 |
BeanDefinition | 扫描结果的载体,可通过 getBeanClassName() 拿到全限定类名 |
AnnotatedBeanDefinition | BeanDefinition 的子类型,附带注解元数据 |
为什么不是 spring-core 而是 spring-context?
ClassPathScanningCandidateComponentProvider 位于 org.springframework.context.annotation,属于 spring-context 包。所以 forge-ddl 的 pom.xml 显式声明了 spring-context 依赖。
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(false); // false = 不读取默认过滤器
provider.addIncludeFilter(new AnnotationTypeFilter(Model.class));
for (BeanDefinition bd : provider.findCandidateComponents("com.demo")) {
String className = bd.getBeanClassName();
Class<?> clazz = Class.forName(className);
// do something
}构造器 useDefaultFilters 传 false 是惯用法:默认 filters 会把 @Component 系也加进来,本框架只想要 @Model,所以关掉。
isCandidateComponent 默认实现要求被扫描的类是「非抽象、非接口、独立类」。如果想放开(比如允许抽象类),就匿名子类化覆写:
new ClassPathScanningCandidateComponentProvider(false) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition definition) {
return definition.getMetadata().isIndependent();
}
};ClazzScanner#buildProvider 同时使用了「关默认 filter + 覆写 isCandidateComponent + Include filter + Exclude filter」四种手法:
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition definition) {
return definition.getMetadata().isIndependent()
&& !definition.getMetadata().isAbstract()
&& !definition.getMetadata().isInterface();
}
};
provider.addIncludeFilter(new AnnotationTypeFilter(Model.class));
provider.addExcludeFilter((reader, factory) ->
reader.getClassMetadata().getClassName().contains("$")); // 排除内部类之后 scan(...) 用 Class.forName(className, false, ContextClassLoader) 把类加载进来,并用 clazz.isMemberClass() / isAnonymousClass() / isLocalClass() 再过一遍兜底,确保最终只剩“独立的、可实例化的、标了 @Model 的”普通类。
→ 详细见 《扫描机制》