上一篇幅我们讲到
ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法,这个方法扫描得到所有的BeanDefinition,那具体是怎么扫描得到所有BeanDefinition的呢?postProcessBeanDefinitionRegistry调用了一个核心方法:processConfigBeanDefinitions,代码如下:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//关键点一
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
//关键点二
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//关键点三
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
我们来分析下该方法的几个关键点:
关键点一
调用
ConfigurationClassUtils.checkConfigurationClassCandidate方法来检查有哪些是配置类候选者?因为只有有了配置类,Spring才直到如何进一步扫描。Spring是如何来确定哪些是配置类的呢?且看checkConfigurationClassCandidate方法的逻辑代码:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
......
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}else {
return false;
}
......
}
如上代码,有三种类型的类是配置类:
- 增加了Configuration注解的类,这类配置类的BeanDefinition的CONFIGURATION_CLASS_ATTRIBUTE属性被设置为了CONFIGURATION_CLASS_FULL,还记得我们上一篇幅讲的对于CONFIGURATION_CLASS_FULL这种配置类是需要特别处理的,对,就是在这里标识的。
- 通过isConfigurationCandidate方法检测的类,这类配置类的BeanDefinition的CONFIGURATION_CLASS_ATTRIBUTE属性被设置为了CONFIGURATION_CLASS_LITE,是不是和上面不一样。哪些是通过检测的呢?看代码:
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
看到了吧,加了Component、ComponentScan、Import、ImportResource注解的类,或者这个类里面有方法加了Bean注解的类都是通过检测的类。
关键点二
如果当前BeanFactory支持单例bean,则设置一个BeanNameGenerator,用来在扫描@Component和@Import某个Bean时取一个名字,这里程序员可以通过调用
applicationContext.setBeanNameGenerator方法来指定一个beanName生成器。
关键点三
parser.parse这个是核心中的核心,在”关键点一”中已经获得了需要解析的配置类,然后这里调用parse开始对配置BeanDefinition进行解析,在该方法中会遍历配置类的BeanDefinition,通过ASM这种字节码处理技术来得到资源信息,注意这里不是直接加载字节码到内存中,得到资源信息后,调用
doProcessConfigurationClass方法来进行处理,在该方法中会针对@Component、@PropertySource、@ComponentScans、@ComponentScan、@Import、@ImportResource、@Bean注解的配置类分别处理,由于篇幅原因,详细我们下一篇介绍,本篇幅中大家只要直到,这个方法就是扫描得到具体的BeanDefinition就可以了。
扫描完成后,当前这个注解类也是需要调用
this.reader.loadBeanDefinitions(configClasses)方法注册成为BeanDefinition的;
我们再来关注另外一个问题,这里为什么要用个do…while循环呢?
其实很简单,因为咱们在扫描完成后,肯定会扫描到新的配置类,所以需要循环调用parse来对新的配置类进行处理。