SpringBoot的自动装配

一. 什么是SpringBoot自动装配

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

通俗来说,在我们没有使用SpringBoot之前,如果我们需要引入一个三方依赖或者组件,需要进行很多的配置才能实现,但是在使用了SpringBoot之后,我们只需要引入一个maven依赖即可完成,比如引入Redis到我们的项目中,就只需要加入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后进行一下Redis相关的地址配置即可,这种就是SpringBoot的自动装配机制

我们本篇分析的是自动装配的过程,所以重点只看自动装配相关的代码,关于过程中其他的SpringBoot启动的相关流程,后续会有文章进行分析。

二. 自动装配如何实现的

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Test1Application {

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

}

一个普通的SpringBoot启动类,我们来看他的注解@SpringBootApplication
在这里插入图片描述

可以看到@SpringBootApplication实际上是一个组合注解,去除掉前四个元注解,剩下的

  • @SpringBootConfiguration 其实就是一个@Configuration注解
  • @EnableAutoConfiguration 这个注解是自动装配的关键,其中有两个@Import注解引入了自动装配需要的类:@Import(AutoConfigurationImportSelector.class)@Import(AutoConfigurationPackages.Registrar.class)
  • @ComponentScan 定义了包扫描的信息

这三个注解中,@EnableAutoConfiguration 表示开启自动装配,并引入了AutoConfigurationImportSelector这个自动装配的关键类。接下来我们从SpringApplication.run开始顺序分析自动装配的过程

三. 自动装配过程解析

3.1 加载META-INF/spring.factories内容

从启动类的SpringApplication.run进入开始分析,回来到这一步:

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

这个方法分两步,一个是new SpringApplication一个是调用new出来对象实例的run方法,我们先看new SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

这段代码是创建SpringApplication的构造方法,我们重点了来看其中的getSpringFactoriesInstances(ApplicationContextInitializer.class)这一步。至于后面的getSpringFactoriesInstances(ApplicationListener.class)和前面的一样流程,只不过是从缓存中取筛选数据进行实例化,所以就不再进行第二次分析了。
代码跟进会来到:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

在上面这个方法中,我们首先要清楚的是入参typeApplicationContextInitializer.class,然后重点来看SpringFactoriesLoader.loadFactoryNames(type, classLoader)

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

这个方法也是分两个步骤,一个是loadSpringFactories(classLoader)一个是根据入参的类型名称从中获取需要的内容,其实就是map.getOrDefault方法。我们重点跟进loadSpringFactories(classLoader)

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	// 当SpringBoot启动并第一次调用该方法时,这个cache肯定是空的,所以获取不到内容
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		// 通过classLoader加载META-INF/spring.factories资源  
		// 其实就是SpringBoot项目下的所有META-INF/spring.factories文件内容
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		// 创建一个多value的map 
		result = new LinkedMultiValueMap<>();
		// 循环加载到的资源并解析
		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();
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

上面这个方法其实算是自动装配中的第一个比较重要的步骤,加载所有的META-INF/spring.factories内容,这里贴出来一个spring-boot-autoconfigure-2.3.12.RELEASE.jar!\META-INF\spring.factories中的spring.factories文件内容,大家可以看一下:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# 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

# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

这个文件篇幅有点长,但是在上面的loadSpringFactories方法中,最终就是把这些内容加载到一个Map<String, List<String>>中,比如其中的org.springframework.boot.autoconfigure.EnableAutoConfiguration后面对应有127行的类全路径,那么就会把org.springframework.boot.autoconfigure.EnableAutoConfiguration作为key,后面的127个类路径value全部依次加入List<String>中,对于这个EnableAutoConfiguration其实就是自动装配的关键,后续会详细分析。那么对于这个文件中的其他内容也是同样的处理方式,同时只要是满足META-INF/spring.factories的全部都会被加载到,然后累加存入Map<String, List<String>>中。
然后经过这个org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories方法之后,缓存中就存储了所有的META-INF/spring.factories的内容,后续再进入此方法,那么就直接从缓存中获取了。
然后再回到org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)方法中:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

