前言
SpringBoot中我們經常直接使用@SpringBootApplication
這個複合註解去標記爲配置類,包含了SpringBoot常用的三個標籤@Configuration
、@EnableAutoConfiguration
、@ComponentScan
.具有這三個標籤的作用聲明爲一個配置類、自動配置、自動掃描
使用@SpringBootApplication
註解
我們直接進入這個註解中,查看它的源碼。
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
有道翻譯:
* 表示一個{@link Configuration configuration}類,該類聲明一個或多個
* {@link Bean @Bean}方法,並且還觸發{@link EnableAutoConfiguration
* 自動配置}和{@link ComponentScan組件掃描}。這是一個方便的註釋,等同於聲明{@code @Configuration},
* {@code @EnableAutoConfiguration}和{@code @ComponentScan}。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration <---------聲明爲配置類
@EnableAutoConfiguration <---------完成自動配置
@ComponentScan(excludeFilters = { <---------實現組件掃描
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
自動配置實現流程
1.@Import
標籤
現在我們繼續進入@EnableAutoConfiguration
標籤裏面我們可以看到,EnableAutoConfiguration
這個類,它使用了一個import導入了一個AutoConfigurationImportSelector.calss
自動導入選擇器.所以我們完成自動配置的具體功能還在這裏面。
@Import(AutoConfigurationImportSelector.class) <------導入選擇器
public @interface EnableAutoConfiguration
@Import這個註解,能將繼承了
ImportSelector
這個類的子類,調用子類中的:selectImports()
方法傳入完全限定名,就可以將完全限定名對應的類導入SpringIOC容器中.會把第三方jar的類加載到當前spring容器
2.AutoConfigurationImportSelector
自動導入選擇器
2.1繼承DeferredImportSelector類
這個類的父類就繼承了ImportSelector
。就剛好可以與上面的import
標籤相對
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
public interface DeferredImportSelector extends ImportSelector
2.2selectImports()
方法
在AutoConfigurationImportSelector
這個類中就實現了這樣一個selectImports()
這個方法(核心),可以幫助我們調用SpringFactoriesLoader.loadFactoryNames()
這個方法
//從這裏可以看出該類實現很多的xxxAware和DeferredImportSelector,所有的aware都優先於selectImports
//方法執行,也就是說selectImports方法最後執行,那麼在它執行的時候所有需要的資源都已經獲取到了
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//1加載META-INF/spring-autoconfigure-metadata.properties文件
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//2獲取註解的屬性及其值(PS:註解指的是@EnableAutoConfiguration註解)
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//3.在classpath下所有的META-INF/spring.factories文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,並將其封裝到一個List中返回
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//4.對上一步返回的List中的元素去重、排序
configurations = this.removeDuplicates(configurations);
//5.依據第2步中獲取的屬性值排除一些特定的類
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//6對上一步中所得到的List進行過濾,過濾的依據是條件匹配。這裏用到的過濾器是
//org.springframework.boot.autoconfigure.condition.OnClassCondition最終返回的是一個ConditionOutcome[]
//數組。(PS:很多類都是依賴於其它的類的,當有某個類時纔會裝配,所以這次過濾的就是根據是否有某個
//class進而決定是否裝配的。這些類所依賴的類都寫在META-INF/spring-autoconfigure-metadata.properties文件裏)
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
2.3selectImports()
方法中list集合是整個的核心
//3.在classpath下所有的META-INF/spring.factories文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,並將其封裝到一個List中返回
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
1.在這個語句中會調用自己的一個:SpringFactoriesLoader.loadFactoryNames()
這個方法,從類路徑下得到一個資源
2.在這個SpringFactoriesLoader
類中的方法中,會找到META-INF/spring.factories
這個資源文件。而這個文件中全是鍵值對結構
public abstract class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
3.那麼掃描到的這些文件作用:是把這個文件的urls拿到之後並把這些urls每一個遍歷,最終把這些文件整成一個properties對象。
4.最後通過開始的import
標籤將spring.factories這個文件找到配置所有EnableAutoConfiguration的值加入到Spring容器中,而每一個xxxAutoConfiguration
類都是容器中的一個組件(90多個左右),並都加入到容器中。
加入到容器中之後的作用就是用它們來做自動配置,這就是Springboot自動配置之源,也就是自動配置的開始,只有這些自動配置類進入到容器中以後,接下來這個自動配置類纔開始進行啓動
3.自動配置生效
現在我們都能找到相應的自動配置xxxAutoConfiguration
類,然後Springboot在某些條件之下才會生效的,這些條件的限制在Spring Boot中以註解的形式體現,常見的條件註解有如下幾項:
@ConditionalOnBean:當容器裏有指定的bean的條件下。
@ConditionalOnMissingBean:當容器裏不存在指定bean的條件下。
@ConditionalOnClass:當類路徑下有指定類的條件下。(常用)
@ConditionalOnMissingClass:當類路徑下不存在指定類的條件下(常用)
@ConditionalOnProperty:指定的屬性是否有指定的值
3.1 我們以其中的RedisAutoConfiguration
類爲例
@Configuration//表示這是一個配置類,類似於以前編寫的配置文件一樣,也可以給容器中添加組件
@ConditionalOnClass(RedisOperations.class)//根據pom裏是否引入了相應的jar包,如果沒有就不會啓動該配置類
@EnableConfigurationProperties(RedisProperties.class)//開啓配置屬性,會去找相應的配置文件類,導入這個已經綁定了屬性的bean到spring容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//會檢測容器中是否有redisTemplate類的對象,如果沒有則生成一個。
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean//會檢測容器中是否有TestService類的對象,如果沒有則生成一個。
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
1.@EnableConfigurationProperties(RedisProperties.class)
這個註解可以找到相應的配置類,導入這個已經綁定了屬性的bean到spring容器中。我們進入這個配置類中查看一些具體的信息
這裏我們就截取一部分,他會自動幫我們配置一些如:端口=6379之類的信息
//這裏我們就截取一部分,他會自動幫我們配置一些如:端口=6379之類的信息
@ConfigurationProperties(prefix = "spring.redis")//根據配置文件的前綴與相應的值進行綁定
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:[email protected]:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
我們就可以得到一個具有一些屬性的而且實例化好了的類。
4.總結
最後我們總結一下:
1.Spring Boot啓動的時候會通過@EnableAutoConfiguration
註解,匹配到@Import(AutoConfigurationImportSelector.class)
2.然後再AutoConfigurationImportSelector.class
找到META-INF/spring.factories
配置文件中的所有自動配置類,並對其進行加載
3.而這些自動配置類都是以AutoConfiguration結尾來命名的,它實際上就是一個JavaConfig形式的Spring容器配置類
4.它能通過以Properties
結尾命名的類中取得在全局配置文件中配置的屬性如:server.port
,而XxxxProperties類是通過@ConfigurationProperties
註解與全局配置文件中對應的屬性進行綁定的