Spring Boot 2.0從出來(lái)到現(xiàn)在已經(jīng)很久了蝗碎,也使用一陣子猛蔽,每次構(gòu)建一個(gè)內(nèi)嵌tomcat的jar的web項(xiàng)目剥悟,都很自然的java -jar XXXX.jar
就跑起來(lái)了。沒(méi)有深入的去研究過(guò)這條命令后面的執(zhí)行過(guò)程曼库,今天就整了這篇博文区岗,做個(gè)記錄。來(lái)分析分析Spring Boot給我們帶來(lái)了什么便捷配置毁枯。
1. Spring Boot Web Jar包結(jié)構(gòu)拆解
1.1. 目標(biāo)項(xiàng)目結(jié)構(gòu)
為了便于調(diào)試慈缔,構(gòu)建一個(gè)極簡(jiǎn)的基于Spring Boot 2.x的Web項(xiàng)目:
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
啟動(dòng)類(lèi)
@SpringBootApplication
public class UnifiedPlatformApplication {
public static void main(String[] args) {
SpringApplication.run(UnifiedPlatformApplication.class, args);
}
}
Maven 編譯
mvn compiler:compile
在項(xiàng)目目錄下將生成:unified-platform-0.0.1-SNAPSHOT.jar
1.2. Jar包展示
- 用解壓軟件,打開(kāi)jar文件后众,目錄結(jié)構(gòu)如下:
- 主要文件和目錄
目錄 | 內(nèi)容 |
---|---|
BOOT-INF\classes | 當(dāng)前項(xiàng)目編譯后的class文件 |
BOOT-INF\lib | 當(dāng)前項(xiàng)目依賴jar包 |
META-INF | MANIFEST.MF文件 |
org\springframework\boot\loader | Spring Boot運(yùn)行輔助類(lèi) |
- 重點(diǎn)文件MANIFEST.MF
Manifest-Version: 1.0
Implementation-Title: unified-platform
Implementation-Version: 0.0.1-SNAPSHOT
Built-By: fuhao-pc
Implementation-Vendor-Id: com.supcon
Spring-Boot-Version: 2.1.2.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.supcon.UnifiedPlatformApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_144
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo
ot-starter-parent/unified-platform
2. Java -jar 執(zhí)行過(guò)程解析
根據(jù)可運(yùn)行Jar執(zhí)行機(jī)制胀糜,存在MANIFEST.MF的情況下,JVM會(huì)解析該文件內(nèi)部的Main-Class的值蒂誉,作為整個(gè)jar程序運(yùn)行的入口教藻。
Main-Class: org.springframework.boot.loader.JarLauncher
看看解壓出來(lái)的文件夾,都是class右锨,二話不說(shuō)那就反編譯唄括堤,其實(shí)直接丟IDEA就行了。
JarLauncher.class
:
public class JarLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
protected boolean isNestedArchive(Entry entry) {
return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
}
//入口方法绍移,構(gòu)建JarLauncher對(duì)象悄窃,執(zhí)行父類(lèi)的ExecutableArchiveLauncher的父類(lèi)Launcher的Launch方法。
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
}
Launcher.class
:
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
this.launch(args, this.getMainClass(), classLoader);
}
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(mainClass, args, classLoader).run();
}
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
MainMethodRunner.class
:
public class MainMethodRunner {
private final String mainClassName;
private final String[] args;
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = args != null ? (String[])args.clone() : null;
}
public void run() throws Exception {
//反射執(zhí)行對(duì)象mainClass類(lèi)的main方法
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke((Object)null, this.args);
}
}
所以關(guān)鍵點(diǎn)就在mainClass的獲取了:
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
} else {
return mainClass;
}
}
跟蹤到這里蹂窖,最后還是到了我們自己的代碼 Start-Class: com.supcon.UnifiedPlatformApplication
轧抗。繞了一大圈,才到SpringBoot啟動(dòng)類(lèi)瞬测,整個(gè)過(guò)程貌似也就是獲取了下當(dāng)前線程的類(lèi)加載器横媚,不知道這么搞的初衷是啥,沒(méi)有進(jìn)一步深究月趟。那么接下來(lái)灯蝴,就得分析Spring Boot的啟動(dòng)過(guò)程了,別的不說(shuō)孝宗,直接代碼跟蹤穷躁。
3. Spring Boot自動(dòng)配置過(guò)程源碼解析
3.1. 官方約定
Spring Boot 文檔顯示,在項(xiàng)目Jar下META-INFO下文件spring.factories內(nèi)添加org.springframework.boot.autoconfigure.EnableAutoConfiguration=***因妇,即可以按照約定的方式進(jìn)行Bean的注冊(cè)和初始化
3.2. 源碼調(diào)試跟蹤
項(xiàng)目啟動(dòng)類(lèi)
public static void main(String[] args) {
SpringApplication.run(UnifiedPlatformApplication.class, args);
}
-
SpringApplication.java
構(gòu)建Spring Context问潭,然后進(jìn)行上下文刷新(構(gòu)建Bean)
public ConfigurableApplicationContext run(String... args) {
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//...非核心代碼...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//調(diào)試斷點(diǎn)
//Spring 容器上下文準(zhǔn)備好后猿诸,進(jìn)行相關(guān)工廠Bean等的注冊(cè)
refreshContext(context);
//**************************
afterRefresh(context, applicationArguments);
//...非核心代碼...
}
//...非核心代碼...
return context;
}
-
AbstractApplicationContext.java
睦授,執(zhí)行各種處理器两芳。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//調(diào)試斷點(diǎn)
//BeanFactory后置處理器
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
//************************************
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
//...非核心代碼...
}
}
-
PostProcessorRegistrationDelegate.java
循環(huán)調(diào)用實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor類(lèi)的處理器
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
//調(diào)試斷點(diǎn)
//循環(huán)調(diào)用實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor類(lèi)的處理器
//當(dāng)然也包括本文重點(diǎn)的ConfigurationClassPostProcessor
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
ConfigurationClassPostProcessor.java
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
//...非核心代碼...
this.registriesPostProcessed.add(registryId);
//調(diào)試斷點(diǎn)
//處理配置類(lèi)Bean的注冊(cè)信息
processConfigBeanDefinitions(registry);
}
@import注解處理
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//...非核心代碼...
//調(diào)試斷點(diǎn)
//處理注解導(dǎo)入類(lèi)
this.deferredImportSelectorHandler.process();
}
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
//調(diào)試斷點(diǎn)
//處理注解導(dǎo)入類(lèi)
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
public Iterable<Group.Entry> getImports() {
//調(diào)試斷點(diǎn)
//定位到 @EnableAutoConfiguration的注解
//@Import(AutoConfigurationImportSelector.class)
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
這個(gè)@EnableAutoConfiguration注解正好是啟動(dòng)類(lèi)繼承來(lái)的
@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 {}
EnableAutoConfiguration.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//...非核心代碼...
}
AutoConfigurationImportSelector.java
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
//調(diào)試斷點(diǎn)
//獲取自動(dòng)配置實(shí)體
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
-
SpringFactoriesLoader.java
摔寨,終于到了核心類(lèi)去枷,就是它去加載了需要自動(dòng)加載的類(lèi)
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//調(diào)試斷點(diǎn)
//通過(guò)SpringFactoriesLoader 去META-INF/spring.factories.
//加載 org.springframework.boot.autoconfigure.EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
//...非核心代碼...
return configurations;
}
接下去就是通過(guò)Spring Bean的導(dǎo)入機(jī)制,遍歷各種AutoConfiguration類(lèi)是复,進(jìn)行上下文Bean注冊(cè)以及后續(xù)的處理過(guò)程删顶。這里有必要列下常用注解的意思:
注解 | 作用 |
---|---|
@Conditional | 作用(判斷是否滿足當(dāng)前指定條件) |
@ConditionalOnJava | 系統(tǒng)的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 滿足SpEL表達(dá)式指定 |
@ConditionalOnClass | 系統(tǒng)中有指定的類(lèi) |
@ConditionalOnMissingClass | 系統(tǒng)中沒(méi)有指定的類(lèi) |
@ConditionalOnSingleCandidate | 容器中只有一個(gè)指定的Bean,或者這個(gè)Bean是首選Bean |
@ConditionalOnProperty | 系統(tǒng)中指定的屬性是否有指定的值 |
@ConditionalOnResource | 類(lèi)路徑下是否存在指定資源文件 |
@ConditionalOnWebApplication | 當(dāng)前是web環(huán)境 |
@ConditionalOnNotWebApplication | 當(dāng)前不是web環(huán)境 |
@ConditionalOnJndi | JNDI存在指定項(xiàng) |
3. Spring MVC 在哪里初始化
心細(xì)如我
的讀者可能會(huì)發(fā)現(xiàn)淑廊,貌似對(duì)于Web項(xiàng)目還少了些什么逗余,怎么沒(méi)有看到MVC的重要類(lèi):DispatcherServlet。莫急季惩,其實(shí)看看spring.factories文件會(huì)發(fā)現(xiàn):
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
就是這些AutoConfiguration類(lèi)录粱,進(jìn)行了對(duì)MVC各個(gè)組件的初始化。比如:
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
//.....
}
具體過(guò)程這里就不在研究了画拾,話題太大了啥繁。