from:http://www.jianshu.com/p/ccadc2bdb6d7

第5章 Spring Boot自動配置原理

5.1 SpringBoot的核心組件模塊

首先,我們來簡單統計一下SpringBoot核心工程的源碼java文件數量:

我們cd到spring-boot-autoconfigure工程根目錄下。執行

$ tree | grep -c .java$
模塊 java文件數
spring-boot 551
spring-boot-actuator 423
spring-boot-autoconfigure 783
spring-boot-devtools 169
spring-boot-cli 180
spring-boot-tools 355

我們可以看到有783個java文件。spring-boot核心工程有551個java文件。從上面的java文件數量大致可以看出,SpringBoot技術框架的核心組成部分:

spring-boot-autoconfigure spring-boot spring-boot-tools

我們把SpringBoot源碼導入IntelliJ IDEA,查看artifact的全部依賴關系。

IDEA有個Maven Projects窗口,一般在右側能夠找到,如果沒有可以從菜單欄打開:View>Tool Windows>Maven Projects;

選擇要分析的maven module(idea的module相當于eclipse的project),右擊show dependencies,會出來該module的全部依賴關系圖,非常清晰細致。

例如,spring-boot-starter-freemarker的依賴圖分析如下:


在spring-boot-build 的pom中,我們可以看到:

           <modules>                 <module>spring-boot-dependencies</module>                 <module>spring-boot-parent</module>                 <module>spring-boot-tools</module>                 <module>spring-boot</module>                 <module>spring-boot-test</module>                 <module>spring-boot-autoconfigure</module>                 <module>spring-boot-test-autoconfigure</module>                 <module>spring-boot-actuator</module>                 <module>spring-boot-devtools</module>                 <module>spring-boot-docs</module>                 <module>spring-boot-starters</module>                 <module>spring-boot-actuator-docs</module>                 <module>spring-boot-cli</module>             </modules>

其中,在spring-boot-dependencies中,SpringBoot項目維護了一份龐大依賴。這些依賴的版本都是經過實踐,測試通過,不會發生依賴沖突的。就這樣一個事情,就大大減少了Spring開發過程中,出現jar包沖突的概率。spring-boot-parent依賴spring-boot-dependencies。

下面我們簡要介紹一下SpringBoot子modules。

spring-boot

SpringBoot核心工程。

spring-boot-starters

是SpringBoot的啟動服務工程。

spring-boot-autoconfigure

是SpringBoot實現自動配置的核心工程。

spring-boot-actuator

提供SpringBoot應用的外圍支撐性功能。 比如:

  • Endpoints,SpringBoot應用狀態監控管理
  • HealthIndicator,SpringBoot應用健康指示表
  • 提供metrics支持
  • 提供遠程shell支持

spring-boot-tools

提供了SpringBoot開發者的常用工具集。諸如,spring-boot-gradle-plugin,spring-boot-maven-plugin就是這個工程里面的。

spring-boot-cli

是Spring Boot命令行交互工具,可用于使用Spring進行快速原型搭建。你可以用它直接運行Groovy腳本。如果你不喜歡Maven或Gradle,Spring提供了CLI(Command Line Interface)來開發運行Spring應用程序。你可以使用它來運行Groovy腳本,甚至編寫自定義命令。

5.2 SpringBoot Starters

Spring boot中的starter概念是非常重要的機制,能夠拋棄以前繁雜的配置,統一集成進starter,應用者只需要引入starter jar包,spring boot就能自動掃描到要加載的信息。

starter讓我們擺脫了各種依賴庫的處理,需要配置各種信息的困擾。Spring Boot會自動通過classpath路徑下的類發現需要的Bean,并織入bean。

例如,如果你想使用Spring和用JPA訪問數據庫,你只要依賴 spring-boot-starter-data-jpa 即可。

目前,github上spring-boot項目的最新的starter列表spring-boot/spring-boot-starters如下:

