本文來自網易雲社區
上一篇介紹了起步依賴,這篇我們先來看下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
本文來自網易實踐者社區,經作者金港生授權發布。
