重读Spring系列(3)-@SpringBootApplication注解的详细解析

这一篇我们来梳理下@SpringBootApplication这个注解它注入了那些内容。

一、结构分析

1、main方法使用

​ 我们知道@SpringBootApplication是用在Main方法上面的,例如:

@SpringBootApplication
public class SpringBootSimpleDemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(SpringBootSimpleDemoApplication.class, args);
   }

}

​ 通过怎样,其实就能通过入参SpringBootSimpleDemoApplication.class,来获取到@SpringBootApplication

2、@SpringBootApplication结构

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   @AliasFor(annotation = EnableAutoConfiguration.class)
   Class<?>[] exclude() default {};

   @AliasFor(annotation = EnableAutoConfiguration.class)
   String[] excludeName() default {};

   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};

   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

​ 我们进入这个方法里面,可以看到这个方法里面又引入了几个其他的注解。我们先看@ComponentScan,我们知道这个是用来自动扫描包路径的组件的,同时这里又引入了两个排除FilterTypeTypeExcludeFilterAutoConfigurationExcludeFilter

​ 再之后是SpringBootConfiguration,这个注解其实没有什么特殊的内容,主要是用来标记这个是SpringBoot配置,就是引入了@Configuration注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

​ 然后是@EnableAutoConfiguration,这种就是@Enablexxx,用来自动注入的,我们之后的源码也 就是主要分析这类的逻辑。我们之后也可以自己实现一个自定义的@Enablexxx

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   Class<?>[] exclude() default {};

   String[] excludeName() default {};

}

二、关于阅读下面流程来拓展Spring的扫描组件注解

​ 这一项是我们来拓展Spring的源码

1、拓展一个与@Component同样的注入组件@BeanClass

​ 我们一般主动注入一个Bean可以是下面的样式,这里以主动注入:

@Configuration
public class MyConfiguration {

    @Bean
    public Book book()
    {
        return new Book();
    }

}

​ 当然我们也可以直接使用@Service(也就是@Component)注解,下面我们就来独立定义一个主键注解,当然不是不是想@Service本身主要是为了引入@Component

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

   @AliasFor(annotation = Component.class)
   String value() default "";

}

​ 我们是与@Component同级别的。

1)、demo1

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface BeanClass {
}

​ 这个注解我们将其命名为@BeanClass,然后也是使用在类上面的。

再定义一个TypeFilter,如果一个类上面有这个注解,我们就返回true

public class BeanClassTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (metadataReader.getAnnotationMetadata().isAnnotated(BeanClass.class.getName()))
        {
            return true;
        }
        return false;
    }
}

然后再使用@ComponentScan引入BeanClassTypeFilter,并指定扫描的包名。

@Configuration
@ComponentScan(basePackages = "com.fev.springboot.simple.demo",includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = BeanClassTypeFilter.class)})
public class MyConfiguration {

}

​ 最后启动SpringBoot项目测试,就能获取到了,感觉这个demo其实可以放在上一篇文章。

在这里插入图片描述

​ 这里我们启动测试类,就能注入Book了。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringBootSimpleDemoApplicationTests {

   @Autowired
   private ApplicationContext applicationContext;

   @Test
   public void test1()
   {
      Book contextBean = applicationContext.getBean(Book.class);
      System.out.println("............" + contextBean);
   }

}
............com.fev.springboot.simple.demo.Book@33eb6758

​ 当然这个目前可能不知道为什么,但看到后面源码部分我们就知道了。

三、@ComponentScan在@SpringBootApplication中的源码分析

​ 我们在上一篇是有分析过@ComponentScan的整体逻辑的,其的解析是在ConfigurationClassPostProcessor类中的。这里我们直接去doProcessConfigurationClass方法。

1、doProcessConfigurationClass (ConfigurationClassPostProcessor)

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
		...........
   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }
	..........
   // No superclass -> processing is complete
   return null;
}

在这里插入图片描述

​ 首先这里是我们会扫描到这个注解的信息,包括两个排除FilterexcludeFilters

​ 然后便是this.conditionEvaluator.shouldSkip,这里目前是会false,这个ConditionEvaluator我们本篇之后会具体讲。再就是this.componentScanParser.parse解析获取这个@ComponentScan要扫描的包下面的组件了。当前componentScanParserComponentScanAnnotationParser

