Springboot 自动装配解析

160 阅读4分钟

processon

首先也还是先给出自己的processon地址:www.processon.com/view/617cfb…

多余的话

首先网上关于springboot自动装配的名词解释,springboot什么约定大于配置,什么@EnableAutoConfiguration注解等等其实都已经有了非常到位的解释,正常应对面试说实话都是绰绰有余的。 所以此篇文章还是主要以我自己读取源码,分析源码来讲述springboot自动装配的起始与终结。

个人分析

很多人讲springboot的自动装配都会从springboot的主注解讲到@EnableAutoConfiguration注解再讲到 AutoConfigurationImportSelector类,然后再讲这个类里做了什么。。。。

对于我来说这样分析确实是不完整的或者说有些断章取义的,如果确实对springboot源码已经有了相关的阅读,对整体的架子有了一些稍微的认知的话。那所有的分析的入口便绝对不是这个注解了。(如果没有阅读源码的相关理解,这个注解确实是一个切入口)

spring容器加载bean对象按照大步骤来说的话其实就2步。

1.获取beanDefinition信息。 2.根据definition信息生成bean。

这样来理解总体的话也就大致清楚了自动装配的起点在哪里。其实就是在加载beanDefinition的时候。

入口

AbstractApplicationContext类中refresh方法中的invokeBeanFactoryPostProcessors方法 也就是调用bean工厂的后置处理器。(注:BeanFactoryPostProcessor和BeanPostProcessor这两个接口对于我现在的认知来说是spring中极为重要的两个接口,我会单独开一章来讨论这两个接口)

说到自动装配不得不提到的非常重要的类ConfigurationClassPostProcessor。它实现BeanFactoryPostProcessor接口也就是说对于beanDefinition信息的加载它做了很多事情。

源码解析

Snipaste_2022-03-31_16-18-42.png

image.png

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
          //如果配置bean是以注解方式注入走此方法 
         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();

走到doProcessConfigurationClass方法正式处理配置bean的信息

//处理 @PropertySource 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
      sourceClass.getMetadata(), PropertySources.class,
      org.springframework.context.annotation.PropertySource.class)) {
   if (this.environment instanceof ConfigurableEnvironment) {
      processPropertySource(propertySource);
   }
   else {
      logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
            "]. Reason: Environment must implement ConfigurableEnvironment");
   }
}

// 处理我们非常熟悉的 @ComponentScan 注解
// 也就是扫包把类上有 @Component 等我们熟悉的mvc的相关注解类加入beanDefinition后续生成bean
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());
         }
      }
   }
}

// 处理 @Import注解 
// processImports方法非常重要下面讲
// getImports(sourceClass) 递归找出所有import注解里面的类
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

// 处理 @ImportResource注解
AnnotationAttributes importResource =
      AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
   String[] resources = importResource.getStringArray("locations");
   Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
   for (String resource : resources) {
      String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
      configClass.addImportedResource(resolvedResource, readerClass);
   }
}

// 处理 @Bean 修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

processImports方法及其重要

众所周知,将对象加入spring的方式有很多种,其中就有

@Import(A.class)

@Import(B.class) B实现了ImportSelector重写selectImports方法,返回的String[](存放类的名称的数组)会加入到容器

@Import(C.class) C实现了ImportBeanDefinitionRegistrar重写registerBeanDefinitions将beanDefinition信息注册到容器

processImports大致就处理了这三种实现 当然还后一个比较特殊就是自动装配的 DeferredImportSelector

try {
   for (SourceClass candidate : importCandidates) {
      //如果实现了ImportSelector接口
      if (candidate.isAssignable(ImportSelector.class)) {
         Class<?> candidateClass = candidate.loadClass();
         ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
               this.environment, this.resourceLoader, this.registry);
         Predicate<String> selectorFilter = selector.getExclusionFilter();
         if (selectorFilter != null) {
            exclusionFilter = exclusionFilter.or(selectorFilter);
         }
         if (selector instanceof DeferredImportSelector) {
            //点进此方法可以发现把对应的类信息加入到了deferredImportSelectors这个集合里面
            //这个集合非常眼熟其实就是上方第一张截图的集合
            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
         }
         else {
            //获取到String[](存放类的名称的数组)然后递归处理里面的类
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
         }
      }
      //如果实现了ImportBeanDefinitionRegistrar接口 就委托给它注册额外的bean定义 
      else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
         Class<?> candidateClass = candidate.loadClass();
         ImportBeanDefinitionRegistrar registrar =
               ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                     this.environment, this.resourceLoader, this.registry);
         configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
      }
      else {
         //此处英文注解也写得很清楚 如果不是ImportSelector ImportBeanDefinitionRegistrar就是@Import(A.class) 直接按照@Configuration处理
         this.importStack.registerImport(
               currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
         processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
      }
   }
}

然后我们分析这个接口DeferredImportSelector,发现@EnableAutoConfiguration注解中AutoConfigurationImportSelector这个类实现了这个延迟导入的接口,于是乎判定自动装配的实现肯定在deferredImportSelectors集合里也就是第一张截图的地方。

image.png

image.png

image.png


protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//看到loadFactoryNames就已经非常明了了。
//getSpringFactoriesLoaderFactoryClass() 返回的就是EnableAutoConfiguration.class类

   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   //获取EnableAutoConfiguration所需要加载全类名
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {
      //FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }

至此自动装配的大致流程差不多就结束了。当然里面还有很多细节,可能由于水平原因并不能完全讲出请谅解,但是自动装配的源码大致流程已经是比较清晰了。

如果文章中有错误存在,希望大佬指正,非常感谢!