SpringBoot启动run方法分析

SpringBoot启动run方法分析

1.场景引入

在项目启动的时候,有时候我们需要在启动的时候,执行一些逻辑。

比如说,项目启动的时候,我想把一些热门商品的数据加载到缓存中去;

比如说,自定义了一个netty服务,我想在项目启动的时候,自动开启这个netty服务;

比如说,…

反正,这个场景大家肯定或多或少会碰到的吧。

下面就按照先后顺序依次介绍各种方式。

// 这个是示例初始化的方法
@Slf4j
public class InitCode {public static void startMethod( String str) {log.info("========================【{}】 就是这个~~~~~~~######", str);}
}

下面的六个方法参考了【https://blog.csdn.net/QIU176161650/article/details/118087254】这篇文章。其中的Servlet相关的,没有作细致分析,故打了**做标记。

①.实现ServletContextListener接口contextInitialized方法**

@Component
public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {InitCode.startMethod("ServletContextListener");}
}

②.@PostConstruct注解方式

这里顺便比较一下InitializingBean接口的方法。

@Component
public class NettyStarter implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {InitCode.startMethod("InitializingBean接口");}@PostConstructpublic void init() {InitCode.startMethod("@PostConstruct");}
}

③.实现ServletContextAware接口setServletContext 方法**

@Component
public class MyServletContextAware implements ServletContextAware {@Overridepublic void setServletContext(ServletContext servletContext) {InitCode.startMethod("ServletContextAware");}
}

④.@EventListener方式

@Component
public class MyEventListener {// 监听ContextRefreshedEvent事件@EventListenerpublic void onApplicationEvent(ContextRefreshedEvent event) {InitCode.startMethod("EventListener");}
}

⑤.实现ApplicationRunner接口run 方法

@Component
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {InitCode.startMethod("ApplicationRunner");}
}

⑥.实现CommandLineRunner接口run 方法

@Component
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {InitCode.startMethod("CommandLineRunner");}
}

⑦.顺序演示

在这里插入图片描述

通过debug,我们发现前四个打印的是在springBoot的refreshContext(context);之后的。后面两个Runner是callRunners(context, applicationArguments);之后打印的

2.run(args )

这要从SpringBoot的启动流程讲起了。在这篇文章【springboot】【https://www.cnblogs.com/jackjavacpp/p/18653391】中,对run的方法没有作分析,现在我们来看一下run方法。

Spring Boot的启动流程可划分为以下阶段:

  1. 环境准备:加载配置文件、初始化环境变量。
  2. 容器创建:创建ApplicationContext,加载Bean定义。
  3. Bean实例化:实例化Bean并完成依赖注入。
  4. 启动完成:执行后置任务,如缓存预热、定时任务启动。

每个阶段均提供扩展点供开发者介入自己的逻辑。

下面以SpringBoot2.7.18源码为例子

//Spring Boot应用启动入口方法,返回已初始化的应用上下文。
public ConfigurableApplicationContext run(String... args) {// 1.启动计时与引导上下文long startTime = System.nanoTime();DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// 2.事件监听器初始化SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 3.环境准备阶段ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 4.Banner打印与上下文创建Banner printedBanner = printBanner(environment);context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 5.上下文准备阶段prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 6.容器刷新(!!!最核心阶段!!!)refreshContext(context);// 7.启动后处理afterRefresh(context, applicationArguments);Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}listeners.started(context, timeTakenToStartup);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 8.应用就绪阶段Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady); // ready事件}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}

根据上面的源码结构大致总结出以下流程:

graph TD
A[run()启动] --> B[初始化引导上下文]
B --> C[配置Headless模式]
C --> D[初始化事件监听器]
D --> E[发布ApplicationStartingEvent]
E --> F[准备环境变量]
F --> G[打印Banner]
G --> H[创建应用上下文]
H --> I[准备上下文]
I --> J[刷新上下文]==================最核心
J --> K[发布ApplicationStartedEvent]
K --> L[执行ApplicationRunner]
L --> M[发布ApplicationReadyEvent]
M --> N[返回上下文]

下面对run方法中重要的部分作分析。

①事件监听器初始化

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);// getRunListeners(args)
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),this.applicationStartup);
}
// getSpringFactoriesInstances(xxx)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();//SpringFactoriesLoader.loadFactoryNames----Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;
}

