帶你一行一行分析SpringBoot原碼解析


一.說明

 1.本次源碼解析是基於2.3.3.RELEASE版本的

 2.本文主要分析Spring的自動配置

二.原碼分析

1.創建一個普通的springboot項目如下:

 

 只有一個配置文件和一個啟動類。

配置文件中只配了一個redis,配置其他組件都行,這里以redis為例展開說明自動注入。

2.打開啟動類

對於springboot來說,最強大的地方就是沒有復雜的配置文件,創建springboot后只有一個啟動類,那就從啟動類入手,Ctrl + 左鍵 點擊

@SpringBootApplication 注解,進入如下所示:
@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 {

  可以看到,

@SpringBootApplication注解上面又有七個注解,其實,這種包含了很多注解的注解就是組合注解。前四個是元注解(在JDK 1.5中提供了4個標准的用來對注解類型進行注解的注解類,我們稱之為 meta-annotation(元注解)),我們先分析前四個注解:

  • @Target : 描述注解的使用范圍,括號里有個ElementType.TYPE,點進去,如下:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

Target注解用來說明那些被它所注解的注解類可修飾的對象范圍:注解可以用於修飾 packages、types(類、接口、枚舉、注解類)、類成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數),在定義注解類時使用了@Target 能夠更加清晰的知道它能夠被用來修飾哪些對象,它的取值范圍定義在ElementType 枚舉中。

  • @Retention 同理,點擊去 RetentionPolicy類,它的主要作用是:用來限定那些被它所注解的注解類在注解到其他類上以后,可被保留到何時,一共有三種策略,定義在RetentionPolicy枚舉中。

    public enum RetentionPolicy {
     
        SOURCE,    // 源文件保留
        CLASS,       // 編譯期保留,默認值
        RUNTIME   // 運行期保留,可通過反射去獲取注解信息
  •  @Documented ,它的作用是:描述在使用 javadoc 工具為類生成幫助文檔時是否要保留其注解信息
  • @Inherited,這是個比較重要的注解,它表示注解會被子類自動繼承。

接下來就剩三個注解了,其中@EnableAutoConfiguration是最核心的注解,我們放到最后面說,先說其他兩個注解:

  •     @SpringBootConfiguration : 它的作用就是:繼承自@Configuration,二者功能也一致,標注當前類是配置類, 並會將當前類內聲明的一個或多個以@Bean注解標記的方法的實例納入到spring容器中,並且實例名就是方法名。可以點擊去看下,如下:其實,@Configuration和 @SpringBootConfiguration 是具有相同功能的。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • @ComponentScan,也是比較復雜的,點進去,如下圖:

  這也就是為什么,所有的代碼都要放在啟動類所在的包及子包里面。

接下來就是最重要的注解了,@EnableAutoConfiguration ,先點進去,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited   
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

  前四個注解就不說了,

    @AutoConfigurationPackage的作用是導入自定義的類的,

    @Import(AutoConfigurationImportSelector.class)是導入框架本身的一些類的

  在這里,這兩個注解僅僅是找到需要導入的類,並沒有實例化,實例化依然需要spring容器去做。

先看@AutoConfigurationPackage,點進去,如下圖:

 有個Registrar,點進去,如下圖:

 從方法名可以看出是個注冊的方法,打上斷點,啟動,當代碼停住后,Alt + F8,查看new PackageImports(metadata).getPackageNames().toArray(new String[0])的值,如下圖:

 從上圖可以看出,這個方法其實就是就是把自定義包下的類掃描並注冊到容器中。

再看@Import(AutoConfigurationImportSelector.class),它是注入框架本身使用的和自動配置相關的類,點擊去,找到getCandidateConfigurations方法。

 

 

 注釋的意思的是;找到可能的自動配置的類名,進入loadFactoryNames方法,

 繼續進入loadSpringFactories,

 圖中有個FACTORIES_RESOURCE_LOCATION,點進去,

 原來是一個文件,也就是說,springboot框架本省要導入的類就在這個文件中,那么這個spring.factories在哪呢?

接下來打開pom文件,找到spring-boot-starter-web,點進去,

 找到spring-boot-starter,點進去

 就能發現有一個spring-boot-autoconfigure,表示自動配置,如圖。

打開pom文件引入的包,在工程窗口,如下圖:

 找到:spring-boot-autoconfigure,如下圖:

 發現有一個spring.properties文件,打開:

 發現,類似於redis這種組件所對應的類就在這個文件中。

到此為止,springboot只是將可能用到的類加載進來了,但是僅僅知識加載了類名,怎么能根據我們在yml文件中的配置來使用呢?也就是說,springboot怎么能知道我們要使用哪些類,不使用哪些類呢,比如我們現在要是用redis,首先我們需要在yml文件中配置redis的連接信息,如下圖:

 

 

 想到這里,我們就應該想到,springboo肯定是通過加載這個yml文件開讀取的,接下來跟原碼:

打開啟動類,進入run方法,只要是run方法,就一直往下走,直到org.springframework.context.ConfigurableApplicationContext這個方法,如下圖:

 

 

 進入:prepareEnvironment方法這個方法表示環境的准備,如下圖;

 

 進入environmentPrepared,表示添加監聽:

 

 進入environmentPrepared方法,表示初始化:繼續往下走,方法順序為

multicastEvent->

multicastEvent->

multicastEvent->

invokeListener(listener, event))->

doInvokeListener——>如圖:

 

 進入onApplicationEvent接口的配置文件實現類  ConfigFileApplicationListener

 

 進入實現方法:

 

 再按下順序往下走:

onApplicationEnvironmentPreparedEvent ->

postProcessEnvironment->如圖:

再進入postProcessEnvironment的實現類:ConfigFileApplicationListener

 

 實現方法如下:

 

 再進入addPropertySources方法:

 

 在進入load方法:

 

 再進入load方法:

 

 再進入load方法:

 

 點擊getFileExtensions()進入接口,

 

 這個接口有兩個實現類,分別是properties和yml,太熟悉了,這不就是配置文件嗎,這個接口其實就是配置文件的擴展名,

重新進入load方法:

 

 進入loadForFileExtension方法:

 

 

 再進入load方法:

 

 再進入loadDocuments方法:

 

 再進入load接口方法:

 

 發現又有兩個實現類,進入yml的實現類:

 

 ····················終於完了,這是最后一個方法了,在犯法最后一行打上斷點:啟動:

 

 把配置文件中的所有信息都加載進來了。

但是。。。,這也只是把配置文件加載進來了,那么redis怎么起作用呢?

打開前面說的spring.properties文件找到  RedisAutoConfiguration類,進入這個類,如下

 

 要使redis起作用,圖中的四個注解不可少,進入注解括號中的RedisProperties類:

 

 發現,又是熟悉的感覺,這不就對飲配置文件中的屬性嗎?

再看RedisTemplate<Object, Object> template = new RedisTemplate<>(); 這行代碼,直接就new了一個對象,這就是創建了一個RedisTemplate,因此,我們就可以使用RedisTemplate來操作redis了。

所以springboot,也就是通過這種方式進行類的自動裝配的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM