1 简介


本文是SpringCloud源码系列第一篇 — SpringBoot自动配置源码剖析,抛砖引玉。 在剖析SpringCloud源码之前,我们先了解一下SpringBoot的自动配置原理。熟悉SpringBoot自动配置原理能够有助于我们更好地理解SpringCloud原理。SpringBoot是由Pivotal团队提供的Java快速开发框架,基于Spring框架,快速整合一些常用第三方依赖(例如:内置Tomat/Jetty/Undertow等常用Http服务器),最终以可执行JAR的方式启动并运行Java进程。 SpringBoot的核心理念是“约定优于配置”,但享受这一便利的前提是了解SpringBoot相关的“约定”,并对其背后实现原理有一定理解。知其然更要知其所以然,这是本文希望能够带给大家的一点收获。

2 Hello World

SpringBoot上手很简单,只需在项目中引入SpringBoot的POM,然后给项目启动类加上@SpringBootApplication注解,以及在Resourece目录下添加SpringBoot的核心配置文件(application.propertiesapplication.yml,下文统一以application.yml为例)即可。


SpringBoot Maven POM:

org.springframework.boot      spring-boot-starter-parent    2.1.0.RELEASE

启动类注解:

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

配置文件(application.yml):

spring:  application:    name: eureka-serverserver:  port: 9000 #端口

项目启动之后,控制台打印SpringBoot启动标志即表示项目已经启动成功。

apache spring apache springboot源码解读_源码剖析

本文后续的源码剖析依赖的SpringBoot和Spring Core的版本分别是:

spring-boot:2.1.0.RELEASE

spring-core:5.1.2. RELEASE

3 自动配置源码剖析

在上述简单的Hello World项目基础之上,如果我们需要增加一些实际开发过程中用到的第三方组件(如:RabbitMq、Redis等),只需在SpringBoot配置文件(

application.yml)中增加一些全局配置。然后我们就可以在项目当中直接注入并使用

RabbitTemplate

RedisTemplate等。这些第三方组件是如何引入依赖?相关Bean又是如何声明及注入?我们带着这些问题开始代码之旅。


3.1 万物伊始@SpringBootApplication

一切的开始都源于

@SpringBootApplication,我们先看看它的源码:


@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 = ComponentScan.class, attribute = "basePackageClasses")   Class>[] scanBasePackageClasses() default {};}

它主要传递依赖三个注解:

@SpringBootConfiguation:声明所注解的类是一个SpringBoot配置类。

@ComponentScan:Spring核心注解,主要完成classpath的扫描及加载。默认加载启动类所在的包路径及其子包下的class文件。

@EnableAutoConfiguation:SpringBoot实现自动配置的关键注解。

3.2 自动配置的奥义@EnableAutoConfiguation


@EnableAutoConfiguation注解的源码如下:


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

它依赖的核心注解是@Import(AutoConfiguarationImportSelector)。借助AutoConfigurationImportSelector,SpringBoot应用扫描所有符合条件的@Configuration配置并将其加载到当前IOC容器。@Import是Spring核心注解,详细可以参考Spring doc相关文档和源码,这里就不做过多展开。

进一步深入到

AutoConfiguarationImportSelector源码,粗一看,其中

AutoConfiguarationImportSelector重载

ImportSelector接口的

selectImports方法源码如下:


@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {   if (!isEnabled(annotationMetadata)) {      return NO_IMPORTS;   }   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader         .loadMetadata(this.beanClassLoader);   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(         autoConfigurationMetadata, annotationMetadata);   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

如果认为Spring实际加载各种@Configuration逻辑即是如此(网上很多SpringBoot源码剖析文章也都认为如此),那就错了。

仔细分析发现,

AutoConfiguarationImportSelector是实现

DeferredImportSelector接口。

DeferredImportSelector继承

ImportSelector接口的同时,提供了一个默认返回

null

default方法:

getImportGroup()。

AutoConfiguarationImportSelector在实现

DeferredImportSelector接口的同时,重载了

getImportGroup方法,其源码如下:


@Overridepublic Class extends Group> getImportGroup() {   return AutoConfigurationGroup.class;}

进一步阅读AutoConfigurationGroup的源码,可以看到其核心的2个方法源码如下:

(1) process方法(主要加载各种自动配置类xxxAutoConfiguration)

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

(2) selectImports方法(将加载好的自动配置类以迭代器的模式返回)

public Iterable selectImports() {   if (this.autoConfigurationEntries.isEmpty()) {      return Collections.emptyList();   }   Set allExclusions = this.autoConfigurationEntries.stream()         .map(AutoConfigurationEntry::getExclusions)         .flatMap(Collection::stream).collect(Collectors.toSet());   Set processedConfigurations = this.autoConfigurationEntries.stream()         .map(AutoConfigurationEntry::getConfigurations)         .flatMap(Collection::stream)         .collect(Collectors.toCollection(LinkedHashSet::new));   processedConfigurations.removeAll(allExclusions);   return sortAutoConfigurations(processedConfigurations,         getAutoConfigurationMetadata())               .stream()               .map((importClassName) -> new Entry(                     this.entries.get(importClassName), importClassName))               .collect(Collectors.toList());}

为何SpringBoot不直接利用ImportSelector接口的selectImports方法来加载自动配置类,而要曲折地通过Group方式来加载,SpringBoot在DeferredImportSelector类的JavaDoc中也给出了解释:

Implementations may also provide an {@link #getImportGroup() import group} whichcan provide additional sorting and filtering logic across different selectors.

简单总结就是:提供Group方法来加载,是为了给不同的ImportSelector提供额外的排序和过滤逻辑扩展。

继续跟踪

process方法中的关键步骤(

getAutoConfigurationEntry方法),进一步查看源码:


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

在执行完getCandidateConfigurations(annotationMetadata, attributes)之后,所有需要的组件自动配置类(xxxAutoConfiguration)都以全类名的方式返回(例如我们看到的mongo,redis等),然后这些配置类会自动注入到Spring容器。

apache spring apache springboot源码解读_源码剖析_02

那么,这些自动配置类是从哪里来的呢?继续跟踪源码:


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;}
public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {    String factoryClassName = factoryClass.getName();    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);    if (result != null) {        return result;    } else {        try {            Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");            LinkedMultiValueMap result = new LinkedMultiValueMap();            while(urls.hasMoreElements()) {                URL url = (URL)urls.nextElement();                UrlResource resource = new UrlResource(url);                Properties properties = PropertiesLoaderUtils.loadProperties(resource);                Iterator var6 = properties.entrySet().iterator();                while(var6.hasNext()) {                    Entry, ?> entry = (Entry)var6.next();                    String factoryClassName = ((String)entry.getKey()).trim();                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());                    int var10 = var9.length;                    for(int var11 = 0; var11 < var10; ++var11) {                        String factoryName = var9[var11];                        result.add(factoryClassName, factoryName.trim());                    }                }            }            cache.put(classLoader, result);            return result;        } catch (IOException var13) {            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);        }    }}

上述loadSpringFactories方法最终依赖classLoader加载项目resources目录下所有的/META-INF/spring.factories。截取其中一部分配置,我们会很惊奇的发现里面包含有我们熟悉的RabbitMq等配置。

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfigurationorg.springframework.boot.autoconfigure.aop.AopAutoConfigurationorg.springframework.boot.autoconfigure.amqp.RabbitAutoConfigurationorg.springframework.boot.autoconfigure.batch.BatchAutoConfigurationorg.springframework.boot.autoconfigure.cache.CacheAutoConfigurationorg.springframework.boot.autoconfigure.cassandra.CassandraAutoConfigurationorg.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfigurationorg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfigurationorg.springframework.boot.autoconfigure.context.MessageSourceAutoConfigurationorg.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

至此,我们就可以追踪溯源了SpringBoot的自动配置原理:


(1) 通过@EnableAutoConfiguation开启自动配置的奥义;

(2) 借助AutoConfigurationImportSelector魔法工具来完成XXXAutoConfiguration类加载。它实际上是通过启动类加载所有resources目录下的spring.factories文件,而SpringBoot的spring.factories文件默认包含了很多第三方中间件的配置。

3.3 关键先生XXXAutoConfiguration

我们挑选其中

RabbitAutoConfiguration这个配置类,进一步深入源码。

RabbitAutoConfiguration内部通过

@Bean注解声明RabbitTemplate对象,这就是为什么在项目当中没有定义

RabbitTemplate,而我们却能直接注入这个对象并使用的根本原因。


@Bean@ConditionalOnSingleCandidate(ConnectionFactory.class)@ConditionalOnMissingBeanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {   PropertyMapper map = PropertyMapper.get();   RabbitTemplate template = new RabbitTemplate(connectionFactory);   MessageConverter messageConverter = this.messageConverter.getIfUnique();   if (messageConverter != null) {      template.setMessageConverter(messageConverter);   }   template.setMandatory(determineMandatoryFlag());   RabbitProperties.Template properties = this.properties.getTemplate();   if (properties.getRetry().isEnabled()) {      template.setRetryTemplate(new RetryTemplateFactory(            this.retryTemplateCustomizers.orderedStream()                  .collect(Collectors.toList())).createRetryTemplate(                        properties.getRetry(),                        RabbitRetryTemplateCustomizer.Target.SENDER));   }   map.from(properties::getReceiveTimeout).whenNonNull().as(Duration::toMillis)         .to(template::setReceiveTimeout);   map.from(properties::getReplyTimeout).whenNonNull().as(Duration::toMillis)         .to(template::setReplyTimeout);   map.from(properties::getExchange).to(template::setExchange);   map.from(properties::getRoutingKey).to(template::setRoutingKey);   map.from(properties::getQueue).whenNonNull().to(template::setQueue);   return template;}

细心的同学会发现XXXAutoConfiguration 自动配置类在其内部通过@Bean注解声明对象的同时,还会带上@ConditionalOnSingleCandidate@ConditionalOnMissingBean的注解。这是SpringBoot基于项目依赖上下文实现条件装配Bean的核心(例如:项目依赖中有tomcat依赖Jar,则自动加载tomcat容器,有Jetty依赖Jar,则自动加载Jetty容器),关于条件装配,我们将在SpringCloud源码系列的后续文章中详细讲解。

4 SpringApplication初始化启动

任何一个Spring Boot项目,都会有一个类似如下的启动类:


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

为什么执行main方法SpringBoot项目就启动了呢?启动过程中,又是在什么时机触发SpringBoot自动配置呢?在此,我们揭开SpringBoot最后的一层神秘面纱:

public static ConfigurableApplicationContext run(Class>[] primarySources,      String[] args) {   return new SpringApplication(primarySources).run(args);}

从以上代码可以发现,SpringBoot整个启动流程分为两个步骤:

(1) 初始化一个SpringApplication对象;

(2) 执行该对象的run方法;

4.1 SpringApplication初始化

@SuppressWarnings({ "unchecked", "rawtypes" })public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {   this.resourceLoader = resourceLoader;   Assert.notNull(primarySources, "PrimarySources must not be null");   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));   //判断应用类型,常用的一般都是servlet     this.webApplicationType = WebApplicationType.deduceFromClasspath();   //初始化classpath下METF-INF/spring.factories已配置好的ApplicationContextInitializer   setInitializers((Collection) getSpringFactoriesInstances(         ApplicationContextInitializer.class));    //初始化classpath下已配置的ApplicationListener   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));   this.mainApplicationClass = deduceMainApplicationClass();}

初始化

SpringApplication对象主要做了3件事:


(1) 初始化primarySources,指定SpringBoot应用启动时加载业务相关Bean的上下文路径(通常对应声明@SpringBootApplication注解的类,即提供main方法的类)。所以SpringBoot应用通常要求声明@SpringBootApplication注解的Application类放在顶层目录(默认包含所有声明@Bean注解的子包或子类)。除此之外,SpringBoot通过扫描primarySources上传递依赖的一系列注解(@SpringBootApplication@EnableAutoConfiguration等),才能启动后续的自动配置;

(2) 推断SpringBoot应用的应用类型(Reactive Web Application、Servlet-Based Web Application还是普通的Spring应用)

(3) 初始化ApplicationContextInitializerApplicationListener

4.2 run()

public ConfigurableApplicationContext run(String... args) {    StopWatch stopWatch = new StopWatch();    stopWatch.start();    ConfigurableApplicationContext context = null;    Collection exceptionReporters = new ArrayList();    this.configureHeadlessProperty();    SpringApplicationRunListeners listeners = this.getRunListeners(args);    listeners.starting();    Collection exceptionReporters;    try {        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);        this.configureIgnoreBeanInfo(environment);        Banner printedBanner = this.printBanner(environment);        context = this.createApplicationContext();        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);        this.refreshContext(context);        this.afterRefresh(context, applicationArguments);        stopWatch.stop();        if (this.logStartupInfo) {            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);        }        listeners.started(context);        this.callRunners(context, applicationArguments);    } catch (Throwable var10) {        this.handleRunFailure(context, var10, exceptionReporters, listeners);        throw new IllegalStateException(var10);    }    try {        listeners.running(context);        return context;    } catch (Throwable var9) {        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);        throw new IllegalStateException(var9);    }}