通过spring.factories加载所有SpringApplicationRunListener实现类,发布ApplicationStartingEvent事件(最早触发的生命周期事件)

// 发布starting事件
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});
}

②容器刷新

refreshContext(context);private void refreshContext(ConfigurableApplicationContext context) {// Spring Boot 扩展点:应用上下文刷新前的处理if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}refresh(context);
}// 核心逻辑:调用 Spring Framework 的 refresh() 方法
protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();
}// 到抽象类AbstractApplicationContext.java这里来了
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");prepareRefresh(); // 1.准备刷新// 2. 获取BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();prepareBeanFactory(beanFactory);try {postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");invokeBeanFactoryPostProcessors(beanFactory);registerBeanPostProcessors(beanFactory);beanPostProcess.end();initMessageSource();   initApplicationEventMulticaster();onRefresh();registerListeners();finishBeanFactoryInitialization(beanFactory);finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {resetCommonCaches();contextRefresh.end();}}
}

refreshContext(context) 是 Spring Boot 启动过程中最核心的阶段之一,负责 Spring 容器的创建、配置和初始化。它直接调用了 Spring Framework 的 AbstractApplicationContext.refresh() 方法,由于我们demo引入了web依赖, Spring Boot 在此基础上进行了扩展(故会有内嵌 Web 容器的启动)。

1) 准备刷新
2) 获取BeanFactory
// AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 销毁旧 BeanFactory(如果存在)refreshBeanFactory();// 创建新的 BeanFactory(默认实现为 DefaultListableBeanFactory)return getBeanFactory();
}
3) 配置BeanFactory
// AbstractApplicationContext.java
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {beanFactory.setBeanClassLoader(getClassLoader());beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar());// 添加 BeanPostProcessor(如处理 @Autowired)beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);// 注册环境变量等内置 BeanbeanFactory.registerSingleton("environment", getEnvironment());
}

核心操作:注册内置 Bean(如 Environment)和基础 BeanPostProcessor

4) 后处理BeanFactory(扩展点)
// 子类可覆盖此方法(如 WebApplicationContext)
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
5) 执行BeanFactoryPostProcessor
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}
}
// PostProcessorRegistrationDelegate.java
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {// 处理 BeanDefinitionRegistryPostProcessor(优先级高)invokeBeanDefinitionRegistryPostProcessors(postProcessors, registry);// 处理 BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
}
  • 关键扩展点
    • BeanDefinitionRegistryPostProcessor:动态注册 Bean 定义(如 @Configuration 类的解析)。
    • BeanFactoryPostProcessor:修改 Bean 定义(如属性占位符替换 PropertySourcesPlaceholderConfigurer)。
  • Spring Boot 应用ConfigurationClassPostProcessor 在此阶段解析 @ComponentScan@Import(包括 @EnableAutoConfiguration)等注解。
6) 注册 BeanPostProcessor
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
// PostProcessorRegistrationDelegate.java
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {// 获取所有 BeanPostProcessor 并排序String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class);for (String ppName : postProcessorNames) {beanFactory.addBeanPostProcessor(beanFactory.getBean(ppName, BeanPostProcessor.class));}
}
  • 作用:将 BeanPostProcessor 实例注册到容器,后续 Bean 初始化时会经过这些处理器。
  • 关键处理器
    • AutowiredAnnotationBeanPostProcessor:处理 @Autowired@Value
    • CommonAnnotationBeanPostProcessor:处理 @PostConstruct@PreDestroy
    • AnnotationAwareAspectJAutoProxyCreator:AOP 代理生成。
7) 初始化事件广播器
// AbstractApplicationContext.java
protected void initApplicationEventMulticaster() {if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);} else {// 默认使用 SimpleApplicationEventMulticasterthis.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);}
}