spring-boot-starter spring-boot-starter-activemq spring-boot-starter-actuator spring-boot-starter-amqp spring-boot-starter-aop spring-boot-starter-artemis spring-boot-starter-batch spring-boot-starter-cache spring-boot-starter-cloud-connectors spring-boot-starter-data-cassandra spring-boot-starter-data-couchbase spring-boot-starter-data-elasticsearch spring-boot-starter-data-jpa spring-boot-starter-data-ldap spring-boot-starter-data-mongodb spring-boot-starter-data-mongodb-reactive spring-boot-starter-data-neo4j spring-boot-starter-data-redis spring-boot-starter-data-rest spring-boot-starter-data-solr spring-boot-starter-freemarker spring-boot-starter-groovy-templates spring-boot-starter-hateoas spring-boot-starter-integration spring-boot-starter-jdbc spring-boot-starter-jersey spring-boot-starter-jetty spring-boot-starter-jooq spring-boot-starter-jta-atomikos spring-boot-starter-jta-bitronix spring-boot-starter-jta-narayana spring-boot-starter-log4j2 spring-boot-starter-logging spring-boot-starter-mail spring-boot-starter-mobile spring-boot-starter-mustache spring-boot-starter-parent spring-boot-starter-reactor-netty spring-boot-starter-security spring-boot-starter-social-facebook spring-boot-starter-social-linkedin spring-boot-starter-social-twitter spring-boot-starter-test spring-boot-starter-thymeleaf spring-boot-starter-tomcat spring-boot-starter-undertow spring-boot-starter-validation spring-boot-starter-web spring-boot-starter-web-services spring-boot-starter-webflux spring-boot-starter-websocket

(源代碼目錄執行shell:l|awk '{print $9}', l|awk '{print $9}'|grep -c 'starter')

共52個。每個starter工程里面的pom描述有相應的介紹。具體的說明,參考官網文檔[1]。關于這些starters的使用例子,可以參考spring-boot/spring-boot-samples

比如說,spring-boot-starter是:

Core starter, including auto-configuration support, logging and YAML

這是Spring Boot的核心啟動器,包含了自動配置、日志和YAML。它的項目依賴圖如下:



可以看出,這些starter只是配置,真正做自動化配置的代碼的是在spring-boot-autoconfigure里面。同時spring-boot-autoconfigure依賴spring-boot工程,這個spring-boot工程是SpringBoot的核心。

SpringBoot會基于你的classpath中的jar包,試圖猜測和配置您可能需要的bean。

例如,如果你的classpath中有tomcat-embedded.jar,你可能會想要一個TomcatEmbeddedServletContainerFactory Bean (SpringBoot通過獲取EmbeddedServletContainerFactory來啟動對應的web服務器。常用的兩個實現類是TomcatEmbeddedServletContainerFactory和JettyEmbeddedServletContainerFactory)。

其他的所有基于Spring Boot的starter都依賴這個spring-boot-starter。比如說spring-boot-starter-actuator的依賴樹,如下圖:


5.3 @EnableAutoConfiguration自動配置原理

通過@EnableAutoConfiguration啟用Spring應用程序上下文的自動配置,這個注解會導入一個EnableAutoConfigurationImportSelector的類,而這個類會去讀取一個spring.factories下key為EnableAutoConfiguration對應的全限定名的值。

這個spring.factories里面配置的那些類,主要作用是告訴Spring Boot這個stareter所需要加載的那些xxxAutoConfiguration類,也就是你真正的要自動注冊的那些bean或功能。然后,我們實現一個spring.factories指定的類,標上@Configuration注解,一個starter就定義完了。

如果想從自己的starter種讀取應用的starter工程的配置,只需要在入口類上加上如下注解即可:

@EnableConfigurationProperties(MyProperties.class)

讀取spring.factories文件的實現

是通過org.springframework.core.io.support.SpringFactoriesLoader實現。

SpringFactoriesLoader的實現類似于SPI(Service Provider Interface,在java.util.ServiceLoader的文檔里有比較詳細的介紹。java SPI提供一種服務發現機制,為某個接口尋找服務實現的機制。有點類似IOC的思想,就是將裝配的控制權移到程序之外,在模塊化設計中這個機制尤其重要[3])。

