導圖梳理springboot手動、自動裝配,讓springboot不再難懂

  • 2019 年 10 月 5 日
  • 筆記

思維導圖梳理

(基本概念)

(裝配方式)

什麼是springboot

在學springboot之前,你必須有spring、spring mvc基礎,springboot的誕生其實就是用來簡化新Spring應用的初始搭建以及開發過程,該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。

它集成了大量常用的第三方庫配置(例如JDBC, Mongodb, Redis, Mail,rabbitmq等等),所以在Spring Boot應用中這些第三方庫幾乎可以零配置的開箱即用(out-of-the-box),大部分的Spring Boot應用都只需要非常少量的配置程式碼,開發者能夠更加專註於業務邏輯。

也就是說,以前集成ssm框架需要一大堆的xml配置文件,效率底下,而使用了springboot之後,很多時候我們不需要寫任何配置了,有時候直接通過@EnableXXX就能開啟某個模組的功能。

現在問題來了,你知道@EnableXXX是什麼原理嗎?

mvc、boot、cloud

這裡直接引用網友的總結給大家介紹一下:

Spring 是一個「引擎」;springmvc是框架,web項目中實際運行的程式碼;spring boot只是一個配置工具,整合工具,輔助工具,是一套快速開發整合包。

Spring Boot :J2EE一站式解決方案 Spring Cloud :分散式整體解決方案

約定大於配置的體現

在於減少軟體開發人員所需要做出的決定的數量,從而獲得簡單的好處,而又不失去其中的靈活性。

1、Spring Boot默認提供靜態資源目錄位置需置於classpath下,目錄名需符合如下規則:/static /public /resources /META-INF/resources 優先順序:META/resources > resources > static > public

2、spring boot默認的配置文件必須是,也只能是application或application-xxx命名的yml文件或者properties文件,我推薦盡量使用yml文件~ 3、application.yml中默認屬性:a、資料庫連接資訊必須是以spring: datasource: 為前綴,如: 

spring:    datasource:      driverClassName: com.mysql.jdbc.Driver      url: jdbc:mysql://localhost:3306/demo      username: root      password: root

b、多環境配置。該屬性可以根據運行環境自動讀取不同的配置文件。例如將該屬性定義為dev的話,Spring Boot會額外從 application-dev.yml 文件中讀取該環境的配置。

spring:    profiles.active: dev

c、修改埠號、請求路徑

server:    port: 8080    context-path: /demo

4、starter啟動器,開箱即用的Starter依賴讓springboot可以實現零配置即可自動完成框架的整合。

  • spring-boot-starter-web
    • 嵌入tomcat和web開發需要servlet與jsp支援
  • spring-boot-starter-data-jpa
    • 資料庫支援
  • spring-boot-starter-data-redis
    • redis資料庫支援
  • spring-boot-starter-data-solr
    • solr支援
  • mybatis-spring-boot-starter
    • 第三方的mybatis集成starter

接下來我們來分析一下springboot注入bean有多少種方式。

手動裝配

在學習springboot中,我喜歡把總結springboot的一些特性,以及使用springboot的一些規律,比如:在springboot載入bean的過程我分為了

  • 手動裝配
  • 自動裝配

兩種方式,而手動裝配又分為了

  • 模式註解裝配
  • @Enable模組裝配
  • 條件裝配

3種方式,接下來我們來一一探討每種。

首先來看下手動裝配:

1、模式註解裝配

其實就是使用@Component註解,或者@Component註解的拓展,比如@Controller、@Service、Repository、@Configruation等,

這也是我們最常用的一種方式,直接通過spring mvc的註解把組件bean注入到spring容器中。

2、@Enable模組裝配

  • 基於介面驅動實現

當我們需要開啟springboot項目的快取功能時候,我們直接打開@EnableCaching註解就可以注入Caching 模組,這時候我們就可以開心使用@Cacheable、@CacheEvict等註解,這是怎麼做到的?

其實你打開@EnableCaching的源碼你就能看到:

@Target({ElementType.TYPE})  @Retention(RetentionPolicy.RUNTIME)  @Documented  @Import({CachingConfigurationSelector.class})  public @interface EnableCaching {      boolean proxyTargetClass() default false;        AdviceMode mode() default AdviceMode.PROXY;        int order() default 2147483647;  }

上面最重要的一句程式碼就是@Import({CachingConfigurationSelector.class}),你會發現,其實使用@EnableCaching,就是為了導入CachingConfigurationSelector.class這配置類。

而這個CachingConfigurationSelector,其實實現了ImportSelector介面,ImportSelector介面是spring中導入外部配置的核心介面,只有一個方法selectImports,其實就是根據EnableCaching的元數據屬性(proxyTargetClass、mode、order),選擇出需要轉配的Configuration。

public interface ImportSelector {      String[] selectImports(AnnotationMetadata var1);  }

總結其實是這樣子,@EnableCaching其實就是根據元數據屬性然後選擇性條件判斷注入需要的配置,比較靈活。

  • 基於註解驅動實現

然後我們來看另一種沒有元數據屬性的@EnableWebMvc。

@Retention(RetentionPolicy.RUNTIME)  @Target({ElementType.TYPE})  @Documented  @Import({DelegatingWebMvcConfiguration.class})  public @interface EnableWebMvc {  }

可以很直觀看到,其實@EnableWebMvc其實就是為了導入DelegatingWebMvcConfiguration配置類,某種程度上,可以認為@EnableWebMvc其實和@Import({DelegatingWebMvcConfiguration.class})是對等的,只是起了一個有意義的名字而已。