run()方法执行过程中,主要包含以下几个步骤:


(1) 初始化应用参数(DefaultApplicationArguments)

(2) 准备上下文环境(ConfigurableEnvironment)

(3) 打印启动Banner(通常是Log或控制台输出Spring字符图形)

(4) 创建应用上下文(ConfigurableApplicationContext)

(5) 准备上下文(prepareContext)

(6) 刷新上下文(refreshContext)

(7) 调用上下文中注册的Runner(ApplicationRunnerCommandLineRunner)

穿插其中的是利用SpringApplicationRunListeners来完成执行过程中的状态监听。上述步骤中最核心是准备上下文(prepareContext)和刷新上下文(refreshContext),我们进一步剖析其中的源码。

(1) 准备上下文(prepareContext)

private void prepareContext(ConfigurableApplicationContext context,      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,      ApplicationArguments applicationArguments, Banner printedBanner) {   context.setEnvironment(environment);   postProcessApplicationContext(context);   applyInitializers(context);   listeners.contextPrepared(context);   if (this.logStartupInfo) {      logStartupInfo(context.getParent() == null);      logStartupProfileInfo(context);   }   // Add boot specific singleton beans   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);   if (printedBanner != null) {      beanFactory.registerSingleton("springBootBanner", printedBanner);   }   if (beanFactory instanceof DefaultListableBeanFactory) {      ((DefaultListableBeanFactory) beanFactory)            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);   }   // Load the sources   Set<Object> sources = getAllSources();   Assert.notEmpty(sources, "Sources must not be empty");   load(context, sources.toArray(new Object[0]));   listeners.contextLoaded(context);}

上述方法中关键代码步骤为

getAllSources

load(context, sources.toArray(new Object[0]))。进一步深入

getAllSources方法源码:


public Set getAllSources() {   Set allSources = new LinkedHashSet<>();   if (!CollectionUtils.isEmpty(this.primarySources)) {      allSources.addAll(this.primarySources);   }   if (!CollectionUtils.isEmpty(this.sources)) {      allSources.addAll(this.sources);   }   return Collections.unmodifiableSet(allSources);}

这里我们看到之前讲到的primarySources(指定SpringBoot应用启动时加载业务相关Bean的上下文路径),还有一个sources(在SpringApplication启动之前,通过setSources方法指定,用以添加额外的上下文Class、Package或XML文件路径)。再一步深入load方法源码:

protected void load(ApplicationContext context, Object[] sources) {   if (logger.isDebugEnabled()) {      logger.debug(            "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));   }   BeanDefinitionLoader loader = createBeanDefinitionLoader(         getBeanDefinitionRegistry(context), sources);   if (this.beanNameGenerator != null) {      loader.setBeanNameGenerator(this.beanNameGenerator);   }   if (this.resourceLoader != null) {      loader.setResourceLoader(this.resourceLoader);   }   if (this.environment != null) {      loader.setEnvironment(this.environment);   }   loader.load();}

这里我们看到了熟悉的BeanDefinitionLoader初始化,然后启动load(扫描指定的上下文路径,注册BeanDefinitionHolder到之前创建好的ConfigurableApplicationContext)。

(2) 刷新上下文(refreshContext)


refreshContext方法内部会委托调用之前创建好的

ConfigurableApplicationContextrefresh方法,至此,启动了Spring Core的核心模板方法。其源码如下:


public void refresh() throws BeansException, IllegalStateException {   synchronized (this.startupShutdownMonitor) {      // Prepare this context for refreshing.      prepareRefresh();      // Tell the subclass to refresh the internal bean factory.      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();      // Prepare the bean factory for use in this context.      prepareBeanFactory(beanFactory);      try {         // Allows post-processing of the bean factory in context subclasses.         postProcessBeanFactory(beanFactory);         // Invoke factory processors registered as beans in the context.         invokeBeanFactoryPostProcessors(beanFactory);         // Register bean processors that intercept bean creation.         registerBeanPostProcessors(beanFactory);         // Initialize message source for this context.         initMessageSource();         // Initialize event multicaster for this context.         initApplicationEventMulticaster();         // Initialize other special beans in specific context subclasses.         onRefresh();         // Check for listener beans and register them.         registerListeners();         // Instantiate all remaining (non-lazy-init) singletons.         finishBeanFactoryInitialization(beanFactory);         // Last step: publish corresponding event.         finishRefresh();      } catch (BeansException ex) {         if (logger.isWarnEnabled()) {            logger.warn("Exception encountered during context initialization - " +                  "cancelling refresh attempt: " + ex);         }         // Destroy already created singletons to avoid dangling resources.         destroyBeans();         // Reset 'active' flag.         cancelRefresh(ex);         // Propagate exception to caller.         throw ex;      } finally {         // Reset common introspection caches in Spring's core, since we         // might not ever need metadata for singleton beans anymore...         resetCommonCaches();      }   }}

这个模板方法中每一步的具体作用,可以在网上找到很多分析的文章,我们在此不做赘述。此处,我们重点回答跟本文主题相关的一个问题:SpringBoot应用启动过程中是在何时触发SpringBoot自动配置呢?

要回答上面这个问题,关键逻辑在

invokeBeanFactoryPostProcessors方法源码:


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));   }}

可以看到关键逻辑是委托给PostProcessorRegistrationDelegateinvokeBeanFactoryPostProcessors方法,其中关键部分的源码如下:

public static void invokeBeanFactoryPostProcessors(      ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {   // Invoke BeanDefinitionRegistryPostProcessors first, if any.   Set processedBeans = new HashSet<>();   if (beanFactory instanceof BeanDefinitionRegistry) {      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;      List regularPostProcessors = new ArrayList<>();      List registryProcessors = new ArrayList<>();      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {         if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {            BeanDefinitionRegistryPostProcessor registryProcessor =                  (BeanDefinitionRegistryPostProcessor) postProcessor;            registryProcessor.postProcessBeanDefinitionRegistry(registry);            registryProcessors.add(registryProcessor);         } else {            regularPostProcessors.add(postProcessor);         }      }      // Do not initialize FactoryBeans here: We need to leave all regular beans      // uninitialized to let the bean factory post-processors apply to them!      // Separate between BeanDefinitionRegistryPostProcessors that implement      // PriorityOrdered, Ordered, and the rest.      List currentRegistryProcessors = new ArrayList<>();      // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.      String[] postProcessorNames =            beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);      for (String ppName : postProcessorNames) {         if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));            processedBeans.add(ppName);         }      }      sortPostProcessors(currentRegistryProcessors, beanFactory);      registryProcessors.addAll(currentRegistryProcessors);      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);      currentRegistryProcessors.clear();      // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);      for (String ppName : postProcessorNames) {         if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));            processedBeans.add(ppName);         }      }      sortPostProcessors(currentRegistryProcessors, beanFactory);      registryProcessors.addAll(currentRegistryProcessors);      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);      currentRegistryProcessors.clear();      // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.      boolean reiterate = true;      while (reiterate) {         reiterate = false;         postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);         for (String ppName : postProcessorNames) {            if (!processedBeans.contains(ppName)) {               currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));               processedBeans.add(ppName);               reiterate = true;            }         }         sortPostProcessors(currentRegistryProcessors, beanFactory);         registryProcessors.addAll(currentRegistryProcessors);         invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);         currentRegistryProcessors.clear();      }      // Now, invoke the postProcessBeanFactory callback of all processors handled so far.      invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);      invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);   } else {      // Invoke factory processors registered with the context instance.      invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);   }   ...}

其中针对BeanDefinitionRegistry类型的BeanFactory做了特殊处理(因为要触发Configuration中的Bean加载)。对应处理SpringBoot自动配置的BeanFactoryConfigurationClassPostProcessor(实现了BeanDefinitionRegistryPostProcessor接口)。上述方法实现中的关键逻辑方法是invokeBeanDefinitionRegistryPostProcessors,其源码如下:

private static void invokeBeanDefinitionRegistryPostProcessors(      Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {   for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {      postProcessor.postProcessBeanDefinitionRegistry(registry);   }}

循环调用对应的BeanDefinitionRegistryPostProcessor,并委托其进行BeanDefinition注册。

进一步深入SpringBoot自动配置相关的

ConfigurationClassPostProcessor类的

postProcessBeanDefinitionRegistry方法源码:


/** * Derive further bean definitions from the configuration classes in the registry. */@Overridepublic 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);   }   if (this.factoriesPostProcessed.contains(registryId)) {      throw new IllegalStateException(            "postProcessBeanFactory already called on this post-processor against " + registry);   }   this.registriesPostProcessed.add(registryId);   processConfigBeanDefinitions(registry);}

这里也可以从方法的JavaDoc中看到,其主要功能是驱动@Configuration注解的配置类中的Bean加载(@SpringBootConfiguration注解传递依赖了@Configuration注解,上文提到的各类XXXAutoConfiguration也标注了@Configuration注解)。

继续深入到

processConfigBeanDefinitions方法,截取其中核心源码片段:


/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {   ...   // Parse each @Configuration class   ConfigurationClassParser parser = new ConfigurationClassParser(         this.metadataReaderFactory, this.problemReporter, this.environment,         this.resourceLoader, this.componentScanBeanNameGenerator, registry);   Set candidates = new LinkedHashSet<>(configCandidates);   Set alreadyParsed = new HashSet<>(configCandidates.size());   do {      parser.parse(candidates);      parser.validate();      Set 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);      alreadyParsed.addAll(configClasses);      candidates.clear();      if (registry.getBeanDefinitionCount() > candidateNames.length) {         String[] newCandidateNames = registry.getBeanDefinitionNames();         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));         Set<String> alreadyParsedClasses = new HashSet<>();         for (ConfigurationClass configurationClass : alreadyParsed) {            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());         }         for (String candidateName : newCandidateNames) {            if (!oldCandidateNames.contains(candidateName)) {               BeanDefinition bd = registry.getBeanDefinition(candidateName);               if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&                     !alreadyParsedClasses.contains(bd.getBeanClassName())) {                  candidates.add(new BeanDefinitionHolder(bd, candidateName));               }            }         }         candidateNames = newCandidateNames;      }   } while (!candidates.isEmpty());   ...}

首先是创建了通用的XXXConfiguration类解析器(ConfigurationClassParser)。然后循环解析所有标注了@Configuration的配置类(标注了@SpringBootConfiguration的启动类自然也被包含在内)。这里需要特别注意的一点是:如果在解析某个配置类的过程中,引入了新的配置类,则会递归解析新引入的配置类,直到所有的配置类都被解析完。其次在解析完成之后,利用ConfigurationClassBeanDefinitionReader.loadBeanDefinitions方法扫描XXXConfiguration类中的@Bean注解并完成BeanDefinition到Spring容器的注册。

再进一步深入

ConfigurationClassParser类的

parse方法,发现其解析逻辑委托给了

processConfigurationClass方法,源码如下:


protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {      return;   }   ConfigurationClass existingClass = this.configurationClasses.get(configClass);   if (existingClass != null) {      if (configClass.isImported()) {         if (existingClass.isImported()) {            existingClass.mergeImportedBy(configClass);         }         // Otherwise ignore new imported config class; existing non-imported class overrides it.         return;      } else {         // Explicit bean definition found, probably replacing an import.         // Let's remove the old one and go with the new one.         this.configurationClasses.remove(configClass);         this.knownSuperclasses.values().removeIf(configClass::equals);      }   }   // Recursively process the configuration class and its superclass hierarchy.   SourceClass sourceClass = asSourceClass(configClass);   do {      sourceClass = doProcessConfigurationClass(configClass, sourceClass);   } while (sourceClass != null);   this.configurationClasses.put(configClass, configClass);}

核心逻辑在doProcessConfigurationClass方法,继续深入:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)      throws IOException {   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {      // Recursively process any member (nested) classes first      processMemberClasses(configClass, sourceClass);   }   // Process any @PropertySource annotations   ...   // Process any @ComponentScan annotations   ...   // Process any @Import annotations   processImports(configClass, sourceClass, getImports(sourceClass), true);   // Process any @ImportResource annotations   ...   // Process individual @Bean methods   Set beanMethods = retrieveBeanMethodMetadata(sourceClass);   for (MethodMetadata methodMetadata : beanMethods) {      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));   }   // Process default methods on interfaces   processInterfaces(configClass, sourceClass);   // Process superclass, if any   if (sourceClass.getMetadata().hasSuperClass()) {      String superclass = sourceClass.getMetadata().getSuperClassName();      if (superclass != null && !superclass.startsWith("java") &&            !this.knownSuperclasses.containsKey(superclass)) {         this.knownSuperclasses.put(superclass, configClass);         // Superclass found, return its annotation metadata and recurse         return sourceClass.getSuperClass();      }   }   // No superclass -> processing is complete   return null;}

我们把目光聚焦到下面这部分源码:

// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), true);

从这里开始,跟自动配置(@EnableAutoConfiguration)开始产生联系:

1) getImports方法获取@EnableAutoConfiguration依赖的@Import(AutoConfigurationImportSelector.class)

2) processImports方法根据Import的Class(例如:AutoConfigurationImportSelector)执行对应的导入逻辑;

进一步深入

processImports方法核心源码片段:


for (SourceClass candidate : importCandidates) {   if (candidate.isAssignable(ImportSelector.class)) {      // Candidate class is an ImportSelector -> delegate to it to determine imports      Class> candidateClass = candidate.loadClass();      ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);      ParserStrategyUtils.invokeAwareMethods(            selector, this.environment, this.resourceLoader, this.registry);      if (selector instanceof DeferredImportSelector) {         this.deferredImportSelectorHandler.handle(               configClass, (DeferredImportSelector) selector);      } else {         String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());         Collection importSourceClasses = asSourceClasses(importClassNames);         processImports(configClass, currentSourceClass, importSourceClasses, false);      }   } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {      // Candidate class is an ImportBeanDefinitionRegistrar ->      // delegate to it to register additional bean definitions      Class> candidateClass = candidate.loadClass();      ImportBeanDefinitionRegistrar registrar =            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);      ParserStrategyUtils.invokeAwareMethods(            registrar, this.environment, this.resourceLoader, this.registry);      configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());   } else {      // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->      // process it as an @Configuration class      this.importStack.registerImport(            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());      processConfigurationClass(candidate.asConfigClass(configClass));   }}

由于AutoConfigurationImportSelector实现了DeferredImportSelector接口,我们进一步把目光聚焦到:

if (selector instanceof DeferredImportSelector) {   this.deferredImportSelectorHandler.handle(         configClass, (DeferredImportSelector) selector);}

继续深入到

deferredImportSelectorHandler

handle方法源码:


@Nullableprivate List deferredImportSelectors = new ArrayList<>();/** * Handle the specified {@link DeferredImportSelector}. If deferred import * selectors are being collected, this registers this instance to the list. If * they are being processed, the {@link DeferredImportSelector} is also processed * immediately according to its {@link DeferredImportSelector.Group}. * @param configClass the source configuration class * @param importSelector the selector to handle */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);   }}

这里需要注意:首次处理的时候,deferredImportSelectors默认是个空的List,不做任何import处理逻辑,仅把AutoConfigurationImportSelector添加进了deferredImportSelectors。那么什么时候执行真正的import逻辑呢?再回到之前ConfigurationClassParser类的parse方法源码:

public void parse(Set 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();}

在方法的最后触发了deferredImportSelectorHandler.process()

public void process() {   List 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<>();   }}

进一步深入DeferredImportSelectorGroupingHandlerprocessGroupImports方法源码:

public void processGroupImports() {   for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {      grouping.getImports().forEach(entry -> {         ConfigurationClass configurationClass = this.configurationClasses.get(               entry.getMetadata());         try {            processImports(configurationClass, asSourceClass(configurationClass),                  asSourceClasses(entry.getImportClassName()), false);         } catch (BeanDefinitionStoreException ex) {            throw ex;         } catch (Throwable ex) {            throw new BeanDefinitionStoreException(                  "Failed to process import candidates for configuration class [" +                        configurationClass.getMetadata().getClassName() + "]", ex);         }      });   }}

这里循环遍历每一个DeferredImportSelectorGrouping,然后获取其所有导入的XXXConfiguration类,并循环调用之前提到的processImports方法来递归地处理所有XXXConfiguration类。

我们重点关注grouping.getImports方法:

/** * Return the imports defined by the group. * @return each import with its associated configuration class */public Iterable getImports() {   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {      this.group.process(deferredImport.getConfigurationClass().getMetadata(),            deferredImport.getImportSelector());   }   return this.group.selectImports();}

如果我告诉你这段代码里面的group就是AutoConfigurationImportSelector.AutoConfigurationGroup,是不是就跟上面第3部分提到的@EnableAutoConfiguation利用AutoConfigurationImportSelector加载各种XXXAutoConfiguration对上了暗号,就撸通了整个SpringBoot自动配置加载XXXAutoConfiguration类并根据@Bean注解完成Spring Bean注册的核心逻辑。

5 大串联

在了解了SpringBoot自动配置和初始化启动的源码之后,我们总体回顾一下,串联起整个SpringBoot启动的全过程: 

(1) 初始化SpringApplication对象

1.1) 初始化primarySources,指定SpringBoot应用启动时加载业务相关Bean的上下文路径(通常对应声明@SpringBootApplication注解的类,即提供main方法的类)。除此之外,SpringBoot通过扫描primarySources上传递依赖的一系列注解(@SpringBootApplication@EnableAutoConfiguration等),才能启动后续的自动配置;

1.2) 推断SpringBoot应用的应用类型(Reactive Web Application、Servlet-Based Web Application还是普通的Spring应用);