SpringFactoriesLoader會加載classpath下所有JAR文件里面的META-INF/spring.factories文件。

其中加載spring.factories文件的代碼在loadFactoryNames方法里:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";  ....      public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {         String factoryClassName = factoryClass.getName();         try {             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));             List<String> result = new ArrayList<>();             while (urls.hasMoreElements()) {                 URL url = urls.nextElement();                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));                 String factoryClassNames = properties.getProperty(factoryClassName);                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));             }             return result;         }         catch (IOException ex) {             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);         }     }

通過org.springframework.boot.autoconfigure.AutoConfigurationImportSelector里面的getCandidateConfigurations方法,獲取到候選類的名字List<String>。該方法代碼如下:

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

其中,getSpringFactoriesLoaderFactoryClass()方法直接返回的是EnableAutoConfiguration.class, 代碼如下:

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {         return EnableAutoConfiguration.class;     }

所以,getCandidateConfigurations方法里面的這段代碼:

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

會過濾出key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對應的值。全限定名都使用如下命名方法:

包名.外部類名 包名.外部類名$內部類名  e.g:  org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

SpringBoot中的META-INF/spring.factories(完整路徑:spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)中關于EnableAutoConfiguration的這段配置如下:

# 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.CloudAutoConfiguration,\ 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.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ 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.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoRepositoriesAutoConfiguration,\ 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.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.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.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.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.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ 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.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAnnotationAutoConfiguration,\ 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.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

當然了,這些AutoConfiguration不是所有都會加載的,會根據AutoConfiguration上的@ConditionalOnClass等條件,再進一步判斷是否加載。我們下文通過FreeMarkerAutoConfiguration實例來分析整個自動配置的過程。

5.4 FreeMarkerAutoConfiguration自動配置的實例分析

我們首先看spring-boot-starter-freemarker工程,目錄結構如下:

. ├── pom.xml ├── spring-boot-starter-freemarker.iml └── src     └── main         └── resources             └── META-INF                 └── spring.provides  4 directories, 3 files

我們可以看出,這個工程沒有任何Java代碼,只有兩個文件:pom.xml跟spring.provides。starter本身在你的應用程序中實際上是空的。

其中,
spring.provides文件

provides: freemarker,spring-context-support

主要是給這個starter起個好區分的名字。

Spring Boot 通過starter對項目的依賴進行統一管理. starter利用了maven的傳遞依賴解析機制,把常用庫聚合在一起, 組成了針對特定功能而定制的依賴starter。

我們可以使用IDEA提供的maven依賴圖分析的功能(如下圖),得到spring-boot-starter-freemarker依賴的module。


IDEA提供的maven依賴圖分析

spring-boot-starter-freemarker依賴的module

從上面的依賴圖,我們可以清晰看出其間依賴關系。

當Spring Boot Application中自動配置EnableAutoConfiguration的相關類執行完畢之后,Spring Boot會進一步解析對應類的配置信息。如果我們配置了spring-boot-starter-freemarker ,maven就會通過這個starter所依賴的spring-boot-autoconfigure,自動傳遞到spring-boot-autoconfigure工程中。

我們來簡單分析一下spring-boot-autoconfigure工程的架構。

其中,FreeMarker的自動配置類是org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration。

下面我們來簡要分析一下FreeMarkerAutoConfiguration這個類。

在FreeMarkerAutoConfiguration類上面有四行注解:

@Configuration @ConditionalOnClass({ freemarker.template.Configuration.class,         FreeMarkerConfigurationFactory.class }) @AutoConfigureAfter(WebMvcAutoConfiguration.class) @EnableConfigurationProperties(FreeMarkerProperties.class) public class FreeMarkerAutoConfiguration {     ... }

其中,
(1)@Configuration,是org.springframework.context.annotation包里面的注解。這么說吧,用@Configuration注解該類,等價 與XML中配置beans;用@Bean標注方法等價于XML中配置bean。

