1. SpringBoot如何通过jar包启动
1.1. java -jar做了什么?
https://docs.oracle.com/javase/tutorial/deployment/jar/run.html
To indicate which class is the application's entry point, you must add a Main-Class header to the JAR file's manifest. The header takes the form:
Main-Class: classname
Java没有提供任何标准的方式来加载嵌套的jar文件(即,它们本身包含在jar中的jar文件)。
Jar包的打包插件及核心方法
Spring Boot项目的pom.xml文件中使用如下插件进行打包:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
执行maven clean package之后,会生成两个文件:
spring-boot-start-demo-1.0-SNAPSHOT.jar
spring-boot-start-demo-1.0-SNAPSHOT.jar.original
spring-boot-maven-plugin默认有5个goals:
repackage、run、start、stop、build-info
在打包的时候默认使用的是repackage。
1.2. jar包目录结构
spring-boot-start-demo-1.0-SNAPSHOT
├── META-INF
│ └── MANIFEST.MF
├── BOOT-INF
│ ├── classes
│ │ └── 应用程序类
│ └── lib
│ └── 第三方依赖jar
└── org
└── springframework
└── boot
└── loader
└── springboot启动程序
META-INF内容:
在上述目录结构中,MANIFEST.MF记录了相关jar包的基础信息,包括入口程序等:
Manifest-Version: 1.0
Implementation-Title: spring-boot-start-demo
Implementation-Version: 1.0-SNAPSHOT
Start-Class: com.isvmspace.demo.SpringBootDemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.17.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
Main-Class是org.springframework.boot.loader.JarLauncher ,这个是jar启动的Main函数。
Start-Class是com.isvmspace.demo.SpringBootDemoApplication,这个是我们应用自己的Main函数。
1.3. JarLauncher源码解析
引入JarLauncher的依赖,查看源码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
</dependency>
JarLauncher的依赖关系:
class JarLauncher extends ExecutableArchiveLauncher
class ExecutableArchiveLauncher extends Launcher
JarLauncher的源码分析
public class JarLauncher extends ExecutableArchiveLauncher {
public JarLauncher() {}
public static void main(String[] args) throws Exception {
// 新建了JarLauncher并调用父类Launcher中的launch方法启动程序
new JarLauncher().launch(args);
}
}
在创建JarLauncher时,父类ExecutableArchiveLauncher找到自己所在的jar,并创建archive。
public abstract class ExecutableArchiveLauncher extends Launcher {
private final Archive archive;
public ExecutableArchiveLauncher() {
try {
// 找到自己所在的jar,并调用父类Launcher方法创建Archive
this.archive = createArchive();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}
Launcher的核心执行方法launch
public abstract class Launcher {
/**
* Launch the application. This method is the initial entry point that should be
* called by a subclass {@code public static void main(String[] args)} method.
*/
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
// 获取archives,并且得ClassLoader
ClassLoader classLoader = createClassLoader(getClassPathArchives());
// 获取启动类,并且通过反射运行主类的main()
launch(args, getMainClass(), classLoader);
}
}
2. SpringBoot如何启动Spring容器
2.1. 自定义springboot应用启动类
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
2.2. 创建SpringApplication并启动
org.springframework.boot.SpringApplication#run
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
2.2.1. 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));
// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 根据main方法推算出mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2.2. SpringApplication#run核心源码解析
org.springframework.boot.SpringApplication#run(java.lang.String...)
/**
* 运行Spring application, 创建并且更新ApplicationContext.
* @param args 命令行参数
* @return ApplicationContext
*/
public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
// 记录启动开始时间
stopWatch.start();
// spring上下文的接口, 接收ApplicationContext实现
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 通过spring.factroies中读取了SpringApplicationRunListener 的组件,用来发布事件或者运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发ApplicationStartingEvent的监听者执行onApplicationEvent
listeners.starting();
try {
// 根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
// 打印Banner
Banner printedBanner = printBanner(environment);
// 根据webApplicationType创建Spring上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 预初始化spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载spring ioc 容器 **相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布ApplicationStartedEvent事件
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 发布ApplicationReadyEvent事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
org.springframework.boot.SpringApplication#prepareEnvironment
/**
* 预初始化环境
*
* @param listeners 监听器
* @param applicationArguments 命令行参数
* @return 环境信息
*/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType 创建Environment 创建就会读取: java环境变量和系统环境变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将configurationProperties的配置信息 放在第一位
ConfigurationPropertySources.attach(environment);
// 发布ApplicationEnvironmentPreparedEvent的监听器
// ConfigFileApplicationListener读取了全局配置文件
listeners.environmentPrepared(environment);
// 将所有spring.main 开头的配置信息绑定SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//更新PropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
org.springframework.boot.SpringApplication#prepareContext
/**
* 预初始化spring上下文
*
* @param context 上下文
* @param environment 环境
* @param listeners 监听器
* @param applicationArguments 命令行参数
* @param printedBanner banner
*/
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
applyInitializers(context);
// 发布了ApplicationContextInitializedEvent
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 获取当前spring上下文beanFactory (负责创建bean)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类
load(context, sources.toArray(new Object[0]));
// 读取完配置类后发送ApplicationPreparedEvent
listeners.contextLoaded(context);
}