正文
SpringBoot的启动过程
今天我们通过调试的方式讲述一下SpringBoot的启动的过程,加深自己的理解。
一、运行环境介绍
我们的运行环境是web的运行环境。
JDK:1.8
SpringBoot:2.2.2.RELEASE
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.breakpoint</groupId> <artifactId>spring-boot-learn</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-amqp</artifactId>--> <!--</dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- 属性绑定的适时候,会有响应的提示的 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <!-- Package as an executable jar --> <!-- 可以将这个应用打包成可以执行的jar --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- 配置成可以执行的jar --> <executable>true</executable> </configuration> </plugin> </plugins> </build> </project>
二、启动的过程
2.1 创建SpringApplication对象并且执行run方法。
//org.springframework.context.ConfigurableApplicationContext public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
根据上面的执行的方式,可以总结为Spring启动的时候,一共做了2件事情:
1)、新建SpringApplication对象
2)、执行这个对象的run方法。
那么接下来分别看一看这2个方法都给咱们做了哪些的操作?
2.2 新建SpringApplication对象
新建SpringApplication对象是SpringBoot执行的第一个步骤:
// org.springframework.boot.SpringApplication#SpringApplication() // public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 设置资源的加载对象 this.resourceLoader = resourceLoader; //判断primarySources是不是null Assert.notNull(primarySources, "PrimarySources must not be null"); // 设置主要资源的对象 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 决定当前应用的类别 // 由于我们用的是web,所以当前的应用视为 WebApplicationType.SERVLET 是一个服务 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 从项目的META-INF/spring.factories文件里找到所有的ApplicationContextInitializer的组件,设置到this.initializers对象上 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 这个和上面的相似,将META-INF/spring.factories所有的ApplicationListener对象获取出来,设置到this.listeners这个对象上 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 决定哪一个类是祝启动类 /* StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } 上面的方式用了一个抛出异常的方式来决定哪一个类的里面存在main方法。 */ this.mainApplicationClass = deduceMainApplicationClass(); }
决定当前的应用是web还是
2.3 执行run方法
下面的代码就是执行所有的run的方法。
public ConfigurableApplicationContext run(String... args) { // 创建一个监视器,监视整个项目的启动,执行的开始时间以及结束的时间 StopWatch stopWatch = new StopWatch(); // 开始监视 stopWatch.start(); // 生命ConfigurableApplicationContext对象,也就是IOC容器,后面有具体的创建的过程 ConfigurableApplicationContext context = null; // 创建 exceptionReporters 、也就是异常报告对象,如果我们的应用在启动的过场中,出现了异常,这个时候,会报告相关的操作 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // 设置 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 的信息 configureHeadlessProperty(); // 从 META-INF/spring.factories 获取到所有的SpringApplicationRunListener组件 SpringApplicationRunListeners listeners = getRunListeners(args); // 这个listeners开始监听 listeners.starting(); try { // 设置应用的启动参数 也就是main防范的args参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 准备环境并且配置环境设置以及绑定到bindToSpringApplication上 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 配置环境 configureIgnoreBeanInfo(environment); // 打印Banner 也就是springboot的图标 Banner printedBanner = printBanner(environment); // 创建IOC容器对象,创建的过程中,根据不同的类别创建不同的IOC容器对象 /* switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } */ context = createApplicationContext(); // 获取到错误的报告 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备上下文环境 // 1/IOC容器设置环境 // 2.执行ApplicationContext 的后置处理器 // 3.调用ApplicationContextInitializer 的initialize方法在refreshed之前 // 4. 家在bean的定义到IOC容器 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新IOC容器 // 实际上就是调用Spring启动的核心的过程,调用各种的后置处理器以及初始化我们的bean的组件,并且将他们加入到IOC的容器中。 // 这一步,也创建了enbedded的tomcat的对象,并且在我们的配置好的端口上启动了 refreshContext(context); // 准备好IOC的容器后的操作 afterRefresh(context, applicationArguments); // 停止应用的监控,实际上,我们的主要的应用已经启动完成啦 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // SpringApplicationRunListener 的组件开始监听,也就是调用arted(context);的方法 listeners.started(context); // 调用所有的Runners /* runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } 根据上面的代码,可以知道 调用了所有的ApplicationRunner 以及 CommandLineRunner的组件,也就是最后的一次回调了。 */ callRunners(context, applicationArguments); } catch (Throwable ex) { // 如果有异常,那么就会报告异常 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 运行listeners.running listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
三、后记
经过上面的调试,更加的清晰的认识了SpringBoot的启动过程,SpringBoot还有其他许多的东西,比如自动装配等等这些比较重要的知识。