Spring中ImportBeanDefinitionRegistrar的实现原理

387 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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.classthis.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.registrythis.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()