一、前言

  上一篇文章,通过分析refresh()方法中的invokeBeanFactoryPostProcessors()方法,分析了IoC容器的初始化过程,这一节从代码上如下所示,接上一节ConfigurationClassParser类中的parse()方法,接着分析SpringBoot的自动装配原理。

// ConfigurationClassParser类
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
            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);
        }
    }
    // 加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
    this.deferredImportSelectorHandler.process();
}

二、SpringBoot自动装配原理

@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 {
    ...
}

2.1 @EnableAutoConfiguration

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

  看到@Import(AutoConfigurationImportSelector.class)导入了一个重要的类AutoConfigurationImportSelector。

2.2 AutoConfigurationImportSelector

// AutoConfigurationImportSelector类
//自动装配
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //获取所有的自动配置类(META-INF/spring.factories中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类)
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
            attributes);
    configurations = removeDuplicates(configurations);
    //需要排除的自动装配类(springboot的主类上 @SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})指定的排除的自动装配类)
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    //将需要排除的类从 configurations remove掉
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
}

  至于怎么从章节一中提到的ConfigurationClassParser类中的parse()=>processDeferredImportSelectors()>AutoConfigurationImportSelector#selectImports(),篇幅有限不做过多介绍。
List configurations = getCandidateConfigurations(annotationMetadata, attributes);

2.3 getCandidateConfigurations()

// AutoConfigurationImportSelector类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                                                  AnnotationAttributes attributes) {
    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;
}

  是不是又看到一个十分熟悉的方法loadFactoryNames(),没错,其实我们在分析SpringBoot启动流程的第一篇文章的时候,就已经分析了,SpringBoot是如何从META-INF/spring.factories中加载指定key的value的。ok,我们在这里再次回顾一遍。

// SpringFactoriesLoader类
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
     String factoryClassName = factoryClass.getName();
     return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
 }

  debug,看看要从META-INF/spring.factories中加载的类的key,如下图所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration

Springboot oom自动重启能解决吗 springboot自动启动原理_自动装配


  回到selectImports()方法,debug,跳过List configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations

Springboot oom自动重启能解决吗 springboot自动启动原理_自动装配_02


  竟然有110个,那这些类都在哪里呢?看spring-boot-autoconfigure(当然在SpringBoot的工程中,也不止这一个依赖包中存在该配置文件)工程下的META-INF/spring.factories,我们能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定义了一大堆。

Springboot oom自动重启能解决吗 springboot自动启动原理_List_03


  继续看Set exclusions = getExclusions(annotationMetadata, attributes);方法,该方法是排除主类上@SpringBootApplication注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(当然也可以用excludeName进行排除),那么在后面的configurations.removeAll(exclusions);方法中将会删除我们的com.demo.starter.config.DemoConfig.class。

  configurations = filter(configurations, autoConfigurationMetadata);该行代码将会过滤掉不需要装配的类。过滤的逻辑有很多,比如我们常用的@ConditionXXX注解。如下所示:

@ConditionalOnBean:容器中有指定的Bean 
@ConditionalOnClass:当类路径下有指定的类
@ConditionalOnExpression:基于SpEL表达式作为判断条件  
@ConditionalOnJava:基于JVM版本作为判断条件  
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置  
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下  
@ConditionalOnMissingClass:当类路径下没有指定的类
@ConditionalOnNotWebApplication:当前项目不是Web项目
@ConditionalOnProperty:配置文件中指定的属性是否有指定的值  
@ConditionalOnResource:类路径下是否有指定的资源  
@ConditionalOnSingleCandidate:当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件下

  至于如何将这些类解析成BeanDefinition并注册进beanDefinition中的,和上一节讲的过程是一样的,不再赘述了。debug,跳过refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);方法。如下图所示,最终在beanFactory的BeanDefinitionMap中找到了自定义starter中的自动装配的类。

三 总结

1、自动装配的执行入口是ioc容器初始化时refresh()—invokeBeanFactoryPostProcessors()–ConfigurationClassParser.parse()–processDeferredImportSelectors()。

2、自动装配注解@EnableAutoConfiguration加载了AutoConfigurationImportSelector类,加载了工程下的META-INF/spring.factories,配置的org.springframework.boot.autoconfigure.EnableAutoConfiguration,加载进入ioc容器