第5章 Spring Boot自動(dòng)配置原理
5.1 SpringBoot的核心組件模塊
首先割按,我們來(lái)簡(jiǎn)單統(tǒng)計(jì)一下SpringBoot核心工程的源碼java文件數(shù)量:
我們cd到spring-boot-autoconfigure工程根目錄下悟狱。執(zhí)行
$ tree | grep -c .java$
模塊 | java文件數(shù) |
---|---|
spring-boot | 551 |
spring-boot-actuator | 423 |
spring-boot-autoconfigure | 783 |
spring-boot-devtools | 169 |
spring-boot-cli | 180 |
spring-boot-tools | 355 |
我們可以看到有783個(gè)java文件静浴。spring-boot核心工程有551個(gè)java文件。從上面的java文件數(shù)量大致可以看出挤渐,SpringBoot技術(shù)框架的核心組成部分:
spring-boot-autoconfigure
spring-boot
spring-boot-tools
我們把SpringBoot源碼導(dǎo)入IntelliJ IDEA苹享,查看artifact的全部依賴關(guān)系。
IDEA有個(gè)Maven Projects窗口浴麻,一般在右側(cè)能夠找到得问,如果沒(méi)有可以從菜單欄打開(kāi):View>Tool Windows>Maven Projects;
選擇要分析的maven module(idea的module相當(dāng)于eclipse的project),右擊show dependencies,會(huì)出來(lái)該module的全部依賴關(guān)系圖,非常清晰細(xì)致软免。
例如宫纬,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項(xiàng)目維護(hù)了一份龐大依賴。這些依賴的版本都是經(jīng)過(guò)實(shí)踐榛泛,測(cè)試通過(guò)蝌蹂,不會(huì)發(fā)生依賴沖突的。就這樣一個(gè)事情挟鸠,就大大減少了Spring開(kāi)發(fā)過(guò)程中叉信,出現(xiàn)jar包沖突的概率。spring-boot-parent依賴spring-boot-dependencies艘希。
下面我們簡(jiǎn)要介紹一下SpringBoot子modules硼身。
spring-boot
SpringBoot核心工程。
spring-boot-starters
是SpringBoot的啟動(dòng)服務(wù)工程覆享。
spring-boot-autoconfigure
是SpringBoot實(shí)現(xiàn)自動(dòng)配置的核心工程佳遂。
spring-boot-actuator
提供SpringBoot應(yīng)用的外圍支撐性功能。 比如:
- Endpoints撒顿,SpringBoot應(yīng)用狀態(tài)監(jiān)控管理
- HealthIndicator丑罪,SpringBoot應(yīng)用健康指示表
- 提供metrics支持
- 提供遠(yuǎn)程shell支持
spring-boot-tools
提供了SpringBoot開(kāi)發(fā)者的常用工具集。諸如,spring-boot-gradle-plugin吩屹,spring-boot-maven-plugin就是這個(gè)工程里面的跪另。
spring-boot-cli
是Spring Boot命令行交互工具,可用于使用Spring進(jìn)行快速原型搭建煤搜。你可以用它直接運(yùn)行Groovy腳本免绿。如果你不喜歡Maven或Gradle,Spring提供了CLI(Command Line Interface)來(lái)開(kāi)發(fā)運(yùn)行Spring應(yīng)用程序擦盾。你可以使用它來(lái)運(yùn)行Groovy腳本嘲驾,甚至編寫(xiě)自定義命令。
5.2 SpringBoot Starters
Spring boot中的starter概念是非常重要的機(jī)制迹卢,能夠拋棄以前繁雜的配置辽故,統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要引入starter jar包腐碱,spring boot就能自動(dòng)掃描到要加載的信息誊垢。
starter讓我們擺脫了各種依賴庫(kù)的處理,需要配置各種信息的困擾喻杈。Spring Boot會(huì)自動(dòng)通過(guò)classpath路徑下的類發(fā)現(xiàn)需要的Bean彤枢,并織入bean。
例如筒饰,如果你想使用Spring和用JPA訪問(wèn)數(shù)據(jù)庫(kù)缴啡,你只要依賴 spring-boot-starter-data-jpa 即可。
目前瓷们,github上spring-boot項(xiàng)目的最新的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
(源代碼目錄執(zhí)行shell:l|awk '{print $9}'业栅, l|awk '{print $9}'|grep -c 'starter')
共52個(gè)。每個(gè)starter工程里面的pom描述有相應(yīng)的介紹谬晕。具體的說(shuō)明碘裕,參考官網(wǎng)文檔[1]。關(guān)于這些starters的使用例子攒钳,可以參考spring-boot/spring-boot-samples
比如說(shuō)帮孔,spring-boot-starter是:
Core starter, including auto-configuration support, logging and YAML
這是Spring Boot的核心啟動(dòng)器,包含了自動(dòng)配置不撑、日志和YAML文兢。它的項(xiàng)目依賴圖如下:
可以看出,這些starter只是配置焕檬,真正做自動(dòng)化配置的代碼的是在spring-boot-autoconfigure里面姆坚。同時(shí)spring-boot-autoconfigure依賴spring-boot工程,這個(gè)spring-boot工程是SpringBoot的核心实愚。
SpringBoot會(huì)基于你的classpath中的jar包闷串,試圖猜測(cè)和配置您可能需要的bean。
例如躏吊,如果你的classpath中有tomcat-embedded.jar,你可能會(huì)想要一個(gè)TomcatEmbeddedServletContainerFactory Bean (SpringBoot通過(guò)獲取EmbeddedServletContainerFactory來(lái)啟動(dòng)對(duì)應(yīng)的web服務(wù)器琼梆。常用的兩個(gè)實(shí)現(xiàn)類是TomcatEmbeddedServletContainerFactory和JettyEmbeddedServletContainerFactory)。
其他的所有基于Spring Boot的starter都依賴這個(gè)spring-boot-starter。比如說(shuō)spring-boot-starter-actuator的依賴樹(shù),如下圖:
5.3 @EnableAutoConfiguration自動(dòng)配置原理
通過(guò)@EnableAutoConfiguration啟用Spring應(yīng)用程序上下文的自動(dòng)配置蕉鸳,這個(gè)注解會(huì)導(dǎo)入一個(gè)EnableAutoConfigurationImportSelector的類,而這個(gè)類會(huì)去讀取一個(gè)spring.factories下key為EnableAutoConfiguration對(duì)應(yīng)的全限定名的值。
這個(gè)spring.factories里面配置的那些類忍法,主要作用是告訴Spring Boot這個(gè)stareter所需要加載的那些xxxAutoConfiguration類,也就是你真正的要自動(dòng)注冊(cè)的那些bean或功能榕吼。然后饿序,我們實(shí)現(xiàn)一個(gè)spring.factories指定的類,標(biāo)上@Configuration注解羹蚣,一個(gè)starter就定義完了原探。
如果想從自己的starter種讀取應(yīng)用的starter工程的配置,只需要在入口類上加上如下注解即可:
@EnableConfigurationProperties(MyProperties.class)
讀取spring.factories文件的實(shí)現(xiàn)
是通過(guò)org.springframework.core.io.support.SpringFactoriesLoader實(shí)現(xiàn)顽素。
SpringFactoriesLoader的實(shí)現(xiàn)類似于SPI(Service Provider Interface咽弦,在java.util.ServiceLoader的文檔里有比較詳細(xì)的介紹。java SPI提供一種服務(wù)發(fā)現(xiàn)機(jī)制胁出,為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制型型。有點(diǎn)類似IOC的思想,就是將裝配的控制權(quán)移到程序之外全蝶,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要[3])闹蒜。
SpringFactoriesLoader會(huì)加載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);
}
}
通過(guò)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());
會(huì)過(guò)濾出key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對(duì)應(yīng)的值砌烁。全限定名都使用如下命名方法:
包名.外部類名
包名.外部類名$內(nèi)部類名
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)中關(guān)于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
當(dāng)然了,這些AutoConfiguration不是所有都會(huì)加載的催式,會(huì)根據(jù)AutoConfiguration上的@ConditionalOnClass等條件函喉,再進(jìn)一步判斷是否加載。我們下文通過(guò)FreeMarkerAutoConfiguration實(shí)例來(lái)分析整個(gè)自動(dòng)配置的過(guò)程蓄氧。
5.4 FreeMarkerAutoConfiguration自動(dòng)配置的實(shí)例分析
我們首先看spring-boot-starter-freemarker工程函似,目錄結(jié)構(gòu)如下:
.
├── pom.xml
├── spring-boot-starter-freemarker.iml
└── src
└── main
└── resources
└── META-INF
└── spring.provides
4 directories, 3 files
我們可以看出,這個(gè)工程沒(méi)有任何Java代碼喉童,只有兩個(gè)文件:pom.xml跟spring.provides撇寞。starter本身在你的應(yīng)用程序中實(shí)際上是空的顿天。
其中,
spring.provides文件
provides: freemarker,spring-context-support
主要是給這個(gè)starter起個(gè)好區(qū)分的名字蔑担。
Spring Boot 通過(guò)starter對(duì)項(xiàng)目的依賴進(jìn)行統(tǒng)一管理. starter利用了maven的傳遞依賴解析機(jī)制,把常用庫(kù)聚合在一起, 組成了針對(duì)特定功能而定制的依賴starter牌废。
我們可以使用IDEA提供的maven依賴圖分析的功能(如下圖),得到spring-boot-starter-freemarker依賴的module啤握。
從上面的依賴圖鸟缕,我們可以清晰看出其間依賴關(guān)系。
當(dāng)Spring Boot Application中自動(dòng)配置EnableAutoConfiguration的相關(guān)類執(zhí)行完畢之后排抬,Spring Boot會(huì)進(jìn)一步解析對(duì)應(yīng)類的配置信息懂从。如果我們配置了spring-boot-starter-freemarker ,maven就會(huì)通過(guò)這個(gè)starter所依賴的spring-boot-autoconfigure蹲蒲,自動(dòng)傳遞到spring-boot-autoconfigure工程中番甩。
我們來(lái)簡(jiǎn)單分析一下spring-boot-autoconfigure工程的架構(gòu)。
其中届搁,F(xiàn)reeMarker的自動(dòng)配置類是org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration缘薛。
下面我們來(lái)簡(jiǎn)要分析一下FreeMarkerAutoConfiguration這個(gè)類。
在FreeMarkerAutoConfiguration類上面有四行注解:
@Configuration
@ConditionalOnClass({ freemarker.template.Configuration.class,
FreeMarkerConfigurationFactory.class })
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(FreeMarkerProperties.class)
public class FreeMarkerAutoConfiguration {
...
}
其中卡睦,
(1)@Configuration宴胧,是org.springframework.context.annotation包里面的注解。這么說(shuō)吧表锻,用@Configuration注解該類恕齐,等價(jià) 與XML中配置beans;用@Bean標(biāo)注方法等價(jià)于XML中配置bean浩嫌。
(2)@ConditionalOnClass檐迟,org.springframework.boot.autoconfigure.condition包里面的注解。意思是當(dāng)類路徑下有指定的類的條件下码耐,才會(huì)去注冊(cè)被標(biāo)注的類為一個(gè)bean追迟。在上面的代碼中的意思就是,當(dāng)類路徑中有freemarker.template.Configuration.class,FreeMarkerConfigurationFactory.class兩個(gè)類的時(shí)候骚腥,才會(huì)實(shí)例化FreeMarkerAutoConfiguration這個(gè)Bean敦间。
(3)@AutoConfigureAfter,org.springframework.boot.autoconfigure包里面的注解束铭。這個(gè)通過(guò)注解的名字意思就可以知道廓块,當(dāng)WebMvcAutoConfiguration.class這個(gè)類實(shí)例化完畢,才能實(shí)例化FreeMarkerAutoConfiguration(有個(gè)先后順序)契沫。SpringBoot使用@ AutoConfigureBefore带猴、@AutoConfigureAfter注解來(lái)定義這些配置類的載入順序。
(4)@EnableConfigurationProperties懈万,表示啟動(dòng)對(duì)FreeMarkerProperties.class的內(nèi)嵌配置支持拴清,自動(dòng)將FreeMarkerProperties注冊(cè)為一個(gè)bean靶病。這個(gè)FreeMarkerProperties類里面就是關(guān)于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;
}
}
綜上,當(dāng)(1)(2)兩個(gè)條件滿足時(shí)口予,才會(huì)繼續(xù)(3)(4)的動(dòng)作娄周,同時(shí)注冊(cè)FreeMarkerAutoConfiguration這個(gè)Bean。該類的結(jié)構(gòu)如下圖:
我們來(lái)看其內(nèi)部類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)煤辨, 是當(dāng)該應(yīng)用是基于Servlet的Web應(yīng)用時(shí)。
(2)@ConditionalOnMissingBean(name = "freeMarkerViewResolver")木张,是當(dāng)Spring容器中不存在freeMarkerViewResolver的Bean時(shí)众辨。
(3)@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true),指定的spring.freemarker.enabled屬性是否有舷礼。如果沒(méi)有(IfMissing)泻轰,設(shè)為true。
當(dāng)(1)(2)(3)三個(gè)條件都滿足且轨,則注冊(cè)freeMarkerViewResolver這個(gè)Bean。
我們也可以自定義我們自己的my-starter虚婿,以及實(shí)現(xiàn)對(duì)應(yīng)的@MyEnableAutoConfiguration旋奢。SpringBoot有很多第三方starter,其自動(dòng)配置的原理基本都是這樣然痊,比如mybatis-spring-boot-starter的MybatisAutoConfiguration至朗,閱讀源碼https://github.com/mybatis/spring-boot-starter[4]。
上面文字描述了這么多剧浸,再用一張形象生動(dòng)的圖來(lái)說(shuō)明[5]:
5.5 spring.factories與定義應(yīng)用程序的初始化行為
上面說(shuō)了這么多锹引,講的都是讀取properties文件中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名對(duì)應(yīng)的值。SpringBoot內(nèi)部還有許多其他的key用于過(guò)濾得到需要加載的類唆香。
# 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文件中嫌变。
還有對(duì)應(yīng)的用于測(cè)試的自動(dòng)配置,在
spring-boot/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories文件中定義躬它。
另外腾啥,我們使用spring.factories里還可以定制應(yīng)用程序的初始化行為。這樣我們就可以在應(yīng)用程序載入前操縱Spring的應(yīng)用程序上下文ApplicationContext冯吓。
例如倘待,可以使用ConfigurableApplicationContext類的addApplicationListener()方法,在應(yīng)用上下文ApplicationContext中創(chuàng)建監(jiān)聽(tīng)器组贺。
自動(dòng)配置運(yùn)行日志報(bào)告功能就是這么實(shí)現(xiàn)的凸舵。我們來(lái)看在spring.factories中,Initializers一段的配置:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
其中失尖,AutoConfigurationReportLoggingInitializer監(jiān)聽(tīng)到系統(tǒng)事件時(shí)啊奄,比如上下文刷新ContextRefreshedEvent或應(yīng)用程序啟動(dòng)故障ApplicationFailedEvent之類的事件渐苏,Spring Boot可以做一些事情。這里說(shuō)的代碼在AutoConfigurationReportLoggingInitializer.AutoConfigurationReportListener里面增热。關(guān)于支持的事件類型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);
}
}
要以調(diào)試模式啟動(dòng)應(yīng)用程序整以,可以使用-Ddebug標(biāo)識(shí),或者在application.properties文件這添加屬性debug= true峻仇。這樣公黑,當(dāng)我們以調(diào)試模式啟動(dòng)應(yīng)用程序時(shí),SpringBoot就可以幫助我們創(chuàng)建自動(dòng)配置的運(yùn)行報(bào)告摄咆。對(duì)于每個(gè)自動(dòng)配置凡蚜,通過(guò)報(bào)告我們可以看到它啟動(dòng)或失敗的原因。 這個(gè)報(bào)告內(nèi)容格式大致如下:
=========================
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外吭从,還有社區(qū)貢獻(xiàn)的很多常用的第三方starter朝蜘,列表可參考[2]。
另外涩金,國(guó)內(nèi)很多公司使用RPC框架dubbo谱醇,關(guān)于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