【源码】简单看SpringBoot启动过程
最简单的启动类如下。通过该启动类,Java线程可以启动并维持一个进程(如tomcat进程),用于处理相应请求。
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args);//从这行出发探讨 } }
实际上,上述中的run方法是将DemoApplication转为SpringApplication,再调用SpringApplication的run(args)方法,去掉异常、打印日志等次要信息后,得到【简化版】代码:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start();//启动计时器 ConfigurableApplicationContext context = null; SpringApplicationRunListeners listeners = getRunListeners(args);//根据参数获取Spring应用*** listeners.starting();//启动***,***处于监听状态 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//***+参数-->准备运行环境 Banner printedBanner = printBanner(environment);//控制台打印banner,即控制台打印经典的《Spring》 context = createApplicationContext();//创建应用上下文 // 准备、刷新上下文(使用到了***和环境) prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop();//停止计时器 listeners.started(context);//***启动上下文,启动但还未运行 callRunners(context, applicationArguments); listeners.running(context);//***运行上下文 return context; }
该方法用到两个类:SpringApplicationRunListeners和StopWatch,分别与***和计时器相关,SpringBoot通过***SpringApplicationRunListeners来启动CPU进程并使项目保持运行状态,并通过计时器StopWatch来统计启动时间。
从代码逻辑看,***参与了运行环境和上下文的逻辑过程,而运行环境又参与了上下文的逻辑过程。最后通过***运行上下文运行项目,并返回上下文,从中可知,上下文是关键。
以上就是SpringBoot启动的简化版过程。
另外,上面提到的***是run方法专属的***,每调用一次run方法,就会创建一个***SpringApplicationRunListener(注意名称中的Run)。该***是一个接口,提供了启动相关的接口方法,比如starting(),started(),running(),failed(),contextLoaded()等,启动时会通过其实现类来执行具体过程。那在哪里实现了***接口呢?就在run()方法代码中的:
SpringApplicationRunListeners listeners = getRunListeners(args);//根据参数获取Spring应用***
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }
再具体点,构造器参数的getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)返回了一个***实现类,抽象来看,该方法"告诉"Spring实例工厂: "我给你这些args参数,请给我一个SpringApplicationRunListener.class类型的实例",创建***实例的工作就由Spring的实例工厂负责了。而Spring实例工厂如何通过参数和类加载器创建一个个的实例,就和启动过程无关了,因此暂不在本文范围内。
实际上到了这里,对于SpringBoot的启动过程,我们只是到了源码的表层,至于***实例如何启动和运行上下文、如何读取配置文件等具体细节,SpringBoot做了大量的封装,本文也没能深入到这个层次。但看完本文,你可以在源码的层次理解:SpringBoot的启动需要若干类的参与,其中的***经过Spring工厂实例化后,通过操纵应用上下文来启动SpringBoot。