news 2026/8/14 10:19:10

SpringBoot启动源码深度解析:从自动装配到内嵌服务器启动全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot启动源码深度解析:从自动装配到内嵌服务器启动全流程

1. 项目概述:为什么我们需要深入SpringBoot启动源码?

如果你是一个Java开发者,尤其是使用SpringBoot框架的开发者,你可能已经习惯了在main方法里写上一行SpringApplication.run(YourApplication.class, args),然后一个功能完备的Web应用就启动了。这太方便了,方便到我们几乎不再关心背后发生了什么。但当你遇到启动失败、配置不生效、Bean加载顺序诡异、或者想深度定制启动流程时,这种“黑盒”状态就会让你束手无策。

我经历过很多次这样的时刻:一个看似简单的依赖冲突导致应用启动卡住半小时;一个自定义的BeanPostProcessor没有按照预期执行;想实现一个类似@Conditional的注解却无从下手。最终,解决问题的钥匙都藏在源码里。因此,我决定花时间,把SpringBoot从点击“运行”到服务就绪的整个启动流程,像解剖麻雀一样彻底拆解一遍。这不是一篇简单的API罗列,而是结合我踩过的坑和调试经验,带你走一遍SpringBoot的“心路历程”。无论你是想应对高级面试,还是想真正掌握框架以进行深度定制,这篇超详细的源码解析都将为你提供一张清晰的“地图”。

2. 启动流程全景与核心脉络拆解

在深入代码之前,我们需要建立一个宏观的认知。SpringBoot的启动过程,本质上是一个事件驱动、生命周期明确、高度可扩展的初始化流程。它并非一蹴而就,而是分阶段、分层次地将一个简单的Java类,逐步膨胀为一个完整的Spring应用上下文(ApplicationContext),并最终启动内嵌的Web服务器。

整个流程可以概括为以下几个核心阶段,这也是我们后续解析的路线图:

  1. 初始化阶段:创建SpringApplication实例,推断应用类型,加载初始化器和监听器。
  2. 运行阶段:执行SpringApplication.run()方法,这是整个流程的引擎。
  3. 准备环境阶段:创建并配置应用运行环境(Environment),加载配置文件(如application.yml)。
  4. 创建应用上下文阶段:根据应用类型(Servlet、Reactive等)实例化对应的ApplicationContext
  5. 刷新应用上下文阶段:这是Spring框架的核心,包括Bean定义加载、Bean工厂后处理、Bean实例化、依赖注入、AOP代理等。
  6. 后置处理与服务器启动阶段:执行刷新后的回调,启动内嵌Web服务器(如Tomcat),并发布应用启动完成事件。

理解这个脉络后,我们就能带着问题去看源码:每个阶段具体做了什么?提供了哪些扩展点?常见的坑都出在哪里?

2.1 核心类SpringApplication的初始化

一切始于SpringApplication的构造方法。当我们调用new SpringApplication(primarySources)或使用SpringApplication.run(YourApplication.class, args)的静态方法时,构造过程就开始了。

// 简化后的核心初始化逻辑 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 1. 推断Web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 2. 加载并设置“应用上下文初始化器” setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 3. 加载并设置“应用监听器” setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 4. 推断主应用类 this.mainApplicationClass = deduceMainApplicationClass(); }

关键点解析与实操心得:

  • Web应用类型推断WebApplicationType.deduceFromClasspath()方法通过检查类路径下是否存在特定的类来判断应用类型。例如,存在ServletConfigurableWebApplicationContext类,但不存在WebFlux相关类,则推断为SERVLET类型(即传统的Spring MVC应用)。这个推断决定了后续创建哪种ApplicationContext踩坑提示:如果你在非Web项目中错误引入了spring-boot-starter-web依赖,它会被推断为Web应用,可能导致不必要的资源消耗和端口占用。
  • getSpringFactoriesInstances机制:这是SpringBoot“约定优于配置”和自动装配的灵魂机制之一。它会从所有jar包的META-INF/spring.factories文件中,读取指定接口(如ApplicationContextInitializer.class)的全限定类名,然后实例化。我们自定义的初始化器或监听器,也是通过在这个文件中配置来生效的。实操技巧:在SpringBoot 2.7+版本,更推荐使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件来定义自动配置类,但spring.factories对于初始化器和监听器依然有效。

