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.pswp.cn/bicheng/75793.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75793.shtml
英文地址,請注明出處:http://en.pswp.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…