2、this.componentScanParser.parse (ComponentScanAnnotationParser)

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
   ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
         componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
		.......
   for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addIncludeFilter(typeFilter);
      }
   }
   for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addExcludeFilter(typeFilter);
      }
   }
		.........
   Set<String> basePackages = new LinkedHashSet<>();
   String[] basePackagesArray = componentScan.getStringArray("basePackages");
   for (String pkg : basePackagesArray) {
      String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      Collections.addAll(basePackages, tokenized);
   }
   for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
   }
   if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(declaringClass));
   }

   scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
         return declaringClass.equals(className);
      }
   });
   return scanner.doScan(StringUtils.toStringArray(basePackages));
}

​ 上面这个就是构建ClassPathBeanDefinitionScanner scanner,并且讲@ComponentSans的注入信息添加到scanner中,例如IncludeFilterExcludeFilter这些。然后就是一个扫描包的路径问题,如果你有指定要扫描的包名basePackages,其就会是指定的包名,如果没有指定basePackages.isEmpty(),其就会是declaringClass也就是被@ComponentScan注解的类的包名。之后就是具体的scanner.doScan(StringUtils.toStringArray(basePackages))了。

​ 同时这里我们可以注意到这里还另外加了一个ExcludeFilterscanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false)

3、scanner.doScan (ClassPathBeanDefinitionScanner)

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         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);
         }
      }
   }
   return beanDefinitions;
}	

这里首先是通过findCandidateComponents获取到这个包下面所有的.classResource文件。

1)、findCandidateComponents (重要)

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		......
      for (Resource resource : resources) {
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);
                  }
                  .........
               }
               ....
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
    		..........
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@RestController
public class MyController {

    @RequestMapping(value = "hello",method = RequestMethod.GET)
    public String hello(@RequestParam String name, @RequestParam Integer age)
    {
        return "Hello " + name + " - " + age;
    }

}
public class Book {

    private Integer age;

    private String name;
	........
}

​ 再通过isCandidateComponent判断这个资源是不是需要注入的组件,如果是的话就添加到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())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

​ 这里就是前面注入的excludeFiltersincludeFilters,排除与添加判断了。

这里的excludeFilters我们在前面是有注入3个:然后我们来看下逻辑:

第一个是判断是否是定义的declaringClass,也就是被@ComponentScan注解的类,如果是就排除:

scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
   @Override
   protected boolean matchClassName(String className) {
      return declaringClass.equals(className);
   }
});

第二个是AutoConfigurationExcludeFilter,这个是用来判断是不是自动注入的配置类。

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {

   private ClassLoader beanClassLoader;

   private volatile List<String> autoConfigurations;

   @Override
   public void setBeanClassLoader(ClassLoader beanClassLoader) {
      this.beanClassLoader = beanClassLoader;
   }

   @Override
   public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
         throws IOException {
      return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
   }

   private boolean isConfiguration(MetadataReader metadataReader) {
      return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
   }

   private boolean isAutoConfiguration(MetadataReader metadataReader) {
      return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
   }

   protected List<String> getAutoConfigurations() {
      if (this.autoConfigurations == null) {
         this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
               this.beanClassLoader);
      }
      return this.autoConfigurations;
   }

}

​ 这里我们看到其如果有扫描到@ConfigurationEnableAutoConfiguration的配置,也是排除匹配,就不注入这个资源类。

这里的SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,this.beanClassLoader)我们知道其实加载spring.factories配置的值。例如这个EnableAutoConfiguration就会加载很多这个xxxAutoConfiguration

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
		.........
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
.......

​ 这里如果是@Component扫描到的,其就会先忽略,因为@Configuration我们知道是有专门处理的,然后这个EnableAutoConfiguration配置也是有其他地方专门处理。这个我们本篇后面也会梳理到。

​ 所有这里再扫描中,对于目前我们的两个使用的BookMyController都是不会排除的return false;。下面我们就来看下加载的TypeFilter了。

这里是两个都是AnnotationTypeFilter

在这里插入图片描述

​ 主要判断是:

@Override
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

​ 也就是判断扫描到的类有没有注解为annotationType的,这里也就是两个注解@ComponentManagedBean,即一般的组件,扫描的就返回true,加到candidates中返回。

