「深刻淺出SpringBoot」瞭解Spring Boot自動配置原理

前言

爲何咱們要用 Spring Boot,Spring Boot 最重要的功能是:自動配置。java

爲何說是自動配置?先看@SpringBootApplication註解的源碼。redis

@Target(ElementType.TYPE)spring

@Retention(RetentionPolicy.RUNTIME)sql

@Documented數據庫

@Inheritedapp

@SpringBootConfigurationide

@EnableAutoConfigurationspring-boot

@ComponentScan(excludeFilters = {工具

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),post

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

Spring Boot 的開啓註解是:@SpringBootApplication,從上面的代碼可知,其實它就是由下面三個註解組成的:

@Configuration

@ComponentScan

@EnableAutoConfiguration

上面三個註解,前面兩個都是 Spring 自帶的,和 Spring Boot 無關。而 Spring Boot 最最核心的就是這個 @EnableAutoConfiguration 註解了,它能根據類路徑下的 jar 包和配置動態加載配置和注入bean。

舉個例子,好比我在 lib 下放一個 druid 鏈接池的 jar 包,而後在 application.yml 文件配置 druid 相關的參數,Spring Boot 就可以自動配置全部咱們須要的東西,若是我把 jar 包拿掉或者把參數去掉,那 Spring Boot 就不會自動配置。

這樣咱們就能把許多功能作成公共的自動配置的啓動器(starters),例如 druid 鏈接池就是這麼作的,它提供了針對 Spring Boot 的啓動器:druid-spring-boot-starter。

有了這個自動配置的啓動器,咱們就能很是簡單的使用它,

先添加 jar 包依賴:

< dependency>

< groupId>com.alibaba< /groupId>

< artifactId>druid-spring-boot-starter< /artifactId>

< version>1.1.10< /version>

< /dependency>

再添加相關參數:

spring.datasource.url=

spring.datasource.username=

spring.datasource.password=

……

若是是傳統的項目,咱們要本身手動寫一大堆的配置,並且還不靈活,有了這個啓動器,咱們就能夠作到簡單集成。

因此,這纔是 Spring Boot 的核心,這纔是咱們爲何使用 Spring Boot 的緣由。

SpringBoot自動配置註解原理解析

前面說過@SpringBootApplication三個比較重要的註解:

@SpringBootConfiguration : Spring Boot的配置類,標註在某個類上,表示這是一個Spring Boot的配置類 @EnableAutoConfiguration: 開啓自動配置類,SpringBoot的精華所在。 @ComponentScan包掃描 之前咱們須要配置的東西,Spring Boot幫咱們自動配置;

@EnableAutoConfiguration告訴SpringBoot開啓自動配置功能;這樣自動配置才能生效

咱們先來看看EnableAutoConfiguration註解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {

其中,兩個比較重要的註解:

@AutoConfigurationPackage:自動配置包

@Import: 導入自動配置的組件

1.AutoConfigurationPackage註解

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

@Override

public void registerBeanDefinitions(AnnotationMetadata metadata,

BeanDefinitionRegistry registry) {

register(registry, new PackageImport(metadata).getPackageName());

}

它實際上是註冊了一個Bean的定義。

*new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 同級以及子級 * 的包組件。

以上圖爲例,DemoApplication是和demo包同級,可是demo2這個類是DemoApplication的父級,和example包同級

也就是說,DemoApplication啓動加載的Bean中,並不會加載demo2,這也就是爲何,咱們要把DemoApplication放在項目的最高級中。

  1. Import(AutoConfigurationImportSelector.class)註解: 3

能夠從圖中看出 AutoConfigurationImportSelector 繼承了DeferredImportSelector 繼承了 ImportSelector**

ImportSelector有一個方法爲:selectImports

@Override

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader

.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = getAttributes(annotationMetadata);

List configurations = getCandidateConfigurations(annotationMetadata,

attributes);

configurations = removeDuplicates(configurations);

Set exclusions = getExclusions(annotationMetadata, attributes);

checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = filter(configurations, autoConfigurationMetadata);

fireAutoConfigurationImportEvents(configurations, exclusions);

return StringUtils.toStringArray(configurations);

}

能夠看到第九行,它實際上是去加載 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。這個外部文件,有不少自動配置的類。以下:

如何自定義本身的Bean:

咱們以RedisTemplate爲例:

@Configuration

@ConditionalOnClass(RedisOperations.class)

@EnableConfigurationProperties(RedisProperties.class)

@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })

