2. Spring Boot项目启动原理初探
SpringBoot从宏观上说,就是对spring容器进行了一层包装。它内部的入口是利用 SpringApplication类的static的 run 方法进行启动的,调用的图:
上图中的这些方法都位于org.springframework.boot.SpringApplication这个类中,由此可见SpringApplication这个类在springboot框架中的作用。
//调用示例:
public static void main(String[] args) throws Exception {
SpringApplication.run(Examples.class, args);
}
SpringApplication类提供了两个static的run方法:
public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
上面两个方法其实就是一个,最终都会创建一个SringApplication实例,在构造时会处理传入的 sources 参数,而且这souces类可以是多个,调用的方式还可以这样:
public static void main(String[] args) throws Exception {
Class<?>[] primarySources = new Class<?>[] { Examples.class , Examples1.class } ;
SpringApplication.run( primarySources , args);
}
SpringApplication构造方法如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); //用来查找每个jar和类路径下的/META-INF/spring.factories文件,并获取其中的特定接口的配置的 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //找出每个jar和类路径下的/META-INF/spring.factories文件中配置的ApplicationContextInitializer类 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); //推断出带有main方法的运行类 }
上面的代码通过构造完成初始化之后,紧接着就是调用SpringApplication类的实例 run方法,代码如下:
public ConfigurableApplicationContext run(String... args) { //StopWatch作用是用来统计spring实例运行时间 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //ConfigurableApplicationContext接口提供了对Spring容器的配置功能 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); // 1. 会执行ApplicationContextInitializer接口进行初始化工作 // 2. 对外发布监听事件 // 3. 优先创建有特定作用的单例bean // 4. 把带有main方法的主类作为bean的配置源,并从配置源中加载bean信息到容器中 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //spring容器的生命周期处理,该方法完成后单例bean实例就创建了 refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //对外发布监听事件 listeners.started(context); //我们在开发中可能会有这样的情景。需要在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。他们的执行时机为容器启动完成的时候。 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //发布监听事件 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
至此,SpringApplication的实例run方法执行完成,该方法主要是完成spring容器的创建和启动