初始化事件发布机制,用于后续发布 ContextRefreshedEvent 等事件。

8) 模板方法(onRefresh())–内嵌web容器
protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.
}//我们是 ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {super.onRefresh();try {// 创建并启动内嵌的 Web 服务器(如 Tomcat)createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");ServletWebServerFactory factory = getWebServerFactory();createWebServer.tag("factory", factory.getClass().toString());// ServletWebServerFactory 创建 WebServerthis.webServer = factory.getWebServer(getSelfInitializer());createWebServer.end();getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();
}// 经过调试发现运行到了StandardContext.java 不是spring框架范围了public boolean listenerStart() {...ServletContextEvent event = new ServletContextEvent(getServletContext());ServletContextEvent tldEvent = null;....ServletContextListener listener = (ServletContextListener) instance;try {fireContainerEvent("beforeContextInitialized", listener);if (noPluggabilityListeners.contains(listener)) {// ServletContextListener接口contextInitializedlistener.contextInitialized(tldEvent);} else {// ServletContextListener接口contextInitializedlistener.contextInitialized(event);}} ......}

在这里插入图片描述

  • Spring Boot 核心扩展:在此方法中启动内嵌的 Web 容器(如 Tomcat),这是 Spring Boot 与 Spring Framework 的重要区别。
  • 流程
    1. 通过 ServletWebServerFactory 创建 WebServer
    2. 初始化 DispatcherServlet 并注册到 Servlet 容器。
9) 注册监听器
// AbstractApplicationContext.java
protected void registerListeners() {// 添加静态指定的监听器for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// 注册 Bean 形式的监听器String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class);for (String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// 发布早期事件(如果有)publishEarlyApplicationEvents();
}

ApplicationListener 注册到事件广播器,确保后续事件能被监听。

10) 【重要】初始化所有单例 Bean
// AbstractApplicationContext.java
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching.beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes.beanFactory.freezeConfiguration();// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons();
}// DefaultListableBeanFactory.java
public void preInstantiateSingletons() throws BeansException {...Object singletonInstance = getSingleton(beanName);...
}
// DefaultSingletonBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}
  • 核心逻辑DefaultListableBeanFactory.preInstantiateSingletons() 方法会遍历所有 Bean 定义,实例化并初始化非懒加载的单例 Bean。
  • 关键过程
    • 调用 BeanPostProcessor 的前置处理(如 AOP 代理生成)。
    • 执行 @PostConstruct 方法。
    • 调用 InitializingBean.afterPropertiesSet()

getSingleton(String beanName, boolean allowEarlyReference) 如果allowEarlyReference是true的话,就用**三级缓存来解决循环依赖【见后续文章】**的问题。

debug源码得知,在DefaultListableBeanFactory::preInstantiateSingletons()中,调用了AbstractBeanFactory::getBean(String name)方法,接着往下是,AbstractBeanFactory::doGetBean(xx)方法,在该方法中有这样一个片段

// AbstractBeanFactory.java
// Create bean instance.
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args); //==========这里往下}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}// AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {....Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {... // 有如下片段// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}.....
}// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {// 调用 Aware 接口回调invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// 1.调用BeanPostProcessors // ---触发BeanPostProcessor的前置处理wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 2.调用初始化的方法/*在这个里面先((InitializingBean) bean).afterPropertiesSet();接着String initMethodName = mbd.getInitMethodName();if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {invokeCustomInitMethod(beanName, bean, mbd);}*/invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// 触发BeanPostProcessor的后置处理wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}

initializeBean 方法是 Spring 框架中 Bean 生命周期的核心方法之一,负责:

  1. 调用 Aware 接口回调(如 BeanNameAwareBeanFactoryAware 等)。
  2. 触发 BeanPostProcessor 的前置处理(postProcessBeforeInitialization)。
  3. 执行 Bean 的初始化方法(InitializingBean-->invokeInitMethods::afterPropertiesSet 或自定义 init-method)。
  4. 触发 BeanPostProcessor 的后置处理(postProcessAfterInitialization)。

