Spring Boot源碼(一) - 分析總覽

    Spring Boot當前版本爲2.2.0,之前一直對Spring Boot的各個模塊比較模糊,很難理解自動裝配等註解與容器啓動的關係等。但是後面梳理完了之後發現,Spring Boot只是在Spring的基礎上做了比較多的封裝和擴展,個人理解。 還是先看一下,

一、Spring Boot的特性

 

1、獨立運行Spring項目

    之前的時候Spring只是作爲一個服務的一部分,或者說是容器。特別是之前的web項目是通過監聽Servlet服務器規範的ContextLoadeoaderListener(或者ContextLoaderPlugin)等方式,監聽服務器啓動後啓動Ioc容器。現在不論是jar還是war都可以使用java -jar xxx.jar方式啓動項目。

2、嵌入式Servlet服務器(如Tomcat)

    獨立運行Spring項目,特別是web項目的情況下,使用內嵌的Servlet服務器(如Tomcat提供的內嵌版本)。首先是啓動Spring項目,然後在AbstractApplicationContext的refresh方法,在執行模板方法的onRefresh的擴展部分,調用createWebServer方法以創建Servlet服務器,是Spring與Tomcat角色的轉變。

3、提供starter簡化maven配置

    Spring Boot提供了一系列的starter的maven依賴,特別是spring-boot-starter-parent。只需要一個基礎的依賴就能引入大部分的自動裝配的模塊(功能)。

4、自動裝配

    自動裝配使用 @ImportImportBeanDefinitionRegistrar回調接口的方式實現,那麼發生的時機是 SpringApplication.run執行時,會執行AbstractApplicationContext的refresh方法時執行。具體自動裝配的執行時機是在Spring源碼-ImportSelector實現分析或參見SpringIoc源碼(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)

5、準生產的應用監控

    Spring Boot在原有Spring的Environment的基礎上進行擴展出Actuator模塊,可以完成內存等的常規運營監控,特別適合小公司能快速搭建監控。

6、無代碼生成和xml配置

    使用條件註解(@Conditional以及其擴展註解)和Java配置組合,不需要任何的xml配置就能實現所有配置。

 

    在梳理完Spring的源碼後發現,其強大之處在於Ioc容器、ApplicationContext的生命週期、回調接口(BeanPostProcessor等),以及每個Bean的生命週期回調。生命週期也就意味着模板方法模式定義的回調,同樣Spring Boot也定義了自己的回調組件。

SpringApplicationRunListener、(貫穿了Spring Boot的整個啓動過程)

ApplicationContextInitializer(只是AbstractApplicationContext的準備階段)

ApplicationRunner、CommandLineRunner(環境參數相關回調,只是前者參數是被封裝過的,後者是main方法的原參數)

 

二、SpringApplication結構

1、主要字段

    // @SpringBootApplication註解標註的類
    private Set<Class<?>> primarySources;

    // 註冊成Bean的全限定名,默認爲空後續會將primarySources添加進去
    private Set<String> sources = new LinkedHashSet<>();

    // main方法類,一般與primarySources相同
    private Class<?> mainApplicationClass;

    // 啓動Banner圖,默認會啓動SpringBootBanner
    private Banner.Mode bannerMode = Banner.Mode.CONSOLE;

    // 是否大於啓動的StopWatch的信息
    private boolean logStartupInfo = true;

    // 是否大於main方法啓動的args參數
    private boolean addCommandLineProperties = true;

    // 是否爲ApplicationContext添加ConversionService
    private boolean addConversionService = true;

    // Banner圖
    private Banner banner;

    // 類加載器,用於在ApplicationContext還沒有啓動時,加載類
    // ApplicationContextInitializer等類型的類
    private ResourceLoader resourceLoader;

    // Bean的名稱生成器,否則走默認的名稱生成方式(第一個字母小寫的駝峯,多個大寫連着的除外)
    private BeanNameGenerator beanNameGenerator;

    // Spring的Environment
    private ConfigurableEnvironment environment;

    // ApplicationContext的子類類型
    private Class<? extends ConfigurableApplicationContext> applicationContextClass;

    // Application類型,用於確認上面生成的applicationContextClass類型
    private WebApplicationType webApplicationType;

    // 配置headless的值
    private boolean headless = true;

    // 是否註冊鉤子
    private boolean registerShutdownHook = true;

    // 初始化器(後面會詳細分析)
    private List<ApplicationContextInitializer<?>> initializers;

    // 監聽(後面會詳細分析)
    private List<ApplicationListener<?>> listeners;

    // 默認配置,會疊加@ConfigurationProperties
    private Map<String, Object> defaultProperties;

    // Spring Profiles
    private Set<String> additionalProfiles = new HashSet<>();

    // 是否允許BeanDefinition覆蓋
    private boolean allowBeanDefinitionOverriding;

    // 是否自定義Environment,因爲Spring的ApplicationContext在refresh時會初始化
    private boolean isCustomEnvironment = false;

    // 是否加載LazyInitializationBeanFactoryPostProcessor類型的BeanPostProcessor
    private boolean lazyInitialization = false;

2、構造器和run方法

// 1、類加載器、main方法類的Class
SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources);

// 2、main方法類的Class
SpringApplication(Class<?>... primarySources);

    首先構造器可以傳入類加載器,一般都不傳入,則在啓動時候使用當前線程進行獲取類加載器,後續主要用於加載ApplicationContextInitializer和ApplicationListener等類型的類。

    primarySources爲@SpringBootApplication註解標記的類,主要用於後續需要使用該類的包(作爲基準包)進行@Conponent的掃描。以及判斷當前類是否爲@Conponent,是的話則註冊爲Bean。

// main方法類的Class、main方法啓動參數
run(Class<?> primarySource, String... args);

run(Class<?>[] primarySources, String[] args);

run(String... args);

    如果構造器沒有傳入primarySource參數,則在run方法時還可以傳入。但是run方法執行時,一定需要傳入args,很多地方會進行使用,並且會進行封裝成ApplicationArguments。若不需要使用自動掃描基礎包下的@Component的功能,則可以調用方法(不建議):

public static void main(String[] args) throws Exception {
    SpringApplication.run(new Class<?>[0], args);
}

  

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