爲何咱們要用 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放在項目的最高級中。
能夠從圖中看出 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存在,這該註解修飾的代碼塊不執行。 居然都看到最後了,給小編點個關注吧,小編還會持續更新的,只收藏不點關注的都是在耍流氓!