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

本文來自網易雲社區java


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

入口類

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

<packaging>jar</packaging>

SpringBoot默認的打包方式爲jar,而且內嵌web容器。所以咱們能夠用運行jar包的方式啓動一個web程序:spring

java -jar xxx.jar

linux服務器上能夠用下面命令讓服務常駐:服務器

nohup java -jar xxx.jar &

咱們知道jar包方式運行須要main方法,SpringBoot已爲咱們自動生成,這個類即是項目啓動入口。app

個人項目名是blog-demo,對應生成的main方法在BlogDemoApplication.java,其代碼以下:框架

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

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

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

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

 

本文來自網易實踐者社區,經做者金港生受權發佈。

相關文章
相關標籤/搜索