SpringFactoriesLoader.loadFactoryNames(type, classLoader)加载完所有的资源内容并找到其中对应typevalue之后(这里这个type是我们跟进过程中的ApplicationContextInitializer.class),然后就会根据加载到的类全路径去实例化这些对象,这些不属于自动装配的内容,这里就不细说了。
总之到此我们了解到了META-INF/spring.factories内容已Map<String, List<String>>形式全部被加载到cache中了。

3.2 通过ConfigurationClassPostProcessor加载自动装配所需内容

关于这个ConfigurationClassPostProcessor,他是自动装配中扫描@Import@ComponentScan@Configuration等注解并进行解析的重要步骤,但是本篇不重点去分析这个类的处理过程了,感兴趣的同学可以去看我的这篇:Spring的@Bean注解原理详解文章,这篇里面通过对@Bean注解的解析详细的分析了ConfigurationClassPostProcessor是如何进行注解扫描并解析的。这里就简单贴一下主要的执行步骤吧,大家可以跟着步骤debug一下看看:

  1. org.springframework.boot.SpringApplication#run(java.lang.String...) SpringBoot启动的run方法
  2. org.springframework.boot.SpringApplication#refreshContext 其中的refreshContext方法
  3. org.springframework.context.support.AbstractApplicationContext#refresh Spring中的重点refresh方法
  4. org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>) 处理BeanFactoryPostProcessor相关内容的
  5. org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 找到所有需要解析的Bean并一次解析处理
  6. org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) 循环解析注解(AnnotatedBeanDefinition)类型的Bean
  7. org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass 递归处理配置类
  8. org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 这个方法是重点入口,其中依次查找并解析了各种注解,对于我们的自动装配来说,我们重点关注其中对@Import的解析
  9. org.springframework.context.annotation.ConfigurationClassParser#processImports 如果待解析的Bean上有@Import注解,那么就进行解析处理,但是注意调用该方法的时候,入参有一步:getImports(sourceClass),这个方法就是从Bean上查找@Import注解,那么对于我们的SpringBoot启动类来说,应该能找到两个@Import注解,其中一个就是我们重点分析的:org.springframework.boot.autoconfigure.AutoConfigurationImportSelector,在这一步主要是注册选择器DeferredImportSelectorHolderorg.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#deferredImportSelectors中已提供给后续使用
  10. 前一步第9步完成后,会回到第6步的循环org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)中,然后循环结束执行该方法的最后一步:this.deferredImportSelectorHandler.process();
  11. org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process根据第9步中注册的DeferredImportSelectorHolder,注册到org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#groupings,然后调用org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports处理@Import
  12. org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports中调用org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports再调用到org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process然后再调用到org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
  13. 现在来到org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry,这一步是先查找EnableAutoConfiguration注解的值,然后从META-INF/spring.factories加载结果的缓存中获取到EnableAutoConfiguration对应的缓存值,也就是前文中我们举例子的那127个类,然后到此就获取了自动装配的所有类路径
  14. 获取到所有EnableAutoConfiguration注解自动装配类路径之后回到:org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports循环调用processImports方法处理每一个自动装配的自定义逻辑。根据不同的@Import实现方式再进行不同的处理,也可能再次递归进行处理。如果是ImportSelector方式实现,并且还为DeferredImportSelector的实现类,则在自动装配时处理(org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle),对于仅实现的ImportSelector接口的类先实例化,然后调用selectImport接口得到要导入的类,然后转化成sourceClass然后递归调用processImports。如果是实现ImportBeanDefinitionRegistrar的导入,同样进行实例化,然后加入 org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars, key为实例,value为导入该类的类信息(比如当前candidateClass是A,A是Config类导入的,此时value就为config的信息)。如果导入的是普通类,将当前candidate结合configClass(该信息主要用于candidate记录是被谁导入的)生成新的configClass,递归调用processConfigurationClass
  15. 处理完所有筛选的自动装配需要的类之后回到org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,经过parser.parse(candidates);解析完所有的@Import等注解之后,再调用this.reader.loadBeanDefinitions(configClasses);加载所有类的BeanDefinition进行注册,然后后续就会进行实例化以实现自动装配。

