SpringBoot之处理@SpringBootApplication的两个import

都知道注解SpringBootApplication有两个import

1  @Import(AutoConfigurationImportSelector.class)

2  @Import(AutoConfigurationPackages.Registrar.class)

第一个的作用和原理我看网上说的挺多的,但是第二个貌似不多。今天我就分析分析@Import(AutoConfigurationPackages.Registrar.class)的作用

先看看方法内的注解

/**
 * Class for storing auto-configuration packages for reference later (e.g. by JPA entity
 * scanner).

  一个Class,保存了自动装配的包路径,为了后续引用的使用(例如JPA实体类的扫描)

  简单点说就是保存自动装配的路径的

  现在跟随代码看看它是怎么生效的

   ConfigurationClassPostProcessor.processConfigBeanDefinitions 代码片段

  

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);

  经过了parser解析之后,我们最终能得到configClasses这些都是带有@Configuration注解的

  本篇我们不分析  AutoConfigurationImportSelector 但是他的作用还是要提一下,在 parser.parse 就会完成对AutoConfigurationImportSelector的处理,也就是把各个包下的spring.factory里的自动装配相关的class都选出来。所以上面代码中configClasses多达76个。而我们的启动类只是其中之一。

ConfigurationClass里有一个字段专门用来保存Registrar的。比如我们的启动类对应的 ConfigurationClass 就有两个 Registrar。除了自带的AutoConfigurationPackages.Registrar还有
MapperScannerRegistrar那是因为在启动类里配了Mybatis的scan路径
  
接下来就是分析  this.reader.loadBeanDefinitions(configClasses); 
  最终会执行  ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars 
  
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
        registrars.forEach((registrar, metadata) ->
                registrar.registerBeanDefinitions(metadata, this.registry));
    }
AutoConfigurationPackages$Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

  最终调用的是  AutoConfigurationPackages.register

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        if (registry.containsBeanDefinition(BEAN)) { //跟代码的时候走不到这里
            BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
            ConstructorArgumentValues constructorArguments = beanDefinition
                    .getConstructorArgumentValues();
            constructorArguments.addIndexedArgumentValue(0,
                    addBasePackages(constructorArguments, packageNames));
        }
        else {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(BasePackages.class);
            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
                    packageNames);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(BEAN, beanDefinition);//就是注册个beanDefinition,类是 BasePackages
        }
    }

  这里的 packageNames 如果没有配的就是启动类所在的路径  

二 被调用处

   AutoConfigurationPackages.get 该方法的作用就是返回上面提到的路径 我的这个例子就是 com.example.demo

public static List<String> get(BeanFactory beanFactory) {
        try {
            return beanFactory.getBean(BEAN, BasePackages.class).get();
        }
        catch (NoSuchBeanDefinitionException ex) {
            throw new IllegalStateException(
                    "Unable to retrieve @EnableAutoConfiguration base packages");
        }
    }

  那么这个get又有啥作用呢

  我们以 AutoConfiguredMapperScannerRegistrar 为例

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      if (!AutoConfigurationPackages.has(this.beanFactory)) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
        return;
      }

      logger.debug("Searching for mappers annotated with @Mapper");

      List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
      if (logger.isDebugEnabled()) {
        packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
      }

  拿到这个扫包根路径之后就可以扫包了

posted on 2020-12-25 11:38  MaXianZhe  阅读(870)  评论(0编辑  收藏  举报

导航