本文來自網易雲社區
上一篇介紹了起步依賴,這篇我們先來看下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就會被加載進去完成創建和依賴。
網易雲新用戶大禮包:https://www.163yun.com/gift
本文來自網易實踐者社區,經作者金港生授權發布。