public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter {

   private final Class<? extends Annotation> annotationType;

   private final boolean considerMetaAnnotations;

​ 所以这里会扫描到MyController,因为RestController的上层就是@Component注解。

2)、遍历处理candidates

​ 上面处理扫描到组件后就是遍历,根据不同情况进行处理。

for (BeanDefinition candidate : candidates) {
   ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
   candidate.setScope(scopeMetadata.getScopeName());
   String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
   if (candidate instanceof AbstractBeanDefinition) {
      postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
   }
   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);
   }
}

​ 前面两个if就是填充一些信息,就不展开了。第三个ifcheckCandidate(beanName, candidate)主要是判断当前beanName有没有注册,没有就返回true进入这个分支进行registerBeanDefinition(definitionHolder, this.registry)BeanDefintion的注册。

​ 所以这个@Component注解,在@SpringBootApplication中的作用,就是加载当前被这个@SpringBootApplication注解类的包下面的所有组件。

​ 我们根据这个应该也能自定义拓展一个TypeFilter类型的类,就是我们上面的demo1

四、@EnableAutoConfiguration注解

1、结构

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   Class<?>[] exclude() default {};

   String[] excludeName() default {};

}

​ 这里我们可以看到主要是两个注解@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)

2、@AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

​ 这个主要是引入AutoConfigurationPackages.Registrar

public abstract class AutoConfigurationPackages {
   private static final String BEAN = AutoConfigurationPackages.class.getName();

   public static boolean has(BeanFactory beanFactory) {
      return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
   }
	.........
   public static void register(BeanDefinitionRegistry registry, String... packageNames) {
      if (registry.containsBeanDefinition(BEAN)) {
       	.........
      }
      else {
        	..........
      }
   }

   private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
      String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
      Set<String> merged = new LinkedHashSet<>();
      merged.addAll(Arrays.asList(existing));
      merged.addAll(Arrays.asList(packageNames));
      return StringUtils.toStringArray(merged);
   }

   /**
    * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
    * configuration.
    */
   static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

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

      @Override
      public Set<Object> determineImports(AnnotationMetadata metadata) {
         return Collections.singleton(new PackageImport(metadata));
      }

   }

   /**
    * Wrapper for a package import.
    */
   private static final class PackageImport {

      private final String packageName;

      PackageImport(AnnotationMetadata metadata) {
         this.packageName = ClassUtils.getPackageName(metadata.getClassName());
      }
       ...................

​ 然后这个Registrar是实现的ImportBeanDefinitionRegistrar,这个我们上一篇有梳理,主要是通过registerBeanDefinitions来注入BeanDefintion。,然后这里就是调的:

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

​ 就注入BasePackages.class,同时这个BeanDefintion的角色定义是BeanDefinition.ROLE_INFRASTRUCTURE,即用于Spring结构体系内部的BeanDefintion定义,不过这里我并没有看到这个PackageImportBean的使用,然后这个PackageImport主要是用来记录扫描的包名称。我们可以看到其会使用constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));方法来累计记载。

​ 下面我们就来看一下@SpringBootApplcation引入的重要类AutoConfigurationImportSelector

五、AutoConfigurationImportSelector

1、结构

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

​ 这个它有实现各种Aware接口,我们上篇文章有梳理,就是在正式调用DeferredImportSelector(ImportSelector的子类)前,其会先去调用Aware接口的方法。

​ 不过这个类的主要逻辑是对DeferredImportSelector接口的实现,这个接口是一种延迟筛选执行接口。

2、DeferredImportSelector

1)、类实现

public interface DeferredImportSelector extends ImportSelector {

   @Nullable
   default Class<? extends Group> getImportGroup() {
      return null;
   }

   interface Group {

      void process(AnnotationMetadata metadata, DeferredImportSelector selector);
      Iterable<Entry> selectImports();

      class Entry {

         private final AnnotationMetadata metadata;
         private final String importClassName;

         public Entry(AnnotationMetadata metadata, String importClassName) {
            this.metadata = metadata;
            this.importClassName = importClassName;
         }

         public AnnotationMetadata getMetadata() {
            return this.metadata;
         }

         public String getImportClassName() {
            return this.importClassName;
         }
			.........
      }
   }

}