我们就先看上面的第9步org.springframework.context.annotation.ConfigurationClassParser#processImports

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
	// 这个方法的入参importCandidates就是解析的Bean上扫描的所有的@Import注解
	if (importCandidates.isEmpty()) {
		return;
	}

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			// 循环每个@Import注解进行处理
			for (SourceClass candidate : importCandidates) {
				// 查看这个Import的对象是不是实现自ImportSelector的
				// 对于我们今天重点看的AutoConfigurationImportSelector会进入这个if
				if (candidate.isAssignable(ImportSelector.class)) {
					// 如果是`ImportSelector`方式实现,并且还为`DeferredImportSelector`的实现类,
					// 则在自动装配时处理(`org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle`),
					// 对于仅实现的`ImportSelector`接口的类先实例化,然后调用`selectImport`接口得到要导入的类,
					// 然后转化成sourceClass然后递归调用processImports
					
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					// 实例化一个AutoConfigurationImportSelector
					// 这里就是SpringBoot启动类@Import导入的AutoConfigurationImportSelector实例化的地方
					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);
					}
					// 再检查是不是实现自DeferredImportSelector的  
					// AutoConfigurationImportSelector就是实现自DeferredImportSelector的
					if (selector instanceof DeferredImportSelector) {
						// 注册选择器DeferredImportSelectorHolder到 org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#deferredImportSelectors
						// 注意现在这里是注册  也就是给集合中添加一个DeferredImportSelectorHolder
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
					}
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// 如果是实现`ImportBeanDefinitionRegistrar`的导入,同样进行实例化,
					// 然后加入 `org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars`, 
					// key为实例,value为导入该类的类信息(比如当前candidateClass是A,A是Config类导入的,此时value就为config的信息)
					
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									this.environment, this.resourceLoader, this.registry);
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
					// 如果导入的是普通类,将当前candidate结合configClass(该信息主要用于candidate记录是被谁导入的)生成新的configClass,
					// 递归调用processConfigurationClass
					
					// 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), exclusionFilter);
				}
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
		finally {
			this.importStack.pop();
		}
	}
}

对于上面这个方法总结:

  • 针对导入的ImportSelect实现类以及普通类,最终都会执行processConfigurationClass,作为Configuration放入org.springframework.context.annotation.ConfigurationClassParser#configurationClasses,但是都还没注册到beanDefinitionMap中,通过后续的 this.reader.loadBeanDefinitions(configClasses)实现注册beanDefinitionMap
  • 实现ImportBeanDefinitionRegistrar接口的类,未放入ConfigurationClasses中,将实现类标记在ConfigurationClass中的org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars的属性上,最终通过this.reader.loadBeanDefinitions(configClasses)实现注册

再看上面第13步的源码:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	// 获取注解属性值
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// 从上文中加载的`META-INF/spring.factories`缓存中获取EnableAutoConfiguration注解对应的值
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	// 封装成AutoConfigurationEntry进行返回
	return new AutoConfigurationEntry(configurations, exclusions);
}

再看第15步:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

// 这个方法的入参configurationModel其实就是调用地方的parse的org.springframework.context.annotation.ConfigurationClassParser#configurationClasses
// 也就是上面说的`ImportSelect`实现类以及普通类
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	// 遍历所有的ConfigurationClass
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

跟进到:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	// 如果configClass是Import引入的,但是未注入到beanDefinitionMap中,那么在此处注册到beanDefinitionMap中
	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	//如果有@Bean注解的  那么处理@Bean注解
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}

	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	// 处理上面提到的org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars中的@Import实现
	// 也就是实现`ImportBeanDefinitionRegistrar`接口的Import类
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

注册到beanDefinitionMap之后,后续在IOC过程中进行实例化。也就完成了自动装配,所以总结下来就是通过启动类上的注解@EnableAutoConfigurationMETA-INF/spring.factories里找对应的配置,然后对配置进行筛选分类自定义处理,然后加载到beanDefinitionMap中最后统一进行实例化

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值