public class RedisAutoConfiguration {

@Bean

@ConditionalOnMissingBean(name = "redisTemplate")

public RedisTemplate<Object, Object> redisTemplate(

RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {

RedisTemplate<Object, Object> template = new RedisTemplate<>();

template.setConnectionFactory(redisConnectionFactory);

return template;

}

@Bean

@ConditionalOnMissingBean

public StringRedisTemplate stringRedisTemplate(

RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {

StringRedisTemplate template = new StringRedisTemplate();

template.setConnectionFactory(redisConnectionFactory);

return template;

}

}

咱們每次在Spring中使用Redis,都會使用到RedisTemplate這個工具類,可是他默認給咱們返回的這個工具類,可能不是很符合咱們的要求。好比:咱們想要開啓事務,或者想要改變它默認的序列化。

這時候該如何去作呢?

根據前面的分析,只要咱們在容器中放入一個RedisTemplate Bean便可。

@Bean("redisTemplate")

public RedisTemplate<Object, Object> myRedisTemplate(

RedisConnectionFactory redisConnectionFactory) {

RedisTemplate<Object, Object> template = new RedisTemplate<>();

template.setConnectionFactory(redisConnectionFactory);

// 修改序列化爲Jackson

template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());

// 開啓事務

template.setEnableTransactionSupport(true);

return template;

}

咱們本身定義咱們的RedisTemplate模板,修改序列化,開啓事務等操做。

咱們將咱們本身的Bean加入到IoC容器中之後,他就會默認的覆蓋掉原來的RedisTemplate,達到定製的效果。

接下來將在以前的工程的基礎上,觀察在程序的引導啓動過程當中,Spring Boot經過自動配置機制幫咱們作了哪些工做。

How Do

Spring Boot啓動時將自動配置的信息經過DEBUG級別的日誌打印到控制檯。能夠經過設置環境變量(DEBUG)或者程序屬性(--debug)設置程序的日誌輸出級別。 在項目目錄下運行DEBUG=true mvn spring-boot:run啓動應用程序; 在後臺能夠看到DEBUG級別的日誌輸出,在啓動日誌的最後,能夠看到相似AUTO-CONFIGURATION REPORT的字樣。

分析 能夠看到,後臺輸出的自動配置信息特別多,好幾頁屏幕,沒辦法一一分析,在這裏選擇一個postive match和negative match進行分析。

Spring Boot經過配置信息指出:特定配置項被選中的緣由、列出匹配到對應類的配置項(positive match)、不包括某個配置項的緣由(negative match)。如今以DataSourceAutoConfiguration舉例說明:

@ConditionalOnClass 表示對應的類在classpath目錄下存在時,纔會去解析對應的配置文件,對於DataSourceAutoConfiguration來講就是指:只有javax.sql.DataSource和org.springframwork.jdbc.datasource.embedded.EmbeddedDatabaseType類都能存在時,就會配置對應的數據庫資源。 @ConditionalOnMisssingClass表示對應的類在classpath目錄下找不到。 OnClassCondition用於表示匹配的類型(postive or negative) OnClassCondition是最廣泛的瀏覽探測條件,除此以外,Spring Boot也使用別的探測條件,如:OnBeanCondition用於檢測指定bean實例存在與否、OnPropertyCondition用於檢查指定屬性是否存在等等。

符合negative match表明一些配置類(xxxConfiguration之類的),它們雖然存在於classpath目錄,可是修飾它們的註解中依賴的其餘類不存在。導入若是在pom文件中導入spring-boot-autoconfigure包,則GsonAutoConfiguration就會出如今classpath目錄下,可是該配置類被@ConditionalOnClass(Gson.class)修飾,而com.google.gson.Gson類不在classpath目錄。

Configuration @ConditionalOnClass({Gson.class}) public class GsonAutoConfiguration { public GsonAutoConfiguration() { } @Bean @ConditionalOnMissingBean public Gson gson() { return new Gson(); } }

總結 @ConditionalOnClass:該註解的參數對應的類必須存在,不然不解析該註解修飾的配置類; @ConditionalOnMissingBean:該註解表示,若是存在它修飾的類的bean,則不須要再建立這個bean;能夠給該註解傳入參數例如@ConditionOnMissingBean(name = "example"),這個表示若是name爲「example」的bean存在,這該註解修飾的代碼塊不執行。 居然都看到最後了,給小編點個關注吧,小編還會持續更新的,只收藏不點關注的都是在耍流氓!

相關文章
相關標籤/搜索