SpringApplication类分析 SpringBoot项目通常的启动手段
1 2 3 4 5 6 7 8 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
SpringApplication的run方法 SpringBootApplication中有两种重载的静态run方法。
其中一个方法返回值是调用另一个方法,另一个方法会返回一个ApplicationContext。
1 2 3 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
在这里,调用了SpringApplication的构造器,传入了primarySources,就是Application.class,随后调用了run方法
SpringApplicaiton的构造方法 SpringApplication有两个构造方法,一个支持传入ResourceLoader,另一个则调用前一个方法,但设置ResourceLoader为null。
1 2 3 4 5 6 7 8 9 10 11 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 .properties.setWebApplicationType(WebApplicationType.deduceFromClasspath()); this .bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
配置resourceLoader 首先配置resourceLoader
1 this .resourceLoader = resourceLoader;
配置primarySources 使用断言机制,用LinkedHashSet存储primarySources
1 this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
配置WebApplicationType 配置properties中的WebApplicationType(共有三种:None、SERVLET和REACTIVE)
deduceFromClasspath()通过使用ClassUtils根据是否存在相应的类,依次判断,是否为REACTIVE,是否为None,最后默认返回SERVLET
1 this .properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
配置bootstrap 配置bootstrap,调用getSpringFactoriesInstances方法,该方法会调用springframework中的SpringFactoriesLoader类
1 this .bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
getSpringFactoriesInstances方法的终极实现如下
1 2 3 private <T> List<T> getSpringFactoriesInstances (Class<T> type, ArgumentResolver argumentResolver) { return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver); }
forDefaultResourceLocation方法 首先,SpringFactories类有一个forDefaultResourceLocation方法,FACTORIES_RESOURCE_LOCATION变量存储了factories的位置,也就是meta-inf下的spring.factories
1 2 3 4 5 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;public static SpringFactoriesLoader forDefaultResourceLocation (@Nullable ClassLoader classLoader) { return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader); }
经过一系列调用,看到了真正的forResourceLocation方法。
1 2 3 4 5 6 7 8 9 public static SpringFactoriesLoader forResourceLocation (String resourceLocation, @Nullable ClassLoader classLoader) { Assert.hasText(resourceLocation, "'resourceLocation' must not be empty" ); ClassLoader resourceClassLoader = (classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader()); Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent( resourceClassLoader, key -> new ConcurrentReferenceHashMap<>()); return loaders.computeIfAbsent(resourceLocation, key -> new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation))); }
首先使用断言机制检查resourceLocation为Text
1 Assert.hasText(resourceLocation, "'resourceLocation' must not be empty" );
获取SpringFactoriesLoader类的classloader
1 2 ClassLoader resourceClassLoader = (classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader());
可以看到在SpringFactoriesLoader类内还有一个简单的基于ConcurrentReferenceHashMap类的cache,存储了加载过得SpringFactoriesLoader
总而言之,会想办法返回一个SpringFactoriesLoader
1 new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation))
loadFactoriesResource这个方法,会遍历spring.factories这个方法,返回一个map,就是读入spring.factories这个文件,读取这些默认工厂。
load方法 load方法会加载并实例化在spring.factories指出的应使用的实现类,并使用AnnotationAwareOrderComparator排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public <T> List<T> load (Class<T> factoryType, @Nullable ArgumentResolver argumentResolver, @Nullable FailureHandler failureHandler) { Assert.notNull(factoryType, "'factoryType' must not be null" ); List<String> implementationNames = loadFactoryNames(factoryType); logger.trace(LogMessage.format("Loaded [%s] names: %s" , factoryType.getName(), implementationNames)); List<T> result = new ArrayList<>(implementationNames.size()); FailureHandler failureHandlerToUse = (failureHandler != null ) ? failureHandler : THROWING_FAILURE_HANDLER; for (String implementationName : implementationNames) { T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse); if (factory != null ) { result.add(factory); } } AnnotationAwareOrderComparator.sort(result); return result; }
instantiateFactory方法,就是先用ClassUtils获取Class实例,然后用FactoryInstantiator获取实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Nullable protected <T> T instantiateFactory (String implementationName, Class<T> type, @Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) { try { Class<?> factoryImplementationClass = ClassUtils.forName(implementationName, this .classLoader); Assert.isTrue(type.isAssignableFrom(factoryImplementationClass), () -> "Class [%s] is not assignable to factory type [%s]" .formatted(implementationName, type.getName())); FactoryInstantiator<T> factoryInstantiator = FactoryInstantiator.forClass(factoryImplementationClass); return factoryInstantiator.instantiate(argumentResolver); } catch (Throwable ex) { failureHandler.handleFailure(type, implementationName, ex); return null ; } }
FactoryInstantiator是SpringFactoriesLoader的一个静态final内部类,用于初始化工厂实例,主要是使用里面的instantiate方法,该方法会使用KoelinDelegate以防止运行时对Kotlin的hard dependency,或者直接调用类的构造器初始化示例。
1 2 3 4 5 6 7 T instantiate (@Nullable ArgumentResolver argumentResolver) throws Exception { Object[] args = resolveArgs(argumentResolver); if (isKotlinType(this .constructor.getDeclaringClass())) { return KotlinDelegate.instantiate(this .constructor, args); } return this .constructor.newInstance(args); }
setInitializers 前面已经分析道,SpringFactoriesLoader类中有一个缓存,再次调用getSpringFactoriesInstances方法会直接获得之前加载的spring.factories缓存结果并创建实例。
1 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
该方法也就是把spring.factories中配置的initializer载入
setListeners 1 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
同上,也就是把spring.factories中配置的listener载入
配置mainApplicationClass 1 this .mainApplicationClass = deduceMainApplicationClass();
deduceMainApplicationClass方法,调用了java.lang的stackWalker,目的是获取当前的主类。
创建了SpringApplication后,便回到了run方法中,继续分析SpringApplication.run方法。
创建startup 1 Startup startup = Startup.create();
Startup是SpringApplication的一个抽象静态内部类
记录了一些关于springboot应用启动的信息
1 2 3 4 private Duration timeTakenToStarted;protected abstract long startTime () ;protected abstract Long processUptime () ;protected abstract String action () ;
startup的create方法会使用ClassUtil判断,该使用哪种startup类的实力
1 2 3 4 5 6 static Startup create () { ClassLoader classLoader = Startup.class.getClassLoader(); return (ClassUtils.isPresent("jdk.crac.management.CRaCMXBean" , classLoader) && ClassUtils.isPresent("org.crac.management.CRaCMXBean" , classLoader)) ? new CoordinatedRestoreAtCheckpointStartup() : new StandardStartup(); }
前者是一个基于 CRaC(Coordinated Restore At Checkpoint)技术的startup,可以快速启动,后面是一个标准的startup。
标准的startup类内部非常简单,也就不过多分析
ManagementFactory是 Java Management Extensions(JMX)的一部分,提供了一系列静态方法来获取 JVM 的管理接口,用于监控和管理 JVM 的运行状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static final class StandardStartup extends Startup { private final Long startTime = System.currentTimeMillis(); @Override protected long startTime () { return this .startTime; } @Override protected Long processUptime () { try { return ManagementFactory.getRuntimeMXBean().getUptime(); } catch (Throwable ex) { return null ; } } @Override protected String action () { return "Started" ; } }
配置shutdown hook 默认开启了shutdownhook,用于graceful shutdown
1 2 3 if (this.properties.isRegisterShutdownHook()) { SpringApplication.shutdownHook.enableShutdownHookAddition(); }
配置BootstrapContext 1 DefaultBootstrapContext bootstrapContext = createBootstrapContext();
调用了createBootstrapContext方法
1 2 3 4 5 private DefaultBootstrapContext createBootstrapContext () { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this .bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; }
DefaultBootstrapContext是ConfigurableBootstrapContext的默认实现类,ConfigurableBoostrapContext是一个接口,它继承了BootstrapRegistry, BootstrapContext接口。
BootstrapContext是在应用启动时,和应用的Environment后置处理期间,在ApplicationContext还没准备好时使用的上下文环境,提供了对某类单例的lazy access,即可能创建非常消耗资源,或者需要在ApplicationContext有效前共享的单例。BoosstrapContext适用于注册这些单例。
初始化和无头配置 初始化变量,作无头配置。
1 2 ConfigurableApplicationContext context = null ; configureHeadlessProperty();
配置listeners 1 2 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext,this .mainApplicationClass);
getRunListeners方法,也是通过getSpringFactoriesInstances方法,获取spring.factories中配置的listener
1 2 3 4 5 6 7 8 9 10 11 12 13 private SpringApplicationRunListeners getRunListeners (String[] args) { ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this ); argumentResolver = argumentResolver.and(String[].class, args); List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver); SpringApplicationHook hook = applicationHook.get(); SpringApplicationRunListener hookListener = (hook != null ) ? hook.getRunListener(this ) : null ; if (hookListener != null ) { listeners = new ArrayList<>(listeners); listeners.add(hookListener); } return new SpringApplicationRunListeners(logger, listeners, this .applicationStartup); }
SpringApplicationRunListeners中搭载了一系列SpringApplicationRunListener接口的实现类,主要作用是监听SpringApplication的run方法,SpringApplicationRunListeners通过SpringFactoriesLoader加载,每次run都会创建一个SpringApplciationRunListener实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public interface SpringApplicationRunListener { default void starting (ConfigurableBootstrapContext bootstrapContext) { } default void environmentPrepared (ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { } default void contextPrepared (ConfigurableApplicationContext context) { } default void contextLoaded (ConfigurableApplicationContext context) { } default void started (ConfigurableApplicationContext context, Duration timeTaken) { } default void ready (ConfigurableApplicationContext context, Duration timeTaken) { } default void failed (ConfigurableApplicationContext context, Throwable exception) { } }
创建ApplicationContext 一大团try-catch包围的创建ApplicationContext的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); startup.started(); if (this .properties.isLogStartupInfo()) { new StartupInfoLogger(this .mainApplicationClass, environment).logStarted(getApplicationLog(), startup); } listeners.started(context, startup.timeTakenToStarted()); callRunners(context, applicationArguments); } catch (Throwable ex) { throw handleRunFailure(context, ex, listeners); }
创建一个DefaultApplicationArguments实现了ApplicationArguments接口,这个接口就是记录SpringApplication启动时候使用的参数。
1 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
准备环境
1 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
prepareEnvironment方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); ApplicationInfoPropertySource.moveToEnd(environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix" ), "Environment prefix cannot be set via properties." ); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
getOrCreateEnvironment方法会直接获取环境,或者调用当前的applicationContextFactory中的默认创建环境方法。
1 2 3 4 5 6 7 8 9 10 11 private ConfigurableEnvironment getOrCreateEnvironment () { if (this .environment != null ) { return this .environment; } WebApplicationType webApplicationType = this .properties.getWebApplicationType(); ConfigurableEnvironment environment = this .applicationContextFactory.createEnvironment(webApplicationType); if (environment == null && this .applicationContextFactory != ApplicationContextFactory.DEFAULT) { environment = ApplicationContextFactory.DEFAULT.createEnvironment(webApplicationType); } return (environment != null ) ? environment : new ApplicationEnvironment(); }
比如,在ServletWebServerApplicationContextFactory类的createEnvironment方法中,返回了一个ApplciationServletEnvironment,该类又继承了StandardServletEnvironment类
1 2 3 4 @Override public ConfigurableEnvironment createEnvironment (WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment(); }
StandardServletEnvironment记录了一些变量
1 2 3 4 5 6 7 8 /** Servlet context init parameters property source name: {@value}. */ public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"; /** Servlet config init parameters property source name: {@value}. */ public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"; /** JNDI property source name: {@value}. */ public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
可以自定义propertySource,可以初始化propertySource.
WebApplicationContextUtils是一个工具类,提供了一些快捷的方法,可以取出给定的ServletContext的root WebApplicationContext
1 2 3 4 @Override public void initPropertySources (@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); }
接着看
1 configureEnvironment(environment, applicationArguments.getSourceArgs());
configureEnvironment是一个模版方法,用于按顺序进行配置,如果想完全控制环境定制,可以重写这个方法。
1 2 3 4 5 6 7 protected void configureEnvironment (ConfigurableEnvironment environment, String[] args) { if (this .addConversionService) { environment.setConversionService(new ApplicationConversionService()); } configurePropertySources(environment, args); configureProfiles(environment, args); }
通知listeners,把applicationInfoPropertySource和DefaultPropertiesPropertySource移到environment的propertySource的最后面,就是字面上的最后面,list.addLast。随后把environment绑定到application上(就是把environment bind到 aplicationProperties上),如果不是自定义环境,看看是否要转换环境,随后把configurationPropertySources绑定到environment上。
1 2 3 4 5 6 7 8 9 10 11 12 listeners.environmentPrepared(bootstrapContext, environment); ApplicationInfoPropertySource.moveToEnd(environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix" ), "Environment prefix cannot be set via properties." ); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment;
打印banner,就是springboot启动时候的那一坨
1 Banner printedBanner = printBanner(environment);
1 2 3 4 5 6 7 8 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | ' _ | '_| | ' _ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.4.3)
context就是之前初始化为null的ConfigurableApplicationContext,调用createApplicationContext方法
1 2 context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup);
createApplicationContext方法会调用this.applicationContextFactory创建context
1 2 3 protected ConfigurableApplicationContext createApplicationContext () { return this .applicationContextFactory.create(this .properties.getWebApplicationType()); }
this.applicationContextFactory是用ApplicationContextFactory.DEFAULT创建出来的一个DefaultApplicationContextFactory。
1 2 3 4 5 private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();
准备context,详细看prepareContext这个方法
1 2 3 4 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); startup.started();
首先,进行一些设置,包括给context配置环境,关闭之前的bootstrapContext,告诉listeners新的applicationContext已经准备好,同时输出一些log
1 2 3 4 5 6 7 8 9 10 11 context.setEnvironment(environment); postProcessApplicationContext(context); addAotGeneratedInitializerIfNecessary(this .initializers); applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); if (this .properties.isLogStartupInfo()) { logStartupInfo(context.getParent() == null ); logStartupInfo(context); logStartupProfileInfo(context); }
1 2 2025 -03 -07T13:26 :34.321 +08:00 INFO 3564 --- [SpringBoot] [ main] com.example.springboot.Application : Starting Application using Java 21.0 .6 with PID 3564 (D:\IdeaProjects\LearningJava\SpringBoot\target\classes started by Lenovo in D:\IdeaProjects\LearningJava)2025 -03 -07T13:26 :37.457 +08:00 INFO 3564 --- [SpringBoot] [ main] com.example.springboot.Application : No active profile set, falling back to 1 default profile: "default"
接下来开始添加一些bean,首先是取出context里的beanFactory,先注册”springApplicationArguments”这个单例bean,
1 2 3 4 5 6 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); }
判断beanFactory是否是AbstractAutowireCapableBeanFactory的实力,如果是,就允许循环依赖,如果还是DefaultListableBeanFactory的实力,就允许覆写bean的definition。
1 2 3 4 5 6 if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { autowireCapableBeanFactory.setAllowCircularReferences(this .properties.isAllowCircularReferences()); if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { listableBeanFactory.setAllowBeanDefinitionOverriding(this .properties.isAllowBeanDefinitionOverriding()); } }
判断是否懒加载,懒加载就添加一个bean factory后置处理器
1 2 3 if (this .properties.isLazyInitialization()) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }
是否keepalive,是的话添加一个listener
1 2 3 if (this .properties.isKeepAlive()) { context.addApplicationListener(new KeepAlive()); }
keep alive监听器比较有意思,就是一个一直sleep的线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private static final class KeepAlive implements ApplicationListener <ApplicationContextEvent > { private final AtomicReference<Thread> thread = new AtomicReference<>(); @Override public void onApplicationEvent (ApplicationContextEvent event) { if (event instanceof ContextRefreshedEvent) { startKeepAliveThread(); } else if (event instanceof ContextClosedEvent) { stopKeepAliveThread(); } } private void startKeepAliveThread () { Thread thread = new Thread(() -> { while (true ) { try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ex) { break ; } } }); if (this .thread.compareAndSet(null , thread)) { thread.setDaemon(false ); thread.setName("keep-alive" ); thread.start(); } } private void stopKeepAliveThread () { Thread thread = this .thread.getAndSet(null ); if (thread == null ) { return ; } thread.interrupt(); } }
回到前面
再添加一个后置处理器
1 context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
是否AOT optimization,再通知listeners,context已加载好。
1 2 3 4 5 6 7 if (!AotDetector.useGeneratedArtifacts()) { Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object[0 ])); } listeners.contextLoaded(context);
刷新context
1 refreshContext(context);
刷新context后,可以看到控制台一些输出。
1 2 3 4 5 6 7 2025 -03 -07T13:45 :25.483 +08:00 ERROR 3564 --- [SpringBoot] [ main] o.a.catalina.core.AprLifecycleListener : An incompatible version [1.2 .26 ] of the Apache Tomcat Native library is installed, while Tomcat requires version [1.2 .34 ]2025 -03 -07T13:45 :27.143 +08:00 INFO 3564 --- [SpringBoot] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)2025 -03 -07T13:45 :27.258 +08:00 INFO 3564 --- [SpringBoot] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2025 -03 -07T13:45 :27.259 +08:00 INFO 3564 --- [SpringBoot] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1 .36 ]2025 -03 -07T13:45 :27.541 +08:00 INFO 3564 --- [SpringBoot] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2025 -03 -07T13:45 :27.549 +08:00 INFO 3564 --- [SpringBoot] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 31535 ms2025 -03 -07T13:45 :29.742 +08:00 INFO 3564 --- [SpringBoot] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
调用startup记录一下,判断是否添加一个startUpinfoLogger,通知一下listeners
1 2 3 4 5 startup.started(); if (this .properties.isLogStartupInfo()) { new StartupInfoLogger(this .mainApplicationClass, environment).logStarted(getApplicationLog(), startup); } listeners.started(context, startup.timeTakenToStarted());
startupInfoLogger的输出
1 2025 -03 -07T13:47 :56.034 +08:00 INFO 3564 --- [SpringBoot] [ main] com.example.springboot.Application : Started Application in 2040.684 seconds (process running for 2085.357 )
呼叫runners
1 callRunners(context, applicationArguments);
进一步,对每个instance作call runner
1 2 3 4 5 6 7 8 9 10 11 private void callRunners (ConfigurableApplicationContext context, ApplicationArguments args) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); String[] beanNames = beanFactory.getBeanNamesForType(Runner.class); Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>(); for (String beanName : beanNames) { instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName); } Comparator<Object> comparator = getOrderComparator(beanFactory) .withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames)); instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args)); }
有两种runner,一种是普通的runner,另一种是commandLineRunner,commandLineRunner继承了Runner,可以通过ordered或者order注解排序,可以在bean里实现这个接口,spring application在启动时候就会调用run方法
1 2 3 4 5 6 7 8 9 private void callRunner (Runner runner, ApplicationArguments args) { if (runner instanceof ApplicationRunner) { callRunner(ApplicationRunner.class, runner, (applicationRunner) -> applicationRunner.run(args)); } if (runner instanceof CommandLineRunner) { callRunner(CommandLineRunner.class, runner, (commandLineRunner) -> commandLineRunner.run(args.getSourceArgs())); } }
检查context是否处于启动状态,启动则通知listeners
最后返回context
1 2 3 4 5 6 7 8 9 try { if (context.isRunning()) { listeners.ready(context, startup.ready()); } } catch (Throwable ex) { throw handleRunFailure(context, ex, null ); } return context;
至此,整个SpringApplication.run方法已分析完。