2.2run方法:启动引擎的详细拆解

SpringApplication.run()方法是启动流程的总入口。它返回一个ConfigurableApplicationContext,但内部过程极其丰富。

public ConfigurableApplicationContext run(String... args) { // 1. 创建并启动“停止监视器”,用于优雅关机 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 2. 初始化一个空的“引导上下文”,主要用于加载外部配置(如Spring Cloud场景) ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 3. 获取并启动“运行时监听器” SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); // 发布“应用开始启动”事件 try { // 4. 准备应用参数和环境 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 处理需要忽略的Bean信息 configureIgnoreBeanInfo(environment); // 5. 打印Banner Banner printedBanner = printBanner(environment); // 6. 创建应用上下文 context = createApplicationContext(); // 获取异常报告器 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 7. 准备上下文:关联环境、设置BeanName生成器、资源加载器、应用启动器,并执行初始化器 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 8. 刷新上下文(最核心、最复杂的步骤) refreshContext(context); // 9. 刷新后的后置处理(默认为空方法,用于扩展) afterRefresh(context, applicationArguments); // 停止计时 stopWatch.stop(); // 10. 发布“应用已启动”事件 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); // 发布“上下文已刷新”事件 // 11. 执行`ApplicationRunner`和`CommandLineRunner` callRunners(context, applicationArguments); // 12. 发布“应用就绪”事件 listeners.running(context); } catch (Throwable ex) { // 异常处理... } return context; }

这个流程清晰地展示了SpringBoot如何通过事件监听机制SpringApplicationRunListeners)将启动过程模块化、可观测化。开发者可以通过实现ApplicationListener接口并监听特定事件(如ApplicationStartingEvent,ApplicationPreparedEvent等),在生命周期的特定节点插入自定义逻辑。

3. 核心阶段深度解析与实操要点

3.1 环境准备:prepareEnvironment

环境是应用的基石,包含了配置文件、JVM系统属性、操作系统环境变量等。prepareEnvironment方法负责创建和配置它。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 1. 根据Web应用类型创建对应的环境对象(StandardServletEnvironment或StandardEnvironment等) ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2. 配置环境:主要是处理命令行参数(--server.port=8080这种) configureEnvironment(environment, applicationArguments.getSourceArgs()); // 3. 将环境绑定到SpringApplication本身(通过ConfigurationPropertySources) ConfigurationPropertySources.attach(environment); // 4. 通知所有监听器:环境已准备就绪。这是关键扩展点! listeners.environmentPrepared(environment); // 5. 将环境移动到当前上下文 bindToSpringApplication(environment); // 6. 如果不是自定义环境,进行额外配置(如转换非字符串属性) if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } // 7. 再次附加配置属性源,确保顺序 ConfigurationPropertySources.attach(environment); return environment; }

注意事项与排查技巧:

  • 配置加载顺序:SpringBoot的属性源有严格的优先级。listeners.environmentPrepared(environment)这一步会触发ConfigFileApplicationListener,它负责从application.properties,application.yml,application-{profile}.yml等文件加载配置。其优先级顺序是:命令行参数 > Java系统属性 > 操作系统环境变量 > 当前profile的配置文件 > 默认配置文件。理解这个顺序对排查配置冲突至关重要。
  • 环境未绑定错误:如果你在自定义的ApplicationContextInitializer中过早地尝试从Environment获取属性,可能会失败,因为此时环境可能还未完全绑定到SpringApplication。正确的做法是在environmentPrepared事件之后或ApplicationContext创建之后再操作。
  • 自定义环境:如果你想完全控制环境的创建(例如集成Apollo、Nacos等配置中心),可以重写SpringApplicationgetOrCreateEnvironment()方法,返回你自己的ConfigurableEnvironment实现。