​ 可以看到这个接口接口主要是一个getImportGroup()方法,同时这个方法是返回一个Class,这个Class是继承的下面的Group接口,然后其他的就是ImportSelector接口的方法实现,这个Group是用来分组的,就是说,用哪个Group来处理哪一类的DeferredImportSelector。我们通过上一篇文章知道,ImportSelector是用来返回需要注册的具体的类的全名,然后再包装为SourceClass,来进行加载为ConfigurationClass,最后再通过ConfigurationClass来解析构建为对应的BeanDefinition

public interface ImportSelector {

   /**
    * Select and return the names of which class(es) should be imported based on
    * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
    */
   String[] selectImports(AnnotationMetadata importingClassMetadata);

}
if (candidate.isAssignable(ImportSelector.class)) {
   // Candidate class is an ImportSelector -> delegate to it to determine imports
   Class<?> candidateClass = candidate.loadClass();
   ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
         this.environment, this.resourceLoader, this.registry);
   if (selector instanceof DeferredImportSelector) {
      this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
   }
   else {
      String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
      Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
      processImports(configClass, currentSourceClass, importSourceClasses, false);
   }
}

那这个ImportSelector延迟处理是怎样实现的呢?我们可以看到如果不是selector instanceof DeferredImportSelector,其是直接的掉用了``ImportSelectorselector.selectImports(currentSourceClass.getMetadata())方法。然后是DeferredImportSelector`。

2)、AutoConfigurationImportSelector对DeferredImportSelector的实现

@Override
public Class<? extends Group> getImportGroup() {
   return AutoConfigurationGroup.class;
}

​ 我们可以看到这里AutoConfigurationImportSelector使用的处理DeferredImportSelectorAutoConfigurationGroup

3)、DeferredImportSelectorHandler

private class DeferredImportSelectorHandler {

   @Nullable
   private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

   public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
      DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
            configClass, importSelector);
      if (this.deferredImportSelectors == null) {
         DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
         handler.register(holder);
         handler.processGroupImports();
      }
      else {
         this.deferredImportSelectors.add(holder);
      }
   }

   public void process() {
      List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
      this.deferredImportSelectors = null;
      try {
         if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            handler.processGroupImports();
         }
      }
      finally {
         this.deferredImportSelectors = new ArrayList<>();
      }
   }

}
private static class DeferredImportSelectorHolder {

   private final ConfigurationClass configurationClass;
   private final DeferredImportSelector importSelector;
		..........
}

​ 这里是先通过handler方法将入参构建为DeferredImportSelectorHolder,再将其this.deferredImportSelectors.add(holder);。同时这里还有this.deferredImportSelectors == null,这个我们看到没人是不会为null的,但在process()方法执行过程中就会this.deferredImportSelectors = null。不过这两个分支的核心执行都是:

DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();

​ 所以这里的延迟就是先将这些内容存储起来,先不掉用其的selectImports方法,然后我们看第一个处理handler.processGroupImports();process方法:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
         }
         else {
            parse(bd.getBeanClassName(), holder.getBeanName());
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
   }

   this.deferredImportSelectorHandler.process();
}

​ 即这个DeferredImportSelector的正式处理是在parse()方法最后面,所有要处理的@Configuration@Component@ComponentScan这些都已经处理完之后,即所有该扫描加载的BeanDefinition都已经差不多加载完了,再执行的。

​ 下面我们就通过DeferredImportSelectorGroupingHandler来具体看下processGroupImports()方法的执行:

handler.register(holder);
handler.processGroupImports();

​ 这里关键是上面这两个方法的内容,会在这两个方法注入自动配置的内容,我本来是写了一些,当感觉不容易说明白,就去掉了,我们就不具体展开了,这里的关键就是前面的AutoConfigurationGroup类,主要是它处理加载一些内容(类)。然后这两个方法有会掉回AutoConfigurationImportSelector

3、process(AutoConfigurationGroup)

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
   Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
         () -> String.format("Only %s implementations are supported, got %s",
               AutoConfigurationImportSelector.class.getSimpleName(),
               deferredImportSelector.getClass().getName()));
   AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}

​ 这里关键是getAutoConfigurationMetadata()获取自动配置的元素,然后再去调用AutoConfigurationImportSelectorgetAutoConfigurationEntry方法。

private AutoConfigurationMetadata getAutoConfigurationMetadata() {
   if (this.autoConfigurationMetadata == null) {
      this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
   }
   return this.autoConfigurationMetadata;
}
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

private AutoConfigurationMetadataLoader() {
}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
   return loadMetadata(classLoader, PATH);
}
private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {

   private final Properties properties;

​ 这里主要是加载获取META-INF/spring-autoconfigure-metadata.properties的内容:

org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate

org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.core.JdbcTemplate
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=SERVLET
	.................

在这里插入图片描述

​ 我们看到这里的配置是有400多定义的key,下面我们就来看下是怎样使用的

4、getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
protected static class AutoConfigurationEntry {

   private final List<String> configurations;

   private final Set<String> exclusions;

​ 然后这里会返回AutoConfigurationEntry,要注入的configurations(待注入的类名称)、exclusions(应剔除的类名称)。

​ 这里整个流程是通过getCandidateConfigurations(annotationMetadata, attributes)方法获取到spring.factories文件中以EnableAutoConfigurationkeyvalues

​ 然后获取到EnableAutoConfiguration待剔除的类名exclusions,再通过checkExcludedClasses检查configurationsexclusions,如果configurations中没有exclusions即要剔除的类,就会抛出IllegalStateException异常。

​ 再之后是拦截看最终的configurations,已经通过fireAutoConfigurationImportEvents来发布本次自动配置导入事件。

1)、getCandidateConfigurations(@EnableAutoConfiguration)

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
   return EnableAutoConfiguration.class;
}

​ 这里就是获取META-INF/spring.factories中以EnableAutoConfigurationkey的值。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...........

在这里插入图片描述

​ 我们看到通过EnableAutoConfiguration也 就是自动配置的内容是有124个,这些都是xxxAutoConfiguration,也就是自动去注入一些Bean,注入的原理就是通过这些@Conditionxxx来前置判断,看最终需不需要注入这些Bean。这个具体的内容我们下一篇梳理。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass(Advice.class)
   static class AspectJAutoProxyingConfiguration {

      @Configuration(proxyBeanMethods = false)
      @EnableAspectJAutoProxy(proxyTargetClass = false)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
            matchIfMissing = false)
      static class JdkDynamicAutoProxyConfiguration {

      }

      @Configuration(proxyBeanMethods = false)
      @EnableAspectJAutoProxy(proxyTargetClass = true)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
            matchIfMissing = true)
      static class CglibAutoProxyConfiguration {

      }

   }

   @Configuration(proxyBeanMethods = false)
   @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
   @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
         matchIfMissing = true)
   static class ClassProxyingConfiguration {

      ClassProxyingConfiguration(BeanFactory beanFactory) {
         if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
         }
      }

   }

}

2)、filter

private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
   long startTime = System.nanoTime();
   String[] candidates = StringUtils.toStringArray(configurations);
   boolean[] skip = new boolean[candidates.length];
   boolean skipped = false;
   for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
      invokeAwareMethods(filter);
      boolean[] match = filter.match(candidates, autoConfigurationMetadata);
      for (int i = 0; i < match.length; i++) {
         if (!match[i]) {
            skip[i] = true;
            candidates[i] = null;
            skipped = true;
         }
      }
   }
   if (!skipped) {
      return configurations;
   }
   List<String> result = new ArrayList<>(candidates.length);
   for (int i = 0; i < candidates.length; i++) {
      if (!skip[i]) {
         result.add(candidates[i]);
      }
   }
   return new ArrayList<>(result);
}

​ 这里主要是通过AutoConfigurationImportFilter来判断最终candidates有哪些是匹配的,最终将匹配的添加到result来最终返回。然后这个AutoConfigurationImportFilter也同样会先调用invokeAwareMethods(filter)

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
   return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}

​ 这个getAutoConfigurationImportFilters()就是获取以AutoConfigurationImportFilter为key的值。

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

​ 当前是这3个Condition。可以看到这些也是属于Condition这个处理类型的。

​ 通过以上分析我们就知道了@SpringBootApplication是怎样扫描组件,已经怎样自动注入一些默认的配置类的。

这里其实就是有些自定义一个SpringBootstarter启动的过程了,可以看下我前面写的一个demo,即自定义一个SpringBootStarter。关键就是对spring.factoriesEnableAutoConfiguration的value来注入。

​ 然后这里缺的整个Condition的处理过程,我们下一篇再梳理,这里还与另一个我们前面提到的ConditionEvaluator处理有一定相似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值