对于场景引入的例子②中,发现@PostConstruct注解标注的方法,在applyBeanPostProcessorsBeforeInitialization()调用的,也就是在bean的InitializingBean之前就执行了。也就是说PostConstruct 注解先执行InitializingBean 接口方法 次之。
在这里插入图片描述
在这里插入图片描述

// 由上面可见,我们可以推断执行顺序如下:
Bean本身的构造函数
BeanPostProcessor的postProcessBeforeInitialization方法
类中添加了注解@PostConstruct 的方法 【上图中的CommonAnnotationBeanPostProcessorInitializingBean的afterPropertiesSet方法
initMethod
BeanPostProcessor的postProcessAftrInitialization方法
11) 完成刷新
protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).clearResourceCaches();// Initialize lifecycle processor for this context.initLifecycleProcessor();// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();// Publish the final event.// 发布一个ContextRefreshed的事件-----------publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.if (!NativeDetector.inNativeImage()) {LiveBeansView.registerApplicationContext(this);}
}

可以看到,在容器刷新完成之后,会发布一个ContextRefreshed的事件,所以下面的监听器会监听到。

// 监听ContextRefreshedEvent事件
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {InitCode.startMethod("EventListener");
}

从这里可以看出来,我们框架使用者可以扩展的位置如下:

  1. BeanFactoryPostProcessor:动态修改 Bean 定义。
  2. BeanPostProcessor:干预 Bean 的初始化过程(如 AOP)。
  3. ApplicationListener:监听容器刷新完成事件。
  4. 自定义 ServletWebServerFactory:修改内嵌服务器配置。

容器刷新这一小节目前就分析到这里了。

③callRunners()

执行所有ApplicationRunnerCommandLineRunner的实现类。执行顺序:通过@Order注解或Ordered接口控制。

//SpringApplication.java
private void callRunners(ApplicationContext context, ApplicationArguments args) {context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}});
}// 执行run方法了,就是我们重写的run
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {try {(runner).run(args);}catch (Exception ex) {throw new IllegalStateException("Failed to execute ApplicationRunner", ex);}
}private void callRunner(CommandLineRunner runner, ApplicationArguments args) {try {(runner).run(args.getSourceArgs());}catch (Exception ex) {throw new IllegalStateException("Failed to execute CommandLineRunner", ex);}
}

3.总结

经过第二大节的整体分析,我们大致可以知道

  • 在容器刷新之前,由于是Servlet的Application,故由模板方法运行创建Servlet容器,ServletContextListener接口contextInitialized会先执行。

  • 调用构造方法创建bean对象,实例化

  • BeanPostProcessor :: postProcessBeforeInitialization()

  • @PostConstruct ---- 【CommonAnnotationBeanPostProcessor】

  • InitializingBean接口的afterPropertiesSet()

  • 自定义的initMethod

  • BeanPostProcessor :: postProcessAfterInitialization()

  • Runners

对于Spring的容器创建、bean的创建、以及事件等内容的详细分析,请见后续文章。本文通过对SpringBoot一个使用场景,在参考了别人的方法之后,想要探究其原理,对其启动过程作了一个大致的分析。

4.思考题

对上面作了大致分析后,其实还有其他方法,可以在需要在启动的时候,执行一些逻辑。

监听 ApplicationReadyEvent 事件.

@Component
public class StartupListener {@EventListener(ApplicationReadyEvent.class)public void onApplicationReady() {InitCode.startMethod("ApplicationReadyEventListener");}
}

这个在什么阶段执行呢?

第二个问题?上面的4种方法【去掉**的】 + 思考题中的方法 = 5种方法。他们各自有什么优劣呢?

end. 参考

  1. https://blog.csdn.net/QIU176161650/article/details/118087254
  2. https://blog.csdn.net/weixin_53287520/article/details/139484810
  3. https://blog.csdn.net/yerenyuan_pku/article/details/110442093
  4. https://blog.csdn.net/m0_61933976/article/details/128697003

