public static void main(String[] args) {
SpringApplication.run(YtTakeOutApplication.class, args);
System.out.println("********** 小 程 序 后 台 启 动 成 功 !***********");
}
可以看出最后是new 了一个SpringAppclication,然后使用run方法。
源代码如下
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1.实例化SpringApplication,之后执行run()方法
return (new SpringApplication(primarySources)).run(args);
}
//最终执行的run()方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//2.代码执行时间监控开启
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//3.配置headless属性,默认为true
this.configureHeadlessProperty();
//4.获取SpringApplicationRunListener集合
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//5.回调所有SpringApplicationRunListener对象的starting()方法
listeners.starting();
Collection exceptionReporters;
try {
//6.创建ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//7.创建Environment对象,加载属性配置
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//8.设置系统参数
this.configureIgnoreBeanInfo(environment);
//9.获取打印的Spring Boot banner
Banner printedBanner = this.printBanner(environment);
//10.创建Spring容器ApplicationContext
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//11.准备容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//12.刷新Spring容器
this.refreshContext(context);
//13.执行Spring容器初始化的后置逻辑
this.afterRefresh(context, applicationArguments);
//14.代码执行时间监控结束
stopWatch.stop();
//打印Spring Boot的启动时长日志
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//15.发布容器启动事件
listeners.started(context);
//16.调用ApplicationRunner或者CommandLineRunner的运行方法
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);
}
}
public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
public static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private static final Log logger = LogFactory.getLog(SpringApplication.class);
static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook();
private Set<Class<?>> primarySources;
private Set<String> sources;
private Class<?> mainApplicationClass;
private Mode bannerMode;
private boolean logStartupInfo;
private boolean addCommandLineProperties;
private boolean addConversionService;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private WebApplicationType webApplicationType;
private boolean headless;
private boolean registerShutdownHook;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
private Set<String> additionalProfiles;
private boolean allowBeanDefinitionOverriding;
private boolean allowCircularReferences;
private boolean isCustomEnvironment;
private boolean lazyInitialization;
private String environmentPrefix;
private ApplicationContextFactory applicationContextFactory;
private ApplicationStartup applicationStartup;
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
// 构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
值得注意的是 webApplicationType 它是标注是什么类型的 application
//当前应用不是Web应用,不需要启动一个内嵌的Web服务器
NONE,
//当前应用需要启动一个内嵌的基于Servlet的服务器
SERVLET,
//当前应用需要启动一个内嵌的基于Reactive的服务器
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
代码执行时间的监控开启,在SpringBoot应用启动后会打印启动时间。
该代码的作用是SpringBoot应用在启动时,没有检测到显示器也能够继续执行后面的步骤。
getListeners()方法
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
Starting()方法
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
this.doWithListeners("spring.boot.application.starting", (listener) -> {
listener.starting(bootstrapContext);
}, (step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//创建环境对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
//触发监听器
listeners.environmentPrepared((ConfigurableEnvironment)environment);
//为SpringApplication 绑定 environment
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
//为environment其他附件配置信息
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
在创建环境完成后,接下来是配置环境,configureEnvironment()方法源码如下所示:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
//加载启动命令行配置属性
this.configurePropertySources(environment, args);
//设置active属性
this.configureProfiles(environment, args);
}
该方法主要加载一些默认配置,在执行完这一步骤后,会触发监听器(主要触发ConfigFileApplicationListener),将会加载application.properties或者application.yml配置文件
this.configureIgnoreBeanInfo(environment);
该方法会获取spring.beaninfo.ignore配置项的值,即使未获取也没有关系。代码的最后还是给该配置项输入了一个默认值true。
Banner printedBanner = this.printBanner(environment);
这个Banner图案就是Spring Boot 的图案
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Mode.OFF) {
return null;
} else {
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader((ClassLoader)null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
通过源码可以看出createApplicationContext()方法的执行逻辑:根据webApplicationType决定创建哪种contextClass。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将应用环境设置到容器中,包括各种变量
context.setEnvironment(environment);
//执行容器后置处理
this.postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer
this.applyInitializers(context);
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
// 添加特定的Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//加载资源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载启动类,将启动类注入容器中
this.load(context, sources.toArray(new Object[0]));
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextLoaded(context);
}
在创建对应的Spring容器后,程序会进行初始化、加载主启动类等预处理工作,主启动类加载完成,容器准备好。
refreshContext()方法源码如下所示:
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
this.refresh(context);
}
该方法是Spring Bean加载的核心,用于刷新整个Spring上下文信息,定义整个Spring上下文加载的流程。
包括实例的初始化和属性设置、自动配置类的加载和执行、内置Tomcat服务器的启动等步骤。
执行Spring容器初始化的后置逻辑,默认实现是一个空的方法
知道了启动应用所花费的时间
在ApplicationContext完成启动后,程序会对ApplicationRunner和CommandLineRunner进行回调处理,查找当前ApplicationContext中是否注解有CommandLineRunner,如果有,则遍历执行它们。
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
this.handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
this.reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception var9) {
logger.warn("Unable to close ApplicationContext", var9);
}
ReflectionUtils.rethrowRuntimeException(exception);
}