SpringBoot入門(三)——入口類解析


本文來自網易雲社區


上一篇介紹了起步依賴,這篇我們先來看下SpringBoot項目是如何啟動的。

入口類

再次觀察工程的Maven配置文件,可以看到工程的默認打包方式是jar格式的。

<packaging>jar</packaging>

SpringBoot默認的打包方式為jar,並且內嵌web容器。因此我們可以用運行jar包的方式啟動一個web程序:

java -jar xxx.jar

linux服務器上可以用下面命令讓服務常駐:

nohup java -jar xxx.jar &

我們知道jar包方式運行需要main方法,SpringBoot已為我們自動生成,這個類便是項目啟動入口。

我的項目名是blog-demo,對應生成的main方法在BlogDemoApplication.java,其代碼如下:

@SpringBootApplicationpublic class BlogDemoApplication {    public static void main(String[] args) {
        SpringApplication.run(BlogDemoApplication.class, args);
    }
}

main方法中執行SpringApplication的靜態方法run,並將當前類和啟動參數傳入。

靜態方法中實例化一個SpringApplication,並調用實例的run方法:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {        return new SpringApplication(primarySources).run(args);
    }

先來看下調用的SpringApplication的構造方法:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");    // 這里的primarySources就是我們傳入的入口類
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));    // 推斷應用類型
    this.webApplicationType = deduceWebApplicationType();    // 設置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));    // 設置監聽器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));    // 很有意思的方法,通過異常棧獲取應用入口類
    this.mainApplicationClass = deduceMainApplicationClass();
}

注意我們傳入的啟動類被保存到了primarySources變量中,將作為后續context加載beans時的資源,其他細節不再展開。

接着看實例的run方法:

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */public ConfigurableApplicationContext run(String... args) {    // 計時工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();    // 應用上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();    // 設置系統參數-無圖形化界面
    // 獲取監聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                 applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);        // 創建上下文
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,            new Class[] { ConfigurableApplicationContext.class }, context);        // 上下文前置處理,這里會解析我們傳入的入口類
        prepareContext(context, environment, listeners, applicationArguments,
                       printedBanner);        // 刷新上下文
        refreshContext(context);        // 后置處理
        afterRefresh(context, applicationArguments);
        stopWatch.stop();        if (this.logStartupInfo) {            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }    catch (Throwable ex) {
        handleRunFailure(context, listeners, exceptionReporters, ex);        throw new IllegalStateException(ex);
    }
    listeners.running(context);    return context;
}

通過方法注釋也可以看出來該run方法引發了一系列復雜的內部調用和加載過程,從而創建了一個SpringContext。

在prepareContext方法中會解析我們傳入的入口類,解析其上的注解。下面來看下入口類上的注解。

@SpringBootApplication

入口類上的注解@SpringBootApplication是SpringBoot自動配置的關鍵。其定義如下:

@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 {
    ...
}

說明它是@ComponentScan、@SpringBootConfiguration和@EnableAutoConfiguration三個注解的組合。

@ComponentScan

@ComponentScan是Spring框架原有的注解,在spring-context組件下,用來開啟自動掃描Bean並解析注解注入。

可以用basePackages指定掃描的包,缺省情況下默認掃描被注解類所在的包。SpringBoot項目中一般會將入口類放在頂層目錄,這樣默認就會掃描整個項目。

@SpringBootConfiguration

@SpringBootConfiguration是SpringBoot新增的注解,在spring-boot組件下,定義如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {

}

相當於注解@Configuration,配備了該注解的類就能夠以JavaConfig的方式完成一些配置,可以不再使用XML配置。

所以在入口類內也可以以JavaConfig的方式定義Bean。

@EnableAutoConfiguration

@EnableAutoConfiguration是SpringBoot新增的注解,在spring-boot-autoconfigurate組件下,它是SpringBoot開啟自動配置的關鍵。放到下一節再講。

小結

這一節簡單解析了SpringBoot的入口類,一個由SpringBoot自動生成的java類,雖然只有短短幾行代碼,卻引發了Spring上下文的創建的一系列事件。

首先SpringBoot將入口類傳入作為資源的起點,當解析到入口類的時候發現其上的注解又開啟了自動配置和包掃描,這樣我們自定義的Bean就會被加載進去完成創建和依賴。




相關閱讀:SpringBoot入門(一)——開箱即用

SpringBoot入門(二)——起步依賴

SpringBoot入門(三)——入口類解析

SpringBoot入門(四)——自動配置

SpringBoot入門(五)——自定義配置

 

網易雲新用戶大禮包:https://www.163yun.com/gift

 

本文來自網易實踐者社區,經作者金港生授權發布。



免責聲明!

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



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