(2)@ConditionalOnClass,org.springframework.boot.autoconfigure.condition包里面的注解。意思是當類路徑下有指定的類的條件下,才會去注冊被標注的類為一個bean。在上面的代碼中的意思就是,當類路徑中有freemarker.template.Configuration.class,FreeMarkerConfigurationFactory.class兩個類的時候,才會實例化FreeMarkerAutoConfiguration這個Bean。

(3)@AutoConfigureAfter,org.springframework.boot.autoconfigure包里面的注解。這個通過注解的名字意思就可以知道,當WebMvcAutoConfiguration.class這個類實例化完畢,才能實例化FreeMarkerAutoConfiguration(有個先后順序)。SpringBoot使用@ AutoConfigureBefore、@AutoConfigureAfter注解來定義這些配置類的載入順序。

(4)@EnableConfigurationProperties,表示啟動對FreeMarkerProperties.class的內嵌配置支持,自動將FreeMarkerProperties注冊為一個bean。這個FreeMarkerProperties類里面就是關于FreeMarker屬性的配置:

@ConfigurationProperties(prefix = "spring.freemarker") public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {      public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";      public static final String DEFAULT_PREFIX = "";      public static final String DEFAULT_SUFFIX = ".ftl";      /**      * Well-known FreeMarker keys which will be passed to FreeMarker's Configuration.      */     private Map<String, String> settings = new HashMap<>();      /**      * Comma-separated list of template paths.      */     private String[] templateLoaderPath = new String[] { DEFAULT_TEMPLATE_LOADER_PATH };      /**      * Prefer file system access for template loading. File system access enables hot      * detection of template changes.      */     private boolean preferFileSystemAccess = true;      public FreeMarkerProperties() {         super(DEFAULT_PREFIX, DEFAULT_SUFFIX);     }      public Map<String, String> getSettings() {         return this.settings;     }      public void setSettings(Map<String, String> settings) {         this.settings = settings;     }      public String[] getTemplateLoaderPath() {         return this.templateLoaderPath;     }      public boolean isPreferFileSystemAccess() {         return this.preferFileSystemAccess;     }      public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {         this.preferFileSystemAccess = preferFileSystemAccess;     }      public void setTemplateLoaderPath(String... templateLoaderPaths) {         this.templateLoaderPath = templateLoaderPaths;     }  }

綜上,當(1)(2)兩個條件滿足時,才會繼續(3)(4)的動作,同時注冊FreeMarkerAutoConfiguration這個Bean。該類的結構如下圖:


我們來看其內部類FreeMarkerWebConfiguration的代碼:

    @Configuration     @ConditionalOnClass(Servlet.class)     @ConditionalOnWebApplication(type = Type.SERVLET)     public static class FreeMarkerWebConfiguration extends FreeMarkerConfiguration {          @Bean         @ConditionalOnMissingBean(FreeMarkerConfig.class)         public FreeMarkerConfigurer freeMarkerConfigurer() {             FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();             applyProperties(configurer);             return configurer;         }          @Bean         public freemarker.template.Configuration freeMarkerConfiguration(                 FreeMarkerConfig configurer) {             return configurer.getConfiguration();         }          @Bean         @ConditionalOnMissingBean(name = "freeMarkerViewResolver")         @ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)         public FreeMarkerViewResolver freeMarkerViewResolver() {             FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();             this.properties.applyToViewResolver(resolver);             return resolver;         }          @Bean         @ConditionalOnMissingBean         @ConditionalOnEnabledResourceChain         public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {             return new ResourceUrlEncodingFilter();         }      }

其中,
(1)@ConditionalOnWebApplication(type = Type.SERVLET), 是當該應用是基于Servlet的Web應用時。

(2)@ConditionalOnMissingBean(name = "freeMarkerViewResolver"),是當Spring容器中不存在freeMarkerViewResolver的Bean時。

(3)@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true),指定的spring.freemarker.enabled屬性是否有。如果沒有(IfMissing),設為true。

當(1)(2)(3)三個條件都滿足,則注冊freeMarkerViewResolver這個Bean。