示例代码仓库见 【https://gitee.com/quercus-sp204/sourcecode-and-demos】中的 “netty-sp” 模块部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux信号——信号的处理(3)

信号是什么时候被处理&#xff1f; 进程从内核态&#xff0c;切换到用户态的时候&#xff0c;信号会被检测处理。 内核态&#xff1a;操作系统的状态&#xff0c;权限级别高 用户态&#xff1a;你自己的状态 内核态和用户态 进程地址空间第三次 所谓的系统调用本质其实是一堆…

MySQL篇(四)事务相关知识详解

MySQL篇(四&#xff09;事务相关知识详解 MySQL篇(四&#xff09;事务相关知识详解一、事务的特性&#xff08;ACID&#xff09;原子性&#xff08;Atomicity&#xff09;一致性&#xff08;Consistency&#xff09;隔离性&#xff08;Isolation&#xff09;持久性&#xff08;…

SpringBoot定时任务深度优化指南

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 SpringBoot定时任务深度优化指南 引言 在分布式系统架构中&#xff0c;定时任务是实现业务逻辑自动化的重要组件。SpringBoot通过Scheduled注解提供了便捷的…

MySQL表结构导出(Excel)

目录 一、java实现MySQL表结构导出&#xff08;Excel&#xff09; 二、python实现MySQL表结构导出&#xff08;Excel&#xff09; 又到了写毕设的时候了&#xff0c;计算机专业在写论文第四章系统设计的时候肯定会遇到和我一样的难题——要在论文中将数据库的表结构以表格形式…

Android使用OpenGL和MediaCodec渲染视频

目录 一&#xff0c;借助MediaCodec封装解码工具类VideoCodec 二&#xff0c;使用OpenGl绘制视频封装SoulFilter 一&#xff0c;借助MediaCodec封装解码工具类VideoCodec /*** 解码工具类* 解码完成后的数据 通过 ISurface 回调出去*/ public class VideoCodec {private ISu…

day39——输入操作:多值输入

数组输入&#xff1a; int main() {//***** 1、多值输入&#xff08;C&#xff09;/*输入&#xff1a;3 --> 3个值5 4 9*/int n;cin >> n; //输入个数const int MAX_SIZE 0xFFFF;//限定最大个数int a[MAX_SIZE];for (int i 0; i < n; i) {//用 n 作控制输入…

第九课:LoRA模型的原理及应用

文章目录 Part.01 3种LoRA的使用方式Part.02 5种LoRA的应用方向Part.01 3种LoRA的使用方式 LoRA能够在家用级设备上训练,实现对Checkpoint在某些方面的微调使用Lora的三种方式:放置Lora模型到目录中,然后作为提示词的一部分输入。点击生成按钮下面的“画”,然后打开Additio…

Cortex-M3 NVIC可以控制异常向量表的哪些部分

Cortex-M3 的 NVIC(嵌套向量中断控制器)不直接控制整个异常向量表,但可以管理向量表中与中断相关的部分行为。以下是 NVIC 对异常向量表的具体控制范围和相关机制: 1. NVIC 直接控制的部分 NVIC 主要管理 外部中断(IRQ) 和部分 系统异常 的行为,但对向量表本身的存储位…

双向链表增删改查的模拟实现

本章目标 0.双向链表的基本结构 1.双向链表的初始化 2.头插尾插 3.头删尾删 4.查找与打印 5.在指定位置之前插入数据/在指定位置之后插入数据 6.在指定位置之前删除数据/在指定位置之后删除数据 7.销毁链表 0.双向链表的基本结构 本章所实现的双向链表是双向循环带头链表,是…

实战交易策略 篇十四:江南神鹰捕捉热点和熊市生存交易策略

文章目录 系列文章捕捉热点是股市最大的掘金术市场温度不低于50是热点产生的必要条件题材的大小和新颖程度决定热点的持续时间和涨幅炒作热点的3个阶段捕捉热点的方法与步骤操作实战案例熊市生存术“熊市最好的做法是离开股市”的说法是一句空话熊市盈利模式:不轻言底部,超跌…

Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV

Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV Author: Once Day Date: 2025年4月4日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实…

解码 __iter__ 和 itertools.islice - 迭代的艺术

文章目录 前言一、`_iter__`:自定义迭代的钥匙1.1 什么是 __iter__?1.2 基本用法1.3 高级用法:独立迭代器二、itertools.islice:迭代切片的利器2.1 什么是 itertools.islice?2.2 基本用法2.3 处理无限序列2.4 实际应用三、`__iter__` 与 `islice` 的结合六、为什么需要它们…

使用VSCode编写C#程序

目录 一、环境搭建&#xff1a;构建高效开发基础1. 安装VSCode2. 配置.NET SDK3. 安装核心扩展 二、项目开发全流程1. 创建项目2. 代码编辑技巧3. 调试配置4. 高级调试技巧5. 编译与运行 三、常见问题解决指南1. 项目加载失败2. IntelliSense失效3. 代码格式化4. 典型编译错误&…

日本汽车规模性经济计划失败,日产三大品牌的合并合作共赢,还是绝地求生?本田与日产合并确认失败,将成为世界第三大汽车集团愿景失败

本田与日产(含三菱汽车)的合并计划最终因核心矛盾无法调和而宣告失败,这一事件揭示了传统车企在行业变革期的深层困境。以下从合并动机、失败原因、本质判断及未来影响等方面综合分析: 一、合并的初衷:生存压力主导的被动策略 市场危机与财务困境 中国市场溃败:日系品牌在…

AutoCAD2026中文版下载安装教程

AutoCAD是一款由Autodesk公司开发的计算机辅助设计软件&#xff0c;被广泛应用于建筑设计、机械设计、电气设计、土木工程、装饰装潢等多个领域。AutoCAD2026中文版在原有的基础上进行了多项改进和优化&#xff0c;为用户提供了更为高效、便捷的绘图和设计体验。这里我给大家分…

Latex语法入门之数学公式

Latex是一种高质量的排版系统&#xff0c;尤其擅长于数学公式的排版。本文我将带大家深入了解Latex在数学公式排版中的应用。从基础的数学符号到复杂的公式布局&#xff0c;我们都会一一讲解&#xff0c;通过本文的学习&#xff0c;你将能够轻松编写出清晰、美观的数学公式&…

洛谷 P3214 [HNOI2011] 卡农

题目传送门 前言 再次败在 d p dp dp 手下&#xff0c;但是数据范围这么小应该是可以看出是 d p dp dp 的&#xff08;毕竟对于其他组合数的问题数据范围都是 1 0 9 10^9 109 起步&#xff09;。 思路 题意简化 现有 1 , 2 , 3 , . . . , n − 1 , n 1, 2, 3, ... , n -…

【年份数据类型及使用】

在数据分析中,年份的处理需要根据具体场景选择合适的数据类型,以确保后续分析的准确性和效率。以下是常见的年份数据类型及使用场景: 1. 数值类型(整数或浮点数) 适用场景: 仅需存储年份数值(如 2020, 2023),无需进行日期计算。需要将年份作为连续变量参与数学运算(如…

详解七大排序

目录 一.直接插入排序 &#xff08;1&#xff09;基本思想 &#xff08;2&#xff09;算法步骤 &#xff08;3&#xff09;代码实现 &#xff08;4&#xff09;算法特性 &#xff08;5&#xff09;算法优化 &#xff08;6&#xff09;示例演示 二.希尔排序 &#xff08…

YOLOv12 训练从这里开始:LabelImg 标注数据集

视频讲解&#xff1a; YOLOv12 训练从这里开始&#xff1a;LabelImg 标注数据集 labelimg https://github.com/tzutalin/labelImg sudo apt-get install pyqt5-dev-tools pip3 install lxml git clone https://github.com/tzutalin/labelImg.git cd labelImg 开始编译 make…