【源码】Spring —— ClassPathBeanDefinitionScanner 解读
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
Spring容器类 AnnotationConfigApplicationContext,有两个成员属性 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 用于解析并注册对应的 BeanDefinition 对象到容器中。本章节,介绍 ClassPathBeanDefinitionScanner
版本
Spring 5.2.x
ClassPathBeanDefinitionScanner
与 AnnotatedBeanDefinitionReader 不同的是,ClassPathBeanDefinitionScanner 是基于路径扫描的,也就是说我们也可以用以下方式初始化容器
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext("com.xsn.config");
属性
private final BeanDefinitionRegistry registry;
private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
@Nullable
private String[] autowireCandidatePatterns;
/**
* public static final AnnotationBeanNameGenerator INSTANCE
* = new AnnotationBeanNameGenerator();
*/
private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
// 是否注册基于注解相关的 后置处理器
private boolean includeAnnotationConfig = true;
- BeanDefinitionRegistry,注册中心,此处就是容器本身
- BeanDefinitionDefaults,一个简易的 BeanDefinition 封装
- AnnotationBeanNameGenerator,用于给被标注了
@Component以及被标注@Component注解的其他注解(比如@Service等)所标注的bean,以及被其他规范下如@ManagedBean @Named等注解标注的bean生成对应的beanName,如果无法从注解的属性中解析,则根据一定规则处理类名生成,比如 ExampleClass -> exampleClass - AnnotationScopeMetadataResolver,用于解析对应的 ScopeMetadata
- ConditionEvaluator,用于解析 Condition 相关注解,判断是否注册目标 BeanDefinition
构造
// 略去其他空壳构造
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
this.registry = registry;
// 这里会根据 useDefaultFilters 选择是否注册对应的 TypeFilter
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
// 包含和排除的 Type,父类属性
private final List<TypeFilter> includeFilters = new LinkedList<>();
private final List<TypeFilter> excludeFilters = new LinkedList<>();
- 根据
useDefaultFilters选择是否注册对应的TypeFilter - 属性
includeFiltersexcludeFilters决定是否包含|排除对应的元素 - 维护
environmentresourceLoader
BeanDefinition 的注册(scan)
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 返回注册的 BeanDefinition 数
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
该方法注册对应的 BeanDefinition 并返回对应的数目,其中值得一提的是会根据 includeAnnotationConfig(默认 true)选择是否注册以下 BeanDefinition
- ConfigurationClassPostProcessor,不存在对应 BeanDefinition 则注册,支持
@Configuration注解的解析 - AutowiredAnnotationBeanPostProcessor,不存在对应 BeanDefinition 则注册,支持
@Autowired @Value等注解的解析 - CommonAnnotationBeanPostProcessor,引入 了JSR-250 相关依赖且不存在对应 BeanDefinition 则注册,支持
@Resource等注解的解析 - PersistenceAnnotationBeanPostProcessor,引入了 JPA 相关依赖且不存在对应 BeanDefinition 则注册,支持 JPA 相关后处理
- EventListenerMethodProcessor,不存在对应 BeanDefinition 则注册,负责对
@EventListener注解标注的方法进行后处理 - DefaultEventListenerFactory,不存在对应 BeanDefinition 则注册,主要由
EventListenerMethodProcessor使用,用来基于@EventListener注解标注的方法获取对应ApplicationListener
doScan
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 获取 basePackages 类路径下所有的 BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 获取 BeanDefinition 的 ScopeMetadata 属性
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成 beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 后置处理
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 根据 元数据 解析 Lazy Primary DependsOn Role Description 属性
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检验是否需要注册
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 如果需要代理则代理处理
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
// 不同于 AnnotatedBeanDefinitionReader 的 register方法
// 此处返回所有 BeanDefinition
return beanDefinitions;
}
- 从父类的
findCandidateComponents方法获取所有BeanDefinition,该方法下面会重点解读 ScopeMetadata解析beanName生成postProcessBeanDefinition方法对BeanDefinition做了初步处理:1)根据给定的beanDefinitionDefaults设置部分属性 2)如果指定了autowireCandidatePatterns,依此设置autowireCandidate属性- 根据
元数据解析LazyPrimaryDependsOnRoleDescription属性 - 校验目标
BeanDefinition是否需要注册,即是否已经存在一个可以与之兼容的BeanDefinition - 如果校验通过,封装目标
BeanDefinition并调用AnnotationConfigUtils.applyScopedProxyMode 方法,逻辑简述:根据之前解析的ScopeMetadata判断是否需要代理,如果需要代理,则会值得目标类型为ScopedProxyFactoryBean:这主要是针对作用域实例需要代理的场景 - 最后注册目标
BeanDefinition
ClassPathScanningCandidateComponentProvider#findCandidateComponents
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 获取指定 basePackage 下的所有类
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
// 获取对应的 MetadataReader
// 我们可以从 MetadataReader 中获取对应的 ClassMetadata AnnotationMetadata
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 先进行 excludeFilters includeFilters Condition 过滤
if (isCandidateComponent(metadataReader)) {
// 创建的是 ScannedGenericBeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
// 根据解析的 AnnotationMetadata 校验,必须满足
// 1.isIndependent(顶层类或者静态内部类)
// 2.是个具体的类或者是个含有标注了 Lookup 注解方法的抽象类
// ...
}
else {
// ...
}
}
catch (Throwable ex) {
// ...
}
}
else {
// ...
}
}
}
catch (IOException ex) {
// ...
}
return candidates;
}
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
// 排除过滤
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
// 包含过滤
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
// Condition 过滤
return isConditionMatch(metadataReader);
}
}
return false;
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
// isIndependent(顶层类或者静态内部类)
// && 是个具体的类或者是个含有标注了 Lookup 注解方法的抽象类
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
重点聚焦于方法 scanCandidateComponents,流程总结:
-
根据路径加载所有的类资源
Resource集合 -
遍历上述集合,基于
MetadataReaderFactory获取对应的MetadataReader,Spring中一般使用的都是CachingMetadataReaderFactory,它构造的是SimpleMetadataReader -
先根据
excludeFiltersincludeFiltersconditionEvaluator过滤是否匹配 -
如果通过,则构造对应的
ScannedGenericBeanDefinition,这里的构造很简单,只是额外sbd.setSource(resource) -
再次进行过滤,此处的过滤逻辑为:
- 1)目标类是否
TOP LEVEL CLASS或NESTED CLASS,并且 - 2)目标类不能是一个接口或抽象类,或者是个含有被
Lookup直接注解(元注解)标注方法的抽象类
- 1)目标类是否
总结
这是基于路径扫描 BeanDefinition 的常用类,比如 @ComponentScan 注解对应路径的扫描就是由它来完成的