开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第25天,点击查看活动详情
前言
在上一篇文章中,我们讲述了如何使用ImportBeanDefinitionRegistrar实现动态创建Bean对象,在文章中我们模仿mybatis的@MapperScans代码,自己写了一个动态创建验证码Bean对象的示例;这篇文章中,我们将揭开ImportBeanDefinitionRegistrar的实现原理;
ConfigurationClassPostProcessor
在ConfigurationClassPostProcessor中,它实现了BeanDefinitionRegistryPostProcessor,那么我们就要看看postProcessBeanDefinitionRegistry()方法都做了啥:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
} else if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);
} else {
this.registriesPostProcessed.add(registryId);
this.processConfigBeanDefinitions(registry);
}
}
主要就调用了processConfigBeanDefinitions()方法,我们把关键代码拎出来:
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
HashSet alreadyParsed = new HashSet(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
我们的ImportBeanDefinitionRegistrar就是在parser.parse(candidates)这个方法中被解析出来的:
if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
// 把解析出来的ImportBeanDefinitionRegistrar放到configClass中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
在ConfigurationClassParser中解析出来的ImportBeanDefinitionRegistrar会被放进configClass中,然后在ConfigurationClassPostProcessor中被this.reader.loadBeanDefinitions(configClasses)调用,最终将调用到下面这行代码:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) -> {
// 调用ImportBeanDefinitionRegistrar接口方法
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator);
});
}
这段代码是不是很熟悉,不过它比我们实现的registerBeanDefinitions()方法多了一个参数,但是你如果打开ImportBeanDefinitionRegistrar这个接口就会恍然大悟:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
this.registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
原来这个接口里面有两个方法,第一个方法默认会调用第二个方法,BeanNameGenerator是专门用来生成BeanName的,如果你不知道如何给你的Bean命名,那么可以重写第一个方法让Spring给你的Bean生成BeanName;
小结
从上面的解析我们可以了解到:
1.动态生成Bean都是基于BeanDefinitionRegistryPostProcessor实现的,因为BeanDefinitionRegistryPostProcessor可以动态注册BeanDefinition;
2.ImportBeanDefinitionRegistrar的实现是通过ConfigurationClassPostProcessor解析后,统一调用registerBeanDefinitions();