内容出自《Spring源码深度解析》
我们找到主函数入口SpringBootDemoApplication,发现这个入口的启动还是比较奇怪的,这也是Spring Boot启动的必要做法,那么,这也可以作为我们分析Spring Boot的入口:
@SpringBootApplication public class SpringBootDemoApplication { public static void main(String[] args) { SpringApplication.run(SponsorApplication.class, args); } }当顺着SpringApplication方法进入的时候我们找到了SpringApplication的一个看似核心逻辑的方法:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }在这里,我们发现了几个关键字眼:
context = this.createApplicationContext(); this.refreshContext(context); this.afterRefresh(context, applicationArguments);对比Spring完整的初始化方案,其中最为核心的就是SpringContext的创建、初始化、刷新等。那么我们可以直接进入查看其中的逻辑,同时,Spring作为一个全球都在使用的框架,会有非常多的需要考虑的问题,我们在阅读源码的过程中只需要关心核心的主流程,了解其工作原理,并在阅读的过程中感受它的代码风格以及设计理念就好了,如果真的追求理解每一行代码真的是非常耗时的一件事情,毕竟我们阅读源码的目的大多数是成长而不是真的要去维护Spring。
如上,实例化了一个ApplicationContext,而它就是Spring存在的基础。
继续查看prepareContext:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); this.postProcessApplicationContext(context); this.applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { this.logStartupInfo(context.getParent() == null); this.logStartupProfileInfo(context); } context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } Set<Object> sources = this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); this.load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }这里面的load函数是我们比较感兴趣的,代码如下:
protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); }这里出现了BeanDefinitionLoader,也就是走到了bean的加载部分。
对于Spring的扩展属性加载则更为简单,因为这些都是Spring本身原有的东西,Spring Boot仅仅是使用refresh激活而已。
分析下来,Spring Boot的启动并不是我们想象的那么神秘,按照约定大于配置的原则,内置了Spring原有的启动类,并在启动的时候启动及刷新,仅此而已。
org.springframework.context.annotation.AnnotationConfigApplicationContext