3.2 创建应用上下文:createApplicationContext

根据之前推断的webApplicationType,SpringBoot会实例化对应的ApplicationContext

protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { // 根据应用类型选择上下文类 switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); // AnnotationConfigServletWebServerApplicationContext break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); // AnnotationConfigReactiveWebServerApplicationContext break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); // AnnotationConfigApplicationContext } } catch (ClassNotFoundException ex) { // ... } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }

核心要点:对于最常见的Servlet Web应用,创建的是AnnotationConfigServletWebServerApplicationContext。这个类非常重要,它集成了注解配置的能力(AnnotationConfig)、Servlet Web支持以及内嵌Web服务器(WebServer)的生命周期管理。它是我们后续分析refresh()方法的基础。

3.3 准备上下文:prepareContext

在刷新(refresh)之前,需要对新创建的ApplicationContext进行一番“梳妆打扮”。

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 1. 将环境设置到上下文中 context.setEnvironment(environment); // 2. 后置处理上下文:设置资源加载器、类型转换器、应用启动器 postProcessApplicationContext(context); // 3. 执行所有“应用上下文初始化器” applyInitializers(context); // 4. 发布“上下文已准备”事件,此时上下文和环境已关联,但Bean尚未加载 listeners.contextPrepared(context); // 5. 打印启动日志 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 6. 获取BeanFactory并注册一些特殊的单例Bean ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 7. 设置是否允许Bean定义覆盖 if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 8. 延迟加载:如果设置了,将主配置类(@SpringBootApplication标注的类)注册为Bean定义 if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // 9. 加载主配置类(即我们的启动类)及其导入的配置 Set<Object> sources = getAllSources(); load(context, sources.toArray(new Object[0])); // 10. 发布“上下文已加载”事件,此时Bean定义已加载,但Bean尚未实例化 listeners.contextLoaded(context); }

关键步骤深度解析:

  • applyInitializers(context):这里会遍历执行在初始化阶段加载的所有ApplicationContextInitializer。这是一个非常重要的扩展点,允许我们在ApplicationContext刷新之前,对其做一些自定义配置。例如,你可以在这里动态注册Bean定义、修改环境属性、添加BeanFactoryPostProcessor等。
  • load(context, sources...):这个方法负责将我们的主启动类(primarySources)加载到BeanDefinitionRegistry中。对于注解配置的上下文,它内部会创建一个AnnotatedBeanDefinitionReader,将主类注册为一个Bean定义。主类上的@SpringBootApplication注解(它本身是一个组合注解,包含@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan)的元数据在这里被读取,但真正的扫描和自动装配逻辑是在后续的refresh()中触发的。

实操心得ApplicationContextInitializer的执行时机非常早,在Bean定义加载之前。这使它成为影响自动装配和Bean加载流程的绝佳位置。比如,你可以用它来动态激活某个Profile,或者向BeanFactory注册一个自定义的BeanDefinitionRegistryPostProcessor,从而干预Bean定义的扫描和注册过程。

4. 核心中的核心:应用上下文刷新refreshContext

refreshContext最终会调用AbstractApplicationContext.refresh()方法。这是Spring框架最核心、最复杂的方法,SpringBoot的自动装配、内嵌服务器启动等魔法都发生在这里。我们结合SpringBoot的特定实现(ServletWebServerApplicationContext)来解析。

// 这是Spring框架AbstractApplicationContext中的方法,SpringBoot的上下文重写了其中部分步骤 public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新:设置启动时间、激活状态,初始化属性源(空方法,子类可扩展) prepareRefresh(); // 2. 获取新的BeanFactory(通常刷新意味着销毁旧的,创建新的) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. 准备BeanFactory:配置BeanFactory的标准特性(类加载器、表达式解析器等) prepareBeanFactory(beanFactory); try { // 4. 后置处理BeanFactory(允许子类在Bean定义加载后,实例化前对其进行修改) postProcessBeanFactory(beanFactory); // 5. 调用BeanFactory的后置处理器(这是关键!) invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册Bean的后置处理器(用于拦截Bean的创建过程) registerBeanPostProcessors(beanFactory); // 7. 初始化消息源(国际化) initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 初始化其他特殊的Bean(由子类实现,SpringBoot在这里启动Web服务器!) onRefresh(); // 10. 注册监听器 registerListeners(); // 11. 实例化所有剩余的单例Bean(非懒加载的) finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新:发布上下文刷新完成事件 finishRefresh(); } catch (BeansException ex) { // ... 异常处理,销毁已创建的Bean ... throw ex; } finally { // 13. 重置Spring核心中的公共缓存 resetCommonCaches(); } } }

对于SpringBoot的ServletWebServerApplicationContext,它重写了第4步postProcessBeanFactory、第9步onRefresh和第12步finishRefresh。我们重点关注与Boot特性紧密相关的第5步和第9步。

4.1 自动装配的引擎:invokeBeanFactoryPostProcessors

这一步是SpringBoot自动装配原理的核心执行阶段。它会调用所有BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor

  1. 首先处理BeanDefinitionRegistryPostProcessor:这类处理器可以注册新的Bean定义。其中最关键的是ConfigurationClassPostProcessor,它负责处理@Configuration注解的类。
  2. ConfigurationClassPostProcessor的工作
    • 它会找到所有标注了@Configuration的Bean(我们的主启动类就是其中之一)。
    • 解析该类上的注解,特别是@ComponentScan@Import
    • @ComponentScan:根据指定的包路径扫描@Component,@Service,@Repository,@Controller等注解的类,并将它们注册为Bean定义。
    • @Import:导入其他配置类。@EnableAutoConfiguration本质上就是一个@Import(AutoConfigurationImportSelector.class)
  3. AutoConfigurationImportSelector的魔法:这个类是自动装配的“大脑”。它的selectImports方法会:
    • META-INF/spring.factories(或spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)文件中读取org.springframework.boot.autoconfigure.EnableAutoConfiguration键对应的所有自动配置类全名。
    • 通过一系列@Conditional注解(如@ConditionalOnClass,@ConditionalOnBean,@ConditionalOnProperty)对这些类进行过滤,只将符合条件的配置类导入到当前上下文中。
    • 这些自动配置类(例如DataSourceAutoConfiguration,WebMvcAutoConfiguration)内部使用@Bean方法定义了一系列预设好的Bean。这就是为什么我们引入一个starter依赖,相关功能就自动可用的原因。

排查技巧:如果你的自动配置没有生效,可以在这里入手调试。在ConfigurationClassPostProcessorAutoConfigurationImportSelector的相关方法上打断点,查看哪些配置类被扫描到、哪些被排除了,排除条件是什么。

4.2 启动内嵌Web服务器:onRefresh

ServletWebServerApplicationContext重写了onRefresh()方法。

protected void onRefresh() { super.onRefresh(); // 调用父类逻辑 try { createWebServer(); // 创建Web服务器 } catch (Throwable ex) { // ... } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { // 1. 从BeanFactory中获取WebServerFactory(通常是自动配置的TomcatServletWebServerFactory) ServletWebServerFactory factory = getWebServerFactory(); // 2. 使用工厂创建WebServer,同时会初始化ServletContext并注册DispatcherServlet this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { // ... } } initPropertySources(); // 初始化属性源 }

关键过程:

  1. getWebServerFactory()会从Spring容器中查找ServletWebServerFactory类型的Bean。TomcatServletWebServerFactory就是由ServletWebServerFactoryAutoConfiguration在满足条件时自动配置的。
  2. factory.getWebServer(getSelfInitializer())是创建服务器的核心。它会:
    • 实例化Tomcat/Jetty/Undertow等服务器对象。
    • 创建ServletContext
    • 调用传入的ServletContextInitializer(由getSelfInitializer()返回,它负责注册DispatcherServlet、Filters等)。
    • 应用我们在application.properties中配置的服务器属性(端口、上下文路径、SSL等)。

注意事项:此时Web服务器对象虽然创建了,但端口还未监听。真正的端口绑定和服务器启动,是在最后的finishRefresh()阶段,通过发布ContextRefreshedEvent事件,触发WebServerStartStopLifecycle这个Lifecycle接口的实现来完成的。这种设计将服务器的创建和启动分离,提供了更大的灵活性。

4.3 完成Bean初始化:finishBeanFactoryInitialization

这一步会实例化所有非懒加载的单例Bean。BeanFactory会遍历所有Bean定义,通过反射或工厂方法创建Bean实例,处理依赖注入(@Autowired,@Resource),应用BeanPostProcessor(如进行AOP代理),然后调用初始化方法(@PostConstruct,InitializingBean)。

常见问题定位:

  • Bean创建失败:如果在此阶段抛出BeanCreationException,通常是因为依赖注入失败(找不到依赖的Bean)、Bean的初始化方法抛出异常、或BeanPostProcessor处理出错。需要仔细查看异常堆栈,定位到具体的Bean和原因。
  • 循环依赖:Spring通过三级缓存机制解决了Setter注入和字段注入的循环依赖问题。但如果使用构造器注入且形成循环,Spring无法解决,会抛出BeanCurrentlyInCreationException。这是设计问题,需要重构代码打破循环。

5. 启动后流程与扩展点

5.1 执行Runner:callRunners

refresh()完成且listeners.started(context)事件发布后,SpringBoot会执行所有ApplicationRunnerCommandLineRunner类型的Bean。这两个接口都提供一个run方法,用于在应用完全启动后、开始接受请求前,执行一些特定的初始化任务。

private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }

使用场景与区别:

  • ApplicationRunnerrun方法接收的是封装好的ApplicationArguments对象,可以方便地获取解析后的命令行参数(如--key=value)。
  • CommandLineRunnerrun方法接收的是原始的字符串数组String... args
  • 两者都支持@Order注解来定义执行顺序。一个常见的坑:如果Runner中的任务耗时很长,会阻塞应用的启动完成,导致健康检查失败。对于异步任务,应考虑在Runner中启动一个异步线程或使用@EventListener监听ApplicationReadyEvent事件来执行。

5.2 发布就绪事件:listeners.running(context)

最后,SpringBoot会发布ApplicationReadyEvent。这个事件标志着应用已完全启动,内嵌服务器已开始监听端口,可以对外提供服务了。监听这个事件是执行启动后逻辑最安全的位置。

6. 常见问题排查与调试技巧实录

结合源码,我们可以系统化地定位启动期问题。

6.1 Bean定义加载失败

  • 症状:启动时报BeanDefinitionStoreExceptionConfigurationClassParseException
  • 排查
    1. 检查主配置类路径是否正确,是否被组件扫描到。
    2. 检查@Import@ComponentScan注解的使用是否有误,导致循环引用或扫描了不希望的包。
    3. ConfigurationClassPostProcessorprocessConfigBeanDefinitions方法处打断点,查看正在处理的配置类列表。

6.2 自动配置未生效

  • 症状:引入了starter,但相关的Bean没有创建。
  • 排查
    1. 检查依赖是否真的引入成功。
    2. AutoConfigurationImportSelectorgetCandidateConfigurationsfilter方法处打断点,查看候选配置类有哪些,以及被过滤掉的原因(通常是@Conditional条件不满足)。
    3. 开启调试日志:在application.properties中添加debug=true。启动时,SpringBoot会打印一份详细的自动配置报告,显示哪些配置类生效(Positive matches),哪些未生效及原因(Negative matches)。这是最实用的排查工具

6.3 端口被占用或服务器启动失败

  • 症状WebServer启动失败,报端口绑定异常或Servlet初始化错误。
  • 排查
    1. 检查ServletWebServerFactoryBean是否成功创建。可以在ServletWebServerFactoryAutoConfiguration类上打断点。
    2. 检查onRefresh()中的createWebServer()方法,看WebServer对象是否成功创建。
    3. 检查WebServerStartStopLifecyclestart()方法是否被调用。服务器是在finishRefresh()发布事件后异步启动的。

6.4 启动速度慢

  • 症状:应用启动时间过长。
  • 分析与优化
    1. 组件扫描路径过大@ComponentScan如果指定了过大的包范围(如根包com),会扫描很多不必要的类。应精确指定到业务模块包。
    2. 过多的自动配置类:不是所有starter的自动配置都是需要的。可以通过spring.autoconfigure.exclude属性排除不必要的自动配置。
    3. 懒加载:SpringBoot 2.2+支持全局懒加载(spring.main.lazy-initialization=true),或者使用@Lazy注解。但这可能会将启动时的问题延迟到第一次请求时。
    4. 使用Spring Boot DevTools:它在开发时通过重启类加载器来加速重启,但对冷启动帮助不大。
    5. 分析工具:使用SpringApplicationsetBannerMode(Banner.Mode.OFF)关闭Banner,或使用-verbose:classJVM参数查看类加载情况。更专业的可以使用AsyncProfilerJFR分析启动热点。

理解SpringBoot的启动源码,就像掌握了应用的“生命图谱”。当问题出现时,你不再是在黑暗中摸索,而是可以沿着这张图谱,快速定位到问题发生的具体阶段和组件。从环境准备、Bean定义加载、自动装配条件匹配,到Bean实例化、服务器启动,每一个环节都有其明确的职责和扩展点。这份理解不仅能帮你高效解决问题,更能让你在架构设计和框架定制时游刃有余。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 10:18:22

临沧网站建设ynyue如何从0到1打造企业专属线上名片,避开这些坑少走弯路

内容: 在这个互联网触角延伸到每一个角落的时代,对于咱们临沧本地的企业或者是个体工商户来说,拥有一个专业、美观且功能强大的网站,已经不再是什么“高大上”的遥不可及的梦想,而是一门实实在在的生存技能。你可以想象一下,当你的潜在客户在微信上聊完天,转身去百度搜索…

作者头像 李华
网站建设 2026/8/14 10:18:14

picasso2-okhttp3-downloader缓存机制详解:5MB到50MB的智能空间管理

picasso2-okhttp3-downloader缓存机制详解&#xff1a;5MB到50MB的智能空间管理 【免费下载链接】picasso2-okhttp3-downloader A OkHttp 3 downloader implementation for Picasso 2. 项目地址: https://gitcode.com/gh_mirrors/pi/picasso2-okhttp3-downloader picass…

作者头像 李华
网站建设 2026/8/14 10:17:35

漯河市万金镇网站建设:赋能本土乡村经济数字化转型的关键路径

在当今这个信息爆炸、数字经济蓬勃发展的时代,互联网早已不仅仅是连接世界的工具,它更像是我们生活的空气和水,无处不在,不可或缺。对于我们漯河市万金镇的乡亲们来说,过去提起“网站”或者“互联网”,第一反应可能还停留在那些高大上的科技公司或者是大企业的营销手段上…

作者头像 李华
网站建设 2026/8/14 10:17:03

上海网站建设021360企业官网怎么做?老站长掏心窝子告诉你避坑指南

在上海这个快节奏、高压力的魔都里,做企业的老板或者负责项目的负责人,每天睁眼就是各种琐事,业务拓展、团队管理、资金周转,哪一样都不让人省心。但在这 amidst 的喧嚣中,有一个东西往往是决定你能走多远、跑多快的关键基础设施,那就是你的网站。很多人听到“网站建设”…

作者头像 李华
网站建设 2026/8/14 10:17:01

深入解析锦江建设和交通局网站:官方信息发布与市民服务指南,助力城市建设透明化与高效化

在这个数字化浪潮席卷全球的今天,我们生活的每一个细微角落都在经历着深刻的变革。特别是对于那些关乎民生、关乎城市命脉的基础设施建设与交通管理领域,信息透明度的提升、服务效率的提高,已经成为衡量一个城市现代化治理能力的核心指标。而对于锦江区的居民以及关注成都东…

作者头像 李华