所以我們總結一下@EnableXXX模組注入,基於介面驅動實現是實現ImportSelector介面,通過註解參數選擇需要導入的配置,而基於註解驅動實現其實就是@Import的派生註解,直接導入某個配置類。

思維導圖總結如下:

3、條件裝配

所謂條件裝配,其實是Bean裝配的前置條件,我們先來看一下例子:

  • @ConditionalOnBean
  • 僅僅在當前上下文中存在某個對象時,才會實例化一個Bean
  • @ConditionalOnExpression
  • 當表達式為true的時候,才會實例化一個Bean
  • @ConditionalOnMissingClass
  • 某個class類路徑上不存在的時候,才會實例化一個Bean
  • @ConditionalOnNotWebApplication
  • 不是web應用

這就是條件裝配,當這些條件註解放在某個bean上面的時候,只有滿足了條件才能注入bean,這也是為什麼springboot能這麼智慧,知道哪些模組需要開啟,哪些不需要,比如當你導入Freemaker的jar包之後,就自動幫你載入Freemaker的的相關配置,其實你看下程式碼:

@Configuration  @ConditionalOnClass({freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class})  @EnableConfigurationProperties({FreeMarkerProperties.class})  @Import({FreeMarkerServletWebConfiguration.class, FreeMarkerReactiveWebConfiguration.class, FreeMarkerNonWebConfiguration.class})  public class FreeMarkerAutoConfiguration {      ...  }

這些springboot的自動配置類上面一般是不是都有@ConditionalOnClass註解,這裡是說當發現項目有freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class這兩個Class存在時候,我就載入這個FreeMarkerAutoConfiguration,什麼時候才會存在這兩個Class?當我們導入jar包時候:

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-freemarker</artifactId>  </dependency>

所以,當我們沒有導入相關jar包時候,我們不用擔心springboot會自動開啟某些功能,而是會智慧判斷哪些需要開啟,哪些需要跳過。

我們打開@ConditionalOnClass的源碼,發現其實是@Conditional拓展出來的註解。

@Target({ElementType.TYPE, ElementType.METHOD})  @Retention(RetentionPolicy.RUNTIME)  @Documented  @Conditional({OnClassCondition.class})  public @interface ConditionalOnClass {      Class<?>[] value() default {};        String[] name() default {};  }

實現邏輯如下:OnClassCondition.class實現Condition介面,並實現matches()方法,如果matches方法返回true,那麼帶有@Conditional註解的bean就會裝載,false就不會裝載。

思維導圖總結如下:

自動裝配

ok,剛才我們已經說了很多關於手動裝配部分的東西,現在我們來看下自動裝配,其實很多時候自動裝配就是手動裝配的綜合運用,只不過在轉配bean或配置類時候,我們不在需要使用@EnableXXX來導入功能,而是通過自動注入方式。

這時候自動注入的條件判斷(@Conditional)就顯得非常重要了。

我們再用剛才說的Freemaker作為例子,springboot集成freemaker非常簡單,只需要導入starter的jar包就會自動實現注入,這個自動集成就是FreeMarkerAutoConfiguration這裡配置的。

這裡有個問題,你知道為什麼springboot會自動去判斷和載入FreeMarkerAutoConfiguration這個配置類嗎?我沒有寫類似的@EnableFreemaker,那項目怎麼識別的。

其實如果你看過springboot的源碼,你就會發現:

  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;  }

上面的意思是去掃描項目下所有的META-INF/spring.factories文件,然後把EnableAutoConfiguration.class作為key找出對應的值,這個值是個List。那麼我們來看下其中一個spring.factories長什麼樣子的。

  • spring-boot-autoconfigure/2.1.2.RELEASE/spring-boot-autoconfigure-2.1.2.RELEASE.jar!/META-INF/spring.factories

可以看到EnableAutoConfiguration作為key有很多個值,比如RabbitMq的自動配置類等,而你認證點看,就能找到FreeMarkerAutoConfiguration這配置類了。

所以情況是這個的,當springboot項目啟動時候,項目會去載入所有的spring.factories文件,然後在EnableAutoConfiguration後面的所有配置類其實都是可以實現自動裝配的配置,至於需不需要裝配,就需要條件裝配來判定是否滿足特定的條件了。

有了這點基礎之後,我們就可以自己去寫自動裝配了。

第一步、編寫需要自動裝載的配置類。

說明:@Configuration表示是個配置類 @ConditionalOnSystemProperty表示需要滿足當前系統是win10系統

@Configuration  @ConditionalOnSystemProperty(value = "Windows 10")  public class SayHelloWorldAutoConfiguration {        @Bean      SayHelloWorld autoSayHelloWorld() {          System.out.println("here to !!auto!!loading bean autoSayHelloWorld!");          return new SayHelloWorld();      }  }

第二步、在resources目錄下新建META-INF文件夾,編寫spring.factories。

# Auto Configure 自動裝配自定義的配置  org.springframework.boot.autoconfigure.EnableAutoConfiguration=  com.other.configuration.SayHelloWorldAutoConfiguration

啟動springboot之後就會自動載入這個配置類,於是,我們就注入了SayHelloWorld這個業務bean,項目中就可以直接注入使用啦~

有人說,這和直接寫個@Configruation有啥區別,區別在於@Configruation的配置必須寫在Spring能掃描到的目錄下,而自動裝配不需要。

思維導圖總結如下:

– 精選文章 –