1.3) 初始化ApplicationContextInitializerApplicationListener

(2) 执行run方法

2.1) 初始化应用参数(DefaultApplicationArguments);

2.2) 准备上下文环境(ConfigurableEnvironment);

2.3) 打印启动Banner(通常是Log或控制台输出Spring字符图形);

2.4) 创建应用上下文(ConfigurableApplicationContext);

2.5) 准备上下文(prepareContext),首先是BeanDefinitionLoader初始化,然后启动load(扫描指定的上下文路径,注册BeanDefinitionHolder到之前创建好的ConfigurableApplicationContext);

2.6) 刷新上下文(refreshContext),委托调用之前创建好的ConfigurableApplicationContextrefresh方法(启动了SpringCore的核心模板方法),完成BeanDefinition的扫描、初始化、依赖注入等。其中跟SpringBoot自动配置相关的主要是refresh方法中的invokeBeanFactoryPostProcessors步骤:

2.6.1) 委托ConfigurationClassPostProcessorprocessConfigBeanDefinitions处理XXXConfiguration类的加载及注册;

2.6.2)

ConfigurationClassPostProcessor

委托ConfigurationClassParser类处理XXXConfiguration类的加载(

ConfigurationClassParser基于

@EnableAutoConfiguration注解的传递依赖

@Import(AutoConfigurationImportSelector.class),委托

AutoConfigurationImportSelector来实现所有

XXXAutoConfiguration类的加载);2.6.3) 递归加载所有的

XXXConfiguration类之后,

ConfigurationClassPostProcessor

创建ConfigurationClassBeanDefinitionReader对象,并委托其loadBeanDefinitions(扫描

XXXConfiguration类的

@Bean注解并注册

BeanDefinition到Spring容器);

2.7) 调用上下文中注册的Runner( ApplicationRunner

CommandLineRunner);


以上就是SpringBoot自动配置的原理,希望对大家工作有所帮助!