SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的
META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
通俗来说,在我们没有使用SpringBoot之前,如果我们需要引入一个三方依赖或者组件,需要进行很多的配置才能实现,但是在使用了SpringBoot之后,我们只需要引入一个maven依赖即可完成,比如引入Redis到我们的项目中,就只需要加入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
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
开始顺序分析自动装配的过程
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;
}
在上面这个方法中,我们首先要清楚的是入参type
是ApplicationContextInitializer.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
中,比如其中的org.springframework.boot.autoconfigure.EnableAutoConfiguration
后面对应有127行的类全路径,那么就会把org.springframework.boot.autoconfigure.EnableAutoConfiguration
作为key
,后面的127个类路径做value
全部依次加入List
中,对于这个EnableAutoConfiguration
其实就是自动装配的关键,后续会详细分析。那么对于这个文件中的其他内容也是同样的处理方式,同时只要是满足META-INF/spring.factories
的全部都会被加载到,然后累加存入Map
中。
然后经过这个org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
方法之后,缓存中就存储了所有的META-INF/spring.factories
的内容,后续再进入此方法,那么就直接从缓存中获取了。
然后再回到org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.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;
}
当SpringFactoriesLoader.loadFactoryNames(type, classLoader)
加载完所有的资源内容并找到其中对应type
的value
之后(这里这个type是我们跟进过程中的ApplicationContextInitializer.class
),然后就会根据加载到的类全路径去实例化这些对象,这些不属于自动装配的内容,这里就不细说了。
总之到此我们了解到了META-INF/spring.factories
内容已Map
形式全部被加载到cache
中了。
关于这个ConfigurationClassPostProcessor
,他是自动装配中扫描@Import
、@ComponentScan
、@Configuration
等注解并进行解析的重要步骤,但是本篇不重点去分析这个类的处理过程了,感兴趣的同学可以去看我的这篇:Spring的@Bean注解原理详解文章,这篇里面通过对@Bean
注解的解析详细的分析了ConfigurationClassPostProcessor
是如何进行注解扫描并解析的。这里就简单贴一下主要的执行步骤吧,大家可以跟着步骤debug一下看看:
org.springframework.boot.SpringApplication#run(java.lang.String...)
SpringBoot启动的run方法org.springframework.boot.SpringApplication#refreshContext
其中的refreshContext方法org.springframework.context.support.AbstractApplicationContext#refresh
Spring中的重点refresh方法org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List)
处理BeanFactoryPostProcessor相关内容的org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
找到所有需要解析的Bean并一次解析处理org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set)
循环解析注解(AnnotatedBeanDefinition
)类型的Beanorg.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
递归处理配置类org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
这个方法是重点入口,其中依次查找并解析了各种注解,对于我们的自动装配来说,我们重点关注其中对@Import
的解析org.springframework.context.annotation.ConfigurationClassParser#processImports
如果待解析的Bean上有@Import
注解,那么就进行解析处理,但是注意调用该方法的时候,入参有一步:getImports(sourceClass)
,这个方法就是从Bean上查找@Import
注解,那么对于我们的SpringBoot启动类来说,应该能找到两个@Import
注解,其中一个就是我们重点分析的:org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
,在这一步主要是注册选择器DeferredImportSelectorHolder
到 org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#deferredImportSelectors
中已提供给后续使用org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set)
中,然后循环结束执行该方法的最后一步:this.deferredImportSelectorHandler.process();
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
根据第9步中注册的DeferredImportSelectorHolder
,注册到org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#groupings
,然后调用org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
处理@Import
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
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
,这一步是先查找EnableAutoConfiguration
注解的值,然后从META-INF/spring.factories
加载结果的缓存中获取到EnableAutoConfiguration
对应的缓存值,也就是前文中我们举例子的那127个类,然后到此就获取了自动装配的所有类路径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
。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过程中进行实例化。也就完成了自动装配,所以总结下来就是通过启动类上的注解@EnableAutoConfiguration
去META-INF/spring.factories
里找对应的配置,然后对配置进行筛选分类自定义处理,然后加载到beanDefinitionMap
中最后统一进行实例化