SpringBoot 自動配置初探
springboot的一大特點就是自動配置,本筆記將記錄springboot的自動配置是如何實現的。將對源碼進行跟蹤和分析。
2019年07月31日09:16:31
[email protected]
@EnableAutoConfiguration
當我們看到springboot的啓動類的時候,會在上部分看到這個類的註解,其中有一個@EnableAutoConfiguration
的註解 AutoConfiguration
的出現,告訴我們這個一定和自動 Auto
和配置 Configuration
有關。
按住 ctrl
進入這個註解,我們可以看見這個註解的源代碼,下面截取頭部部分。
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import(AutoConfigurationImportSelector.class)
這個註解的作用,是利用這個選擇器 ...Selector.class
來給容器中導入一些組件。
進入這個註解,下拉。我們可以看見在這個類裏面有如下這樣一個方法。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {...}
meta,一般在計算機中的意思是 元 元數據(Metadata)的元。
這個方法,返回的是字符串數組。
selectImports
我們仔細看源碼的返回值:
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
利用 StringUtils
這個spring的工具類,將一個autoConfigurationEntry.getConfigurations()傳回的數據轉換成了字符串數組。
autoConfigurationEntry
我們來觀察這個autoConfigurationEntry
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
這個AutoConfigurationEntry類是怎麼定義的呢?
protected static class AutoConfigurationEntry {
private final List<String> configurations;
private final Set<String> exclusions;
private AutoConfigurationEntry() {
this.configurations = Collections.emptyList();
this.exclusions = Collections.emptySet();
}
一目瞭然,AutoConfigurationEntry
裏面包含着String類型的集合 List<String> configurations
回到 selectImports這個方法。
這個方法的作用,就是想要獲取到這一系列的配置(configurations)
在selectImports這個方法內部
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
這句話告訴我們,autoConfigurationEntry
也就是想要的這些configurations
,必須通過 getAutoConfigurationEntry
這個方法獲取,我們點進來一看,有這樣一句話:
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
繼續點,找到這樣一個方法:
getCandidateConfigurations
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;
}
其中,下面這個方法纔是最主要的。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
SpringFactoriesLoader
這個類的作用就是掃描所有jar包類路徑下的一個文件,在這個類的源碼首行。
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
"META-INF/spring.factories"
就是需要掃描的路徑和文件。
那麼掃描這個文件的作用是什麼?
loadFactoryNames
在上面提及到的 loadFactoryNames
這個方法裏面,我們可以繼續點進去查看。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
同樣,點入 loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
中的 loadSpringFactories(classLoader)
方法。
在這個方法裏面,代碼的語義可以理解爲。
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
得到這些方法的url,最後將每個方法遍歷。
將這些文件的內容整合成一個properties對象。
String factoryClassName = ((String) entry.getKey()).trim();
每一個url都會被獲取它的值,最後被加載返回值result中,這個result中,就包含了spring容器中需要的一些組件。
回到getCandidateConfigurations方法。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
注意看 loadFactoryNames
的第一個入參。 getSpringFactoriesLoaderFactoryClass()
點進去之後。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
可以清楚的觀察到,EnableAutoConfiguration.class
的存在。從properties中獲取到EnableAutoConfiguration.class這個類類名對應的值,把他們添加在容器中。
Jar包下的spring.factories
我們可以點開一個jar包,查看裏面的內容。
但是查看內容之後,並沒有EnableAutoConfiguration
這個類對應的值。
再往下看一個jar包 spring-boot-autoconfigure-2.1.6.RELEASE.jar
,點開 spring.factories
我們可以清楚的看到,這個jar包配置了 EnableAutoConfiguration
,那麼下面的這些組件,都會被自動配置到spring容器中。
回顧一下,在 SpringFactoriesLoader
類裏面,屬性規定了我們要去 "META-INF/spring.factories"
jar包類路徑下配置所有開啓 EnableAutoConfiguration
的值,把他們加入到容器當中去。
結合上面 spring.factories
文件,這個流程更加的直觀。
我們把這個jar包開啓自動配置之下的值全部拷在下面。
# 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.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
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.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
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.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.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
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.groovy.template.GroovyTemplateAutoConfiguration,\
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.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.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
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.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
仔細觀察,每一個 xxAutoConfiguration
類都是容器中的自動配置類。都加入到容器中去,使用他們來做更多的自動配置功能。
例子 HttpEncodingAutoConfiguration
在上面 我們可以找到 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
我們去文件中找到這類。
@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
private static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final HttpProperties.Encoding properties;
LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
首先是註解的解析:
@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
@Configuration
表示,這是一個配置類的方式向容器中添加組件@Bean
的註解。@EnableConfigurationProperties(HttpProperties.class)
指定啓動HttpProperties.class
這個類的ConfigurationProperties
功能。@ConditionalOnWebApplication
根據當前程序是不是web應用,如果是,則生效。@ConditionalOnClass
判斷當期項目有沒有CharacterEncodingFilter.class
的存在。這個是springmvc中解決亂碼的過濾器@ConditionalOnProperty
判斷配置文件中是否存在"spring.http.encoding"
的配置,matchIfMissing = true
如果不存在,也生效。也就是說,如果在我們的配置文件中,沒有spring.http.encoding.enabled=true
那麼這個註解也是生效的。
在HttpEncodingAutoConfiguration
這個自動配置類生效之後,該類會在容器中添加如下兩個組件 @Bean
。
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
===================================================
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
這些組件的值,也就來自於開頭註解中的配置文件。作爲容器中的一個bean,程序上下文在執行這個方法的時候,自動將裏面的屬性賦值。
HttpProperties
源碼如下:
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
點進來,我們可以看見 @ConfigurationProperties(prefix = "spring.http")
的註解。
這個非常好理解,之前也試過這個註解的使用並那他來和 @Value來區分。
這個註解的意思就是指定了配置文件中這個類屬性key的前綴,spring會自動從其中取出這個值並吧上下文中這個bean的屬性的值進行綁定。
@ConfigurationProperties支持鬆綁定
這個類下面的屬性,也就基本上對應了配置文件中,根據他指定的前綴能取到什麼的值。
所有能在配置文件中配置的屬性,都是在XXXProperties的類中封裝着。
關於字符過濾器,我們在學習springMVC的時候,這些屬性是配置在web.xml中的。遷移過來思考一下,就更容易理解。我們可以觀察之前的SSM項目中的配置文件對應着思考一下。
比如在這個類裏面,我們就能配置:
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Charset charset = DEFAULT_CHARSET;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
仔細看看,是不是都很熟悉。
總結
以上就是springboot的整個精髓,總結如下
- springboot啓動會加載大量配置類。
- 我們在開發需要某個功能的時候,可以先看看springboot裏面有沒有已經寫好的自動配置類。
- 再看看自動配置類裏面可以爲我們配置哪些組件,如果有,我們的配置文件中就不需要提及。
- 容器中自動配置類添加組件的時候,會從
properties
類中獲取屬性,這些屬性都是從配置文件中獲取的。因爲這兩個內容是綁定的。 XXXAutoConfiguration
類是自動配置類,這個類會向spring 容器中配置組件,這些組件的屬性是在xxxProperties
類裏面規定好的。在這個類裏面會與配置文件裏面的值進行綁定。