我們也可以自定義我們自己的my-starter,以及實現對應的@MyEnableAutoConfiguration。SpringBoot有很多第三方starter,其自動配置的原理基本都是這樣,比如mybatis-spring-boot-starter的MybatisAutoConfiguration,閱讀源碼https://github.com/mybatis/spring-boot-starter[4]

上面文字描述了這么多,再用一張形象生動的圖來說明[5]:


SpringBoot Autoconfigure 工作原理圖

5.5 spring.factories與定義應用程序的初始化行為

上面說了這么多,講的都是讀取properties文件中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對應的值。SpringBoot內部還有許多其他的key用于過濾得到需要加載的類。

# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer  # 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.OnClassCondition  # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer  # 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

這些key仍然是定義在spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories文件中。

還有對應的用于測試的自動配置,在
spring-boot/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories文件中定義。

另外,我們使用spring.factories里還可以定制應用程序的初始化行為。這樣我們就可以在應用程序載入前操縱Spring的應用程序上下文ApplicationContext。

例如,可以使用ConfigurableApplicationContext類的addApplicationListener()方法,在應用上下文ApplicationContext中創建監聽器。

自動配置運行日志報告功能就是這么實現的。我們來看在spring.factories中,Initializers一段的配置:

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

其中,AutoConfigurationReportLoggingInitializer監聽到系統事件時,比如上下文刷新ContextRefreshedEvent或應用程序啟動故障ApplicationFailedEvent之類的事件,Spring Boot可以做一些事情。這里說的代碼在AutoConfigurationReportLoggingInitializer.AutoConfigurationReportListener里面。關于支持的事件類型supportsEventType的如下:

    private class AutoConfigurationReportListener implements GenericApplicationListener {  ...         @Override         public boolean supportsEventType(ResolvableType resolvableType) {             Class<?> type = resolvableType.getRawClass();             if (type == null) {                 return false;             }             return ContextRefreshedEvent.class.isAssignableFrom(type)                     || ApplicationFailedEvent.class.isAssignableFrom(type);         }          @Override         public boolean supportsSourceType(Class<?> sourceType) {             return true;         }          @Override         public void onApplicationEvent(ApplicationEvent event) {     AutoConfigurationReportLoggingInitializer.this.onApplicationEvent(event);         }      }

要以調試模式啟動應用程序,可以使用-Ddebug標識,或者在application.properties文件這添加屬性debug= true。這樣,當我們以調試模式啟動應用程序時,SpringBoot就可以幫助我們創建自動配置的運行報告。對于每個自動配置,通過報告我們可以看到它啟動或失敗的原因。 這個報告內容格式大致如下:

========================= AUTO-CONFIGURATION REPORT =========================   Positive matches: -----------------     DataSourceAutoConfiguration matched:       - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)     DataSourceAutoConfiguration#dataSourceInitializer matched:       - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer; SearchStrategy: all) did not find any beans (OnBeanCondition)     DataSourceAutoConfiguration.PooledDataSourceConfiguration matched:       - AnyNestedCondition 2 matched 0 did not; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.PooledDataSourceAvailable PooledDataSource found supported DataSource; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.ExplicitType @ConditionalOnProperty (spring.datasource.type) matched (DataSourceAutoConfiguration.PooledDataSourceCondition)       - @ConditionalOnMissingBean (types: javax.sql.DataSource,javax.sql.XADataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)     ...  Exclusions: -----------      None   Unconditional classes: ----------------------      org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration      org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration      org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration      org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration

除了SpringBoot官方提供的starter外,還有社區貢獻的很多常用的第三方starter,列表可參考[2]。

另外,國內很多公司使用RPC框架dubbo,關于SpringBoot集成dubbo,可參考:https://github.com/linux-china/spring-boot-dubbo。

參考資料:

1.http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter
2.https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters
3.http://www.cnblogs.com/javaee6/p/3714719.html
4.https://github.com/mybatis/spring-boot-starter
5.https://afoo.me/posts/2015-07-09-how-spring-boot-works.html



作者:華夏商周秦漢唐宋元明清中華民國
鏈接:http://www.jianshu.com/p/ccadc2bdb6d7
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。