大家在使用springboot的时候不得不感叹它的功能强大和方便,但是作为一个合格的程序员,单纯只是去使用框架是远远不够的!
我们知道,Spring Boot 项目创建完成后,即使不进行任何的配置,也能够顺利地运行,这都要归功于 Spring Boot 的自动化配置。
Spring Boot 默认使用 application.properties 或 application.yml 作为其全局配置文件,我们可以在该配置文件中对各种自动配置属性(server.port、logging.level.* 、spring.config.active.no-profile 等等)进行修改,并使之生效,那么您有没有想过这些属性是否有据可依呢?
而这个答案的回答是"是,肯定有据可依",当我们感叹框架功能之强大、使用之便捷之余,我们也要有深究原理的精神,任何框架的功能的强大和方便都是由一行行的代码所支撑的。
Spring Factories 实现原理springboot的自动装箱功能的实现是基于spring factories机制所实现的。Spring Factories 机制是 Spring Boot 中的一种服务发现机制,这种扩展机制与 Java SPI 机制十分相似(而java的SPI机制这篇博客,我之前想着发表的,但是因为一些原因导致没有发表成功,在之后这段时间,我会找个时间发布,如果有不懂SPI服务的到时候可以去看一看)。Spring Boot 会自动扫描所有 Jar 包类路径下 meta-INF/spring.factories 文件,并读取其中的内容,进行实例化,这种机制也是 Spring Boot Starter 的基础。
而spring factories机制的实现主要依赖一个SpringFactoriesLoader类和多个spring.factories 文件。
spring.factoriesspring.factories文件和java属性文件(.properties)差不多,其中包含一组或多组键值对(key=vlaue),其中,key 的取值为接口的完全限定名;value 的取值为接口实现类的完全限定名,一个接口可以设置多个实现类,不同实现类之间使用“,”隔开,例如:
org.springframework.boot.autoconfigure.AutoConfigurationimportFilter= org.springframework.boot.autoconfigure.condition.OnBeanCondition, org.springframework.boot.autoconfigure.condition.OnClassCondition, org.springframework.boot.autoconfigure.condition.onWebApplicationConditionSpringFactoriesLoader
spring-core 包里定义了 SpringFactoriesLoader 类,这个类会扫描所有 Jar 包类路径下的 meta-INF/spring.factories 文件,并获取指定接口的配置。在 SpringFactoriesLoader 类中定义了两个对外的方法,如下表。
| 方法 | 描述 |
|---|---|
| loadFactories(Class factoryType, @Nullable ClassLoader classLoader) | 根据接口获取其实现类的实例;该方法返回的是实现类对象列表 |
| loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) | 根据接口l获取其实现类的名称;该方法返回的是实现类的类名的列表 |
以上两个方法的关键都是从指定的 ClassLoader 中获取 spring.factories 文件,并解析得到类名列表,具体代码如下:
loadFactories方法:能够获取指定接口的实现类对象
public staticList loadFactories(Class factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } List result = new ArrayList(factoryImplementationNames.size()); Iterator var5 = factoryImplementationNames.iterator(); while(var5.hasNext()) { String factoryImplementationName = (String)var5.next(); result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; }
loadFactoryNames方法:能够根据接口获取其实现类类名的集合
public static ListloadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
而在上面的loadFactoryNames方法中调用了一个内部方法loadSpringFactories方法,这个方法的作用能够读取该项目中所有 Jar 包类路径下 meta-INF/spring.factories 文件的配置内容,并以 Map 集合的形式返回。
private static Map> loadSpringFactories(ClassLoader classLoader) { Map > result = (Map)cache.get(classLoader); if (result != null) { return result; } else { HashMap result = new HashMap(); try { Enumeration urls = classLoader.getResources("meta-INF/spring.factories"); 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 factoryTypeName = ((String)entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); String[] var10 = factoryImplementationNames; int var11 = factoryImplementationNames.length; for(int var12 = 0; var12 < var11; ++var12) { String factoryImplementationName = var10[var12]; ((List)result.computeIfAbsent(factoryTypeName, (key) -> { return new ArrayList(); })).add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> { return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); }); cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [meta-INF/spring.factories]", var14); } } }
到此,我们就已经了解了spring factories的基本原理。下面我们再来了解自动配置的加载是如何实现的
自动配置的加载Spring Boot 自动化配置也是基于 Spring Factories 机制实现的,在 spring-boot-autoconfigure-xxx.jar 类路径下的 meta-INF/spring.factories 中设置了 Spring Boot 自动配置的内容 ,如下。
# 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.Neo4jReactiveDataAutoConfiguration, org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration, 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.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.neo4j.Neo4jAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration, org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration, org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration, 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
以上配置中,value 取值是由多个 xxxAutoConfiguration (使用逗号分隔)组成,每个 xxxAutoConfiguration 都是一个自动配置类。Spring Boot 启动时,会利用 Spring-Factories 机制,将这些 xxxAutoConfiguration 实例化并作为组件加入到容器中,以实现 Spring Boot 的自动配置。
@EnableAutoConfiguration注解@EnableAutoConfiguration 注解用于开启 Spring Boot 的自动配置功能, 它使用 Spring 框架提供的 @import 注解通过 AutoConfigurationimportSelector类(选择器)给容器中导入自动配置组件。而这个注解是SpringBootApplication的元注解之一。
而这个能够帮助SpringBootApplication这个注解实现自动装配的功能,当然,SpringBootApplication这个注解不单纯只有这一个功能,本文不对其他功能进行一个解释,再提一嘴SpringBootApplication注解的功能主要都是由它的元注解所完成。
好了,回到正题,我们知道SpringBootApplication的自动配置功能是由EnableAutoConfiguration 注解所实现的,我们现在就去看看这个注解
在这个注解中我们需要关注其中的import元注解,它导入了一个AutoConfigurationimportSelector配置类,这个配置类会帮我们的EnableAutoConfiguration 注解完成自动配置功能。
AutoConfigurationimportSelector 类实现了 DeferredimportSelector 接口,AutoConfigurationimportSelector 中还包含一个静态内部类 AutoConfigurationGroup,它实现了 DeferredimportSelector 接口的内部接口 Group(Spring 5 新增)
在这个类中,我们需要重点关注三个方法:
- getimportGroup():该方法获取实现了 Group 接口的类,并实例化
- process(Annotationmetadata annotationmetadata, DeferredimportSelector deferredimportSelector):该方法用于引入自动配置的集合
- selectimports():遍历自动配置类集合(Entry 类型的集合),并逐个解析集合中的配置类
他们的执行顺序是:getimportGroup—>process—>selectimports
下面我们将分别对以上 3 个方法及其调用过程进行介绍。
1.getimportGroupAutoConfigurationimportSelector 类中 getimportGroup() 方法主要用于获取实现了 DeferredimportSelector.Group 接口的类,代码如下
public Class extends Group> getimportGroup() {
return AutoConfigurationimportSelector.AutoConfigurationGroup.class;
}
2.process
静态内部类 AutoConfigurationGroup 中的核心方法是 process(),该方法通过调用 getAutoConfigurationEntry() 方法读取 spring.factories 文件中的内容,获得自动配置类的集合,代码如下
public void process(Annotationmetadata annotationmetadata, DeferredimportSelector deferredimportSelector) {
Assert.state(deferredimportSelector instanceof AutoConfigurationimportSelector, () -> {
return String.format("only %s implementations are supported, got %s", AutoConfigurationimportSelector.class.getSimpleName(), deferredimportSelector.getClass().getName());
});
AutoConfigurationimportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationimportSelector)deferredimportSelector).getAutoConfigurationEntry(annotationmetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationmetadata);
}
}
getAutoConfigurationEntry() 方法通过调用 getCandidateConfigurations() 方法来获取自动配置类的完全限定名,并在经过排除、过滤等处理后,将其缓存到成员变量中,具体代码如下。
protected AutoConfigurationimportSelector.AutoConfigurationEntry getAutoConfigurationEntry(Annotationmetadata annotationmetadata) {
if (!this.isEnabled(annotationmetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationmetadata);
List configurations = this.getCandidateConfigurations(annotationmetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set exclusions = this.getExclusions(annotationmetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationimportEvents(configurations, exclusions);
return new AutoConfigurationimportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
在 getCandidateConfigurations() 方法中,根据 Spring Factories 机制调用 SpringFactoriesLoader 的 loadFactoryNames() 方法,根据 EnableAutoConfiguration.class (自动配置接口)获取其实现类(自动配置类)的类名的集合
protected List3.selectimportsgetCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) { List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; } protected Class> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
以上所有方法执行完成后,selectimports() 会将 process() 方法处理后得到的自动配置类,进行过滤、排除,最后将所有自动配置类添加到容器中
public Iterableselectimports() { if (this.autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } else { Set allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationimportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); Set processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationimportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(linkedHashSet::new)); processedConfigurations.removeAll(allExclusions); return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationmetadata()).stream().map((importClassName) -> { return new Entry((Annotationmetadata)this.entries.get(importClassName), importClassName); }).collect(Collectors.toList()); } }
好了,到此,我们的springboot的自动装箱原理就已经讲解完毕,如果觉得有收获的话,就给博主点点赞吧(o( ̄︶ ̄)o)。



