SpringBoot自動裝配詳解---帶源碼

前言

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註解與全局配置文件中對應的屬性進行綁定的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章