当前位置: 首页 > news >正文

SpringBoot系列 启动流程

文章目录

  • SpringApplication
    • SpringApplication#run
  • 启动流程
    • BootstrapContext
    • SpringApplicationRunListeners
    • prepareEnvironment
      • configureEnvironment
        • configurePropertySources
        • configureProfiles
    • 上下文初始化
      • prepareContext
      • refreshContext
        • prepareRefresh
        • obtainFreshBeanFactory
        • prepareBeanFactory
        • postProcessBeanFactory
        • invokeBeanFactoryPostProcessors
        • registerBeanPostProcessors
        • initMessageSource
        • initApplicationEventMulticaster
        • onRefresh
        • registerListeners
        • finishBeanFactoryInitialization
        • finishRefresh
        • destroyBeans
        • cancelRefresh
    • afterRefresh
    • callRunners


以下面的启动类为例:

@SpringBootApplication
public class Main {public static void main(String[] args) {SpringApplication.run(Main.class, args);}
}

下面逐步分析SpringBoot的启动流程

SpringApplication

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.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication#run

SpringApplication#run(java.lang.String…)方法包含了全部SpringBoot应用启动的流程,启动完毕之后会返回最终的应用上下文ConfigurableApplicationContext,通过这个上下文我们可以获取Spring中的所有配置,Bean信息等

public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();// 创建 BootstrapContextDefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// 加载spring.factories文件中的SpringApplicationRunListener实例SpringApplicationRunListeners listeners = getRunListeners(args);// 执行starting回调listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 打印Banner信息Banner printedBanner = printBanner(environment);// 使用ApplicationContextFactory创建ApplicationContext对象context = createApplicationContext();// 将配置的ApplicationStartup加到ApplicationContext中context.setApplicationStartup(this.applicationStartup);// 初始化应用上下文prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 刷新应用上下文refreshContext(context);// 应用上下文刷新完毕之后的回调afterRefresh(context, applicationArguments);Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// ApplicationRunListener的started回调listeners.started(context, timeTakenToStartup);callRunners(context, applicationArguments);} catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady);} catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}

启动流程

BootstrapContext

BootstrapContext是一个在Spring Boot和Spring Cloud中使用的引导上下文,它在应用程序启动期间以及环境后处理过程中扮演着重要角色。以下是BootstrapContext的主要作用:

  1. 早期启动阶段的作用
    延迟访问高成本单例:在应用程序的早期启动阶段,BootstrapContext提供了对可能创建成本高昂的单例的延迟访问。这意味着,只有在需要时,这些单例才会被创建和初始化,从而优化了启动时间和资源使用。
    共享单例:在ApplicationContext可用之前,BootstrapContext允许共享必要的单例对象。这对于需要在早期阶段就进行配置或初始化的组件特别有用。
  2. 配置加载与优先级
    加载外部配置:BootstrapContext负责加载外部资源的配置属性,并解释本地外部配置文件(如bootstrap.yml或bootstrap.properties)中的属性。这些配置属性通常用于设置应用程序的基础设施,如服务发现、配置中心等。
    配置优先级:默认情况下,bootstrap属性具有高优先级,并且无法被本地配置覆盖。这确保了基础设施相关的配置不会被意外地覆盖或修改。
  3. 应用程序上下文层级
    作为Parent Context:在Spring Cloud中,BootstrapContext通常作为主应用程序上下文(main ApplicationContext)的父上下文。这意味着主上下文可以继承BootstrapContext中的属性和配置资源。
    属性资源继承:主应用程序上下文可以从BootstrapContext中继承额外的属性资源和配置文件,从而简化了配置管理。
  4. 自定义与扩展
    自定义配置:通过配置/META-INF/spring.factories文件中的org.springframework.cloud.bootstrap.BootstrapConfiguration值,可以自定义BootstrapContext的行为和配置。这为开发人员提供了极大的灵活性和控制力。
    添加属性资源:可以通过向BootstrapContext中添加PropertySourceLocator类型的bean来添加额外的属性资源。这允许开发人员从自定义的来源加载配置属性。
  5. 生命周期管理
    监听启动事件:开发人员可以通过实现ApplicationListener接口来监听BootstrapContext的启动事件,从而执行自定义的初始化操作或配置任务。
    关闭监听:在应用程序上下文关闭时,BootstrapContext也提供了相应的机制来执行清理操作或释放资源。

综上所述,BootstrapContext在Spring Boot和Spring Cloud应用程序中起到了至关重要的作用,它优化了启动过程、管理了配置优先级、支持了应用程序上下文的层级结构、提供了自定义和扩展的能力,并管理了生命周期事件。

private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();// List<BootstrapRegistryInitializer> bootstrapRegistryInitializersthis.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));return bootstrapContext;
}

这个bootstrapRegistryInitializers就是在SpringApplication的构造方法里进行加载的
在这里插入图片描述

SpringApplicationRunListeners

SpringApplicationRunListeners负责在Spring Boot应用程序启动的不同阶段广播消息,并传递给实现了ApplicationListener接口的监听器类,以执行自定义逻辑。

SpringApplicationRunListener是一个监听SpringApplication中run方法的接口,它在项目启动过程的各个阶段进行事件的发布。通过实现这个接口,开发者可以在应用程序启动的不同生命周期阶段插入自定义的逻辑。

SpringApplicationRunListeners类内部维护了一个SpringApplicationRunListener的集合,提供了与SpringApplicationRunListener接口一致的方法,方便统一遍历调用所有SpringApplicationRunListener。
实例化:
SpringApplicationRunListeners的实例化发生在SpringApplication的run方法中。它通过调用getRunListeners方法,读取META-INF/spring.factories文件,获取SpringApplicationRunListener的子类(如EventPublishingRunListener)的Class信息,并通过反射API创建其实例。

生命周期方法
SpringApplicationRunListeners接口定义了多个回调方法,这些方法在Spring Boot应用程序启动的不同阶段被触发:

  1. starting():在Spring Boot应用程序开始启动时触发。此时,可以执行一些自定义的初始化操作。
  2. environmentPrepared(ConfigurableEnvironment environment):在环境准备好后触发。此时,应用程序上下文尚未创建,但环境已经配置好,允许监听器在此阶段对环境进行进一步的自定义配置。
  3. contextPrepared(ConfigurableApplicationContext context):在应用程序上下文准备好后触发。此时,应用程序上下文已经创建并准备好,但尚未加载任何配置。
  4. contextLoaded(ConfigurableApplicationContext context):在应用程序上下文加载完成后触发。此时,应用程序上下文已经被刷新,但尚未启动。
  5. started(ConfigurableApplicationContext context):在应用程序上下文刷新且应用启动时触发。此时,可以在CommandLineRunner和ApplicationRunners唤醒前执行一些自定义操作。
  6. running(ConfigurableApplicationContext context):在应用程序已经启动并准备好后触发。此时,代表程序已经准备好处理请求。
  7. failed(ConfigurableApplicationContext context, Throwable exception):在应用程序启动失败时触发。此时,可以执行一些自定义的失败处理逻辑。

实现与应用
开发者可以通过实现SpringApplicationRunListeners接口,并在META-INF/spring.factories文件中注册自定义的SpringApplicationRunListener实现类,以在应用程序启动的不同阶段插入自定义逻辑。

例如,可以实现一个自定义的SpringApplicationRunListener,在starting()方法中打印一条消息,以指示应用程序已经开始启动。或者,在environmentPrepared()方法中,可以根据环境变量或配置文件中的设置来动态调整应用程序的配置。

注意事项

  1. 顺序性:由于SpringApplicationRunListeners的回调方法是在应用程序启动的不同阶段被触发的,因此它们的执行顺序是固定的。开发者需要确保在正确的阶段执行自定义逻辑。
  2. 异常处理:在实现SpringApplicationRunListeners的回调方法时,需要注意异常处理。如果某个方法抛出异常,可能会导致应用程序启动失败。因此,建议在方法中添加适当的异常处理逻辑。

综上所述,SpringApplicationRunListeners是Spring Boot框架中用于监听应用程序启动过程的重要组件。通过实现和注册自定义的SpringApplicationRunListener实现类,开发者可以在应用程序启动的不同阶段插入自定义逻辑,从而实现更灵活的应用程序配置和管理。

prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// 创建ConfigurableEnvironmentConfigurableEnvironment environment = getOrCreateEnvironment();// 配置ConfigurableEnvironmentconfigureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);// 调用SpringApplicationRunListener的environmentPrepared回调listeners.environmentPrepared(bootstrapContext, 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;
}

org.springframework.boot.ApplicationContextFactory是springboot2.4.0新加的一个策略接口,它定义了如何创建 ApplicationContext 实例的工厂方法。

configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {if (this.addConversionService) {environment.setConversionService(new ApplicationConversionService());}configurePropertySources(environment, args);configureProfiles(environment, args);
}
configurePropertySources

org.springframework.core.env.PropertySources 是 Spring 框架中用于管理和保存属性配置的关键接口。它是多个 PropertySource 的集合,为应用程序提供了一个统一的配置属性源访问接口。以下是对 PropertySources 的详细解释:

PropertySources 接口扩展了 Iterable<PropertySource<?>>,意味着它可以遍历其中的 PropertySource 元素。每个 PropertySource 都代表了一个配置源,如系统环境变量、命令行参数、属性文件等。PropertySources 通过提供统一的方法来访问这些配置源中的属性。

虽然 PropertySources 本身是一个接口,没有直接定义获取属性的方法,但它通过其内部的 PropertySource 元素来间接提供这些功能。每个 PropertySource 都有一个 getProperty(String name) 方法,用于根据属性名获取属性值。此外,PropertySources 的实现类(如 MutablePropertySources)通常会提供一些方法来添加、删除或修改 PropertySource 元素。

属性源的加载顺序与优先级
在 Spring 应用程序中,PropertySources 中的属性源通常按照特定的顺序加载,并且具有不同的优先级。例如,Spring Boot 在启动时,会通过 PropertySourcesPlaceholderConfigurer 装载 PropertySources,并往其中添加 environmentProperties 和 localProperties 等属性源。这些属性源的加载顺序和优先级决定了当存在多个相同名称的属性时,哪个属性值会被优先使用。

PropertySources 在 Spring 应用程序中广泛应用于配置属性的管理和访问。以下是一些常见的应用场景:

  1. 多配置文件支持:Spring Boot 支持通过 @PropertySource 或 @PropertySources 注解来加载多个配置文件,这些配置文件会被添加到 PropertySources 中。
  2. 系统环境变量与命令行参数:Spring 允许将系统环境变量和命令行参数作为属性源添加到 PropertySources 中,从而方便地在不同环境下配置应用程序。
  3. 自定义属性源:开发者可以通过实现 PropertySource 接口来创建自定义的属性源,并将其添加到 PropertySources 中,以满足特定的配置需求。

五、注意事项

  1. 属性名的唯一性:在 PropertySources 中,每个属性名应该是唯一的。如果存在多个相同名称的属性,那么优先级较高的属性源中的属性值将被优先使用。
  2. 属性值的类型转换:在获取属性值时,可能需要进行类型转换。Spring 提供了丰富的类型转换机制来支持这一需求。
  3. 安全性:在处理敏感配置信息时,需要注意保护这些信息的安全性,避免泄露给未经授权的用户或系统。

综上所述,org.springframework.core.env.PropertySources 是 Spring 框架中用于管理和保存属性配置的重要接口。通过理解和使用它,开发者可以更加灵活地配置和管理 Spring 应用程序中的属性信息。

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {// 所有配置属性MutablePropertySources sources = environment.getPropertySources();// 添加默认属性if (!CollectionUtils.isEmpty(this.defaultProperties)) {  // 1// 添加到末尾DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);}// 命令行的配置属性处理,即是否添加main方法的参数if (this.addCommandLineProperties && args.length > 0) {String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;if (sources.contains(name)) {PropertySource<?> source = sources.get(name);CompositePropertySource composite = new CompositePropertySource(name);composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));composite.addPropertySource(source);sources.replace(name, composite);} else {// 添加到末尾sources.addFirst(new SimpleCommandLinePropertySource(args));}}
}
  1. 可以通过SpringApplication#setDefaultProperties()进行设置
configureProfiles

上下文初始化

prepareContext

用于ApplicationContext的初始化操作,加载上下文的组件,执行一些生命周期回调等等

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);// 设置ApplicationContext的相关组件postProcessApplicationContext(context);// 执行ApplicationContextInitializer#initialize回调applyInitializers(context);// context准备完毕: 执行SpringApplicationRunListener#contextPrepared回调listeners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 添加PropertySourceOrderingBeanFactoryPostProcessor这个BeanFactoryPostProcessorcontext.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 加载SpringApplication的sources对象数组的Bean信息load(context, sources.toArray(new Object[0]));// context加载完毕: 执行SpringApplicationRunListener#contextLoaded回调listeners.contextLoaded(context);
}

refreshContext

加载或刷新配置的持久化,这些配置可以来自 XML 文件、properties 文件、关心数据库等等。因为该方法是一个启动方法,如果失败了,它应该销毁已经创建的单例,以避免出现悬挂资源(dangling resources),换句话说,要么实例化所有的单例,要么一个也不实例化。

void refresh() throws BeansException, IllegalStateException;

AbstractApplicationContext 是 ApplicationContext 的一个抽象实现类。和普通的 BeanFactory 不同的是,ApplicationContext 可以检测到它的内部 BeanFactory 中定义的特殊 Bean:自动完成 BeanFactoryPostProcessor、BeanPostProcessor、ApplicationListener 的注册。

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();} catch (BeansException ex) {// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;} finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}}
}
prepareRefresh

这里是做一些准备工作,包括设置开始时间、active 状态、PropertySources 的初始化等等

protected void prepareRefresh() {// Switch to active.this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);// ...// Initialize any placeholder property sources in the context environment.initPropertySources();// Validate that all properties marked as required are resolvable:// see ConfigurablePropertyResolver#setRequiredPropertiesgetEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners...if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);} else {// Reset local application listeners to pre-refresh state.this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...this.earlyApplicationEvents = new LinkedHashSet<>();
}
obtainFreshBeanFactory

告诉当前ApplicationContext的子类刷新内部的 BeanFactory,然后返回刷新后的 BeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();return getBeanFactory();
}

refreshBeanFactory是
org.springframework.context.support.AbstractApplicationContext的一个抽象方法,需要子类实现,直接的子类有2个,而refreshBeanFactory方法在两个里面都有实现
在这里插入图片描述
一般来说基于WEB的系统的ApplicationContext的类型是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,而它是AbstractRefreshableApplicationContext这个分支下的,所以我们先看它

@Override
protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {// 创建BeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);// 加载所有Bean定义信息,这里会根据不同的配置类型进行加载loadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;} catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}
}

loadBeanDefinitions将bean定义加载到给定的BeanFactory中,通常是通过委托给一个或多个BeanDefinitionReader完成的。
在这里插入图片描述
BeanDefinitionReader的继承体系如下:分别对应BeanDefinition存放的不同位置
在这里插入图片描述

prepareBeanFactory

配置 BeanFactory ,例如设置 context 的 class loader 和 post processor

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.beanFactory.setBeanClassLoader(getClassLoader());if (!shouldIgnoreSpel) {beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));}beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// Register early post-processor for detecting inner beans as ApplicationListeners.beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());}
}
postProcessBeanFactory

对 BeanFactory 进行后处理,默认的实现为空,交给子类去扩展。可以对 beanFactory 进行想要的操作。比如 GenericWebApplicationContext 的实现如下:

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {if (this.servletContext != null) {beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));beanFactory.ignoreDependencyInterface(ServletContextAware.class);}WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
}
invokeBeanFactoryPostProcessors

调用 BeanFactoryPostProcessor

registerBeanPostProcessors

实例化并且调用所有已注册的 BeanPostProcessor

initMessageSource

org.springframework.context.MessageSource是Spring框架中用于解析消息的顶层接口,它支持参数化和国际化信息。

MessageSource接口的主要功能是从资源文件中检索和解析消息。这些资源文件通常包含键值对,其中键是消息的标识符,值是实际要显示的字符串。通过MessageSource,开发者可以在代码中通过键来获取对应的消息值,从而实现了消息与代码的分离,提高了代码的可读性和可维护性。

MessageSource接口提供了多个重载的getMessage方法,用于获取消息。以下是几个常用的方法:

  1. String getMessage(String code, Object[] args, String defaultMessage, Locale locale):根据指定的消息代码(code)、参数数组(args)、默认消息(defaultMessage)和语言环境(locale)来获取消息。如果找不到指定语言环境的消息,则使用默认消息。
  2. String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException:与上一个方法类似,但无法指定默认消息。如果找不到消息,会抛出NoSuchMessageException异常。
  3. String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException:使用MessageSourceResolvable对象来封装消息代码、参数和默认消息,然后根据指定的语言环境来获取消息。如果找不到消息,也会抛出NoSuchMessageException异常。
initApplicationEventMulticaster

初始化上下文中的事件机制

onRefresh

留给子类去实现,用于初始化其他特殊的 bean。比如 Spring Boot 中ServletWebServerApplicationContext 就是在这一步去创建的 web server

registerListeners
protected void registerListeners() {// Register statically specified listeners first.for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let post-processors apply to them!String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);for (String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// Publish early application events now that we finally have a multicaster...Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}
}
finishBeanFactoryInitialization

完成当前 context 的 beanFactory 的初始化,初始化剩下所有的不是懒加载的单例Bean对象

Bean的初始化都是通过BeanFactory#getBean进行的,也就是常说的将一个类型注册单例对象到容器并进行初始化

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.// CONVERSION_SERVICE_BEAN_NAME => conversionServiceif (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no BeanFactoryPostProcessor// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.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.// 项目中自定义的所有Bean就是在此时进行实例化beanFactory.preInstantiateSingletons();
}

ConfigurableListableBeanFactory定义的方法,在DefaultListableBeanFactory中实现:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

@Override
public void preInstantiateSingletons() throws BeansException {// ...// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {// 先获取BeanDefinition信息RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 不是抽象类,并且是单例,且非懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 判断是否FactoryBean实例if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());} else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}} else {getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());} else {smartSingleton.afterSingletonsInstantiated();}smartInitialize.end();}}
}

如果是SmartFactoryBean,则会根据一些条件判断是否要立即进行初始化,SmartFactoryBean#isEagerInit方法

如果是普通的FactoryBean,只有FactoryBean这个工厂Bean对象会被 Spring 容器初始化了,我们的FactoryBean对应的那个对象其实还没被初始化,要在第一次使用的时候,才会被初始化。

finishRefresh

完成当前 context 的 refresh 操作,调用 LifecycleProcessor 的 onRefresh 方法,发布 ContextRefreshedEvent 事件

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.publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.if (!NativeDetector.inNativeImage()) {LiveBeansView.registerApplicationContext(this);}
}
destroyBeans

内部调用的是
org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons,用于出错时清除所有的单例对象

@Override
public void destroySingletons() {super.destroySingletons();updateManualSingletonNames(Set::clear, set -> !set.isEmpty());clearByTypeCache();
}// DefaultSingletonBeanRegistry
public void destroySingletons() {synchronized (this.singletonObjects) {this.singletonsCurrentlyInDestruction = true;}String[] disposableBeanNames;synchronized (this.disposableBeans) {disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());}for (int i = disposableBeanNames.length - 1; i >= 0; i--) {destroySingleton(disposableBeanNames[i]);}this.containedBeanMap.clear();this.dependentBeanMap.clear();this.dependenciesForBeanMap.clear();clearSingletonCache();
}// 清除三级缓存
protected void clearSingletonCache() {synchronized (this.singletonObjects) {this.singletonObjects.clear();this.singletonFactories.clear();this.earlySingletonObjects.clear();this.registeredSingletons.clear();this.singletonsCurrentlyInDestruction = false;}
}
cancelRefresh

由于出现异常,需要取消上下文刷新操作

afterRefresh

afterRefresh是SpringApplication留给子类继承实现的一个方法,相当于一个回调

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

callRunners

这里会调用另外2个扩展点:ApplicationRunner和CommandLineRunner,此时上下文已经刷新完毕

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);}});
}

相关文章:

SpringBoot系列 启动流程

文章目录 SpringApplicationSpringApplication#run 启动流程BootstrapContextSpringApplicationRunListenersprepareEnvironmentconfigureEnvironmentconfigurePropertySourcesconfigureProfiles 上下文初始化prepareContextrefreshContextprepareRefreshobtainFreshBeanFactor…...

vgg19提取特征

一般来说&#xff0c;大家使用VGG16&#xff0c;用的是第四列的网络架构&#xff0c;而使用VGG19&#xff0c;使用的就是第六列的网络架构。 使用vgg进行提取特征&#xff0c;在这个项目中&#xff0c;使用的就是每一块卷积层的第一层。 import torch.nn as nn from torchvis…...

Qt 中的 QChartView

深入理解 Qt 的 QChartView&#xff1a;图表展示与交互 QChartView 是 Qt Charts 模块中的一个核心类&#xff0c;它用于在 Qt 应用程序中显示图表&#xff0c;并支持多种用户交互方式。它继承自 QGraphicsView&#xff0c;通过封装 QChart&#xff0c;为用户提供了强大的图表…...

cheese安卓版纯本地离线文字识别插件

目的 cheese自动化平台是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。可以采用Vscode、IDEA编写&#xff0c;支持Java、Python、nodejs、GO、Rust、Lua。cheese也包含图色功能&#xff0c;识别…...

【C++】多肽

目录 一 多肽定义 1. 多肽的构成条件 1 例一 2 例二 2. 虚函数 3. 虚函数重写的两个意外 1 协变 2 析构函数的重写 二 关键字override 和 final 1. final 2.override 三 三重对比 1. 练习 四 多肽的原理 1. 多肽调用和普通调用 2.虚函数表 3. 分析 4. 原理 …...

Linux下Socket编程

1. Socket简介 Socket是什么&#xff1f; Socket是一种进程间通信的机制&#xff0c;通过它应用程序可以通过网络进行数据传输。Socket提供了一种跨平台的接口&#xff0c;使得同样的代码可以在不同的操作系统上运行。Socket类型 流式套接字&#xff08;SOCK_STREAM&#xff0…...

Scrapy 爬虫的大模型支持

使用 Scrapy 时&#xff0c;你可以轻松使用大型语言模型 (LLM) 来自动化或增强你的 Web 解析。 有多种使用 LLM 来帮助进行 Web 抓取的方法。在本指南中&#xff0c;我们将在每个页面上调用一个 LLM&#xff0c;从中抽取我们定义的一组属性&#xff0c;而无需编写任何选择器或…...

数据仓库简介(一)

数据仓库概述 1. 什么是数据仓库&#xff1f; 数据仓库&#xff08;Data Warehouse&#xff0c;简称 DW&#xff09;是由 Bill Inmon 于 1990 年提出的一种用于数据分析和挖掘的系统。它的主要目标是通过分析和挖掘数据&#xff0c;为不同层级的决策提供支持&#xff0c;构成…...

Kafka和RabbitMQ区别

RabbitMQ的消息延迟是微秒级&#xff0c;Kafka是毫秒级&#xff08;1毫秒1000微秒&#xff09; 延迟消息是指生产者发送消息发送消息后&#xff0c;不能立刻被消费者消费&#xff0c;需要等待指定的时间后才可以被消费。 Kafka的单机呑吐量是十万级&#xff0c;RabbitMQ是万级…...

go-zero学习

go-zero官网&#xff1a; https://go-zero.dev/docs/tasks 好文&#xff1a; https://blog.csdn.net/m0_63629756/article/details/136599547 视频&#xff1a; https://www.bilibili.com/video/BV18JxUeyECg 微服务基础 根目录下&#xff0c;一个文件夹就是一个微服务。如果微…...

python如何查询函数

1、通用的帮助函数help() 使用help()函数来查看函数的帮助信息。 如&#xff1a; import requests help(requests) 会有类似如下输出&#xff1a; 2、查询函数信息 ★查看模块下的所有函数&#xff1a; dir(module_name) #module_name是要查询的函数名 如&#xff1a; i…...

计算机视觉与深度学习 | 从激光雷达数据中提取地面点和非地面点(附matlab代码)

===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 激光雷达数据 使用velodyneFileReader函数从P...

vulnhub-wakanda 1靶机

vulnhub&#xff1a;wakanda: 1 ~ VulnHub 导入靶机&#xff0c;放在kali同网段&#xff0c;扫描 靶机在192.168.81.5&#xff0c;扫描端口 四个端口&#xff0c;详细扫描一下 似乎没什么值得注意的&#xff0c;先看网站 就这一个页面&#xff0c;点按钮也没反应&#xff0c;扫…...

Bilibili视频如何保存到本地

Bilibili(哔哩哔哩)作为中国领先的视频分享平台之一&#xff0c;汇聚了大量的优质内容&#xff0c;从搞笑动画、综艺节目到专业教程&#xff0c;应有尽有。许多用户时常会遇到这样的需求&#xff1a;希望将视频保存到本地&#xff0c;方便离线观看或者保存珍藏。由于版权保护等…...

C++之多线程

前言 多线程和多进程是并发编程的两个核心概念,它们在现代计算中都非常重要,尤其是在需要处理大量数据、提高程序性能和响应能力的场景中。 多线程的重要性: 资源利用率:多线程可以在单个进程中同时执行多个任务,这可以更有效地利用CPU资源,特别是在多核处理器上。 性…...

《C++音频降噪秘籍:让声音纯净如初》

在音频处理领域&#xff0c;降噪是一项至关重要的任务。无论是录制音乐、语音通话还是音频后期制作&#xff0c;都需要有效地去除背景噪声&#xff0c;以获得清晰、纯净的音频效果。在 C中实现高效的音频降噪处理&#xff0c;可以为音频应用带来更高的质量和更好的用户体验。本…...

C(十)for循环 --- 黑神话情景

前言&#xff1a; "踏过三界宝刹&#xff0c;阅过四洲繁华。笑过五蕴痴缠&#xff0c;舍过六根牵挂。怕什么欲念不休&#xff0c;怕什么浪迹天涯。步履不停&#xff0c;便是得救之法。" 国际惯例&#xff0c;开篇先喝碗鸡汤。 今天&#xff0c;杰哥写的 for 循环相…...

记录一次docker报错无法访问文件夹,权限错误问题

记录一次docker报错无法访问文件夹&#xff0c;权限错误问题 1. 背景 使用docker安装photoview&#xff0c;为其分配了一个cache目录&#xff0c;用户其缓存数据。在运行过程中&#xff0c;扫描文件后显示如下错误 could not make album image cache directory: mkdir /app/c…...

react crash course 2024(8) useEffect

引入 import { useEffect } from react; useEffect – React 中文文档useEffect 是一个 React Hook&#xff0c;它允许你 将组件与外部系统同步。 有些组件需要与网络、某些浏览器 API 或第三方库保持连接&#xff0c;当它们显示在页面上时。这些系统不受 React 控制&#xff0…...

GEE开发之Modis_NDWI数据分析和获取

GEE开发之Modis_NDWI数据分析和获取 0 数据介绍NDWI介绍MOD09GA介绍 1 NDWI天数据下载2 NDWI月数据下载3 NDWI年数据下载 前言&#xff1a;本文主要介绍Modis下的NDWI数据集的获取。归一化差异水指数 (NDWI) 对植被冠层液态水含量的变化很敏感。它来自近红外波段和第二个红外波…...

netty之NettyClient半包粘包处理、编码解码处理、收发数据方式

前言 Netty开发中&#xff0c;客户端与服务端需要保持同样的&#xff1b;半包粘包处理&#xff0c;编码解码处理、收发数据方式&#xff0c;这样才能保证数据通信正常。在前面NettyServer的章节中我们也同样处理了&#xff1b;半包粘包、编码解码等&#xff0c;为此在本章节我们…...

Linux:文件描述符介绍

文章目录速览 1、虚拟地址空间(1)What&#xff08;什么是虚拟地址空间&#xff09;(2)Why&#xff08;为什么需要虚拟地址空间&#xff09; 2、文件描述符(1)What&#xff08;什么是文件描述符&#xff09;(2)文件描述符表 1、虚拟地址空间 (1)What&#xff08;什么是虚拟地址…...

stm32f103调试,程序与定时器同步设置

在调试定时器相关代码时&#xff0c;注意到定时器的中断位总是置1&#xff0c;怀疑代码有问题&#xff0c;经过增大定时器的中断时间&#xff0c;发现定时器与代码调试并不同步&#xff0c;这一点对于调试涉及定时器的代码是非常不利的&#xff0c;这里给出keil调试stm32使定时…...

《Python编程:从入门到实践》数据可视化

一、项目 数据可视化学习 二、库依赖 matplotlib&#xff0c;pygal&#xff0c; 三、生成数据 1.绘制简单的折线图 import matplotlib.pyplot as pltsquares [1, 4, 9, 16, 25] plt.plot(squares) plt.show() 模块pyplot包含很多用于生成图表的函数。 &#xff08;1&am…...

github/git密钥配置与使用

零、前言 因为要在ubuntu上做点东西&#xff0c;发现git clone 的时候必须输账户密码&#xff0c;后来发现密码是token&#xff0c;但是token一大串太烦了&#xff0c;忙了一天发现可以通过配置 公钥 来 替代 http 的 部署方式。 一、生成 ssh 密钥对 我们先测试下能不能 连接…...

BiLSTM模型实现电力数据预测

基础模型见&#xff1a;A020-LSTM模型实现电力数据预测 1. 引言 时间序列预测在电力系统管理、负荷预测和能源优化等领域具有重要意义。传统的单向长短期记忆网络&#xff08;LSTM&#xff09;因其在处理时间序列数据中的优势&#xff0c;广泛应用于此类任务。然而&#xff0…...

VMware ESXi 6.7U3u macOS Unlocker 集成驱动版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 6.7U3u macOS Unlocker & OEM BIOS 2.7 集成 Realtek 网卡驱动和 NVMe 驱动 (集成驱动版) UI fix 此版本解决的问题&#xff1a;VMware Host Client 无法将现有虚拟磁盘 (VMDK) 附加到虚拟机 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-…...

一个简单的SQL面试题

最近面试遇到一个SQL题&#xff0c;复述如下&#xff1a; SQL面试题 现在有两张表&#xff0c;结构如下&#xff1a; 学生表&#xff08;student&#xff09; 学号sid姓名name1张三2李四3王五 成绩表&#xff08;score&#xff09; 序号id学号sid科目subject分数score11语…...

网站服务器在不同操作系统上监听端口情况的方法

网站服务器监听端口的情况通常需要通过一些命令行工具来进行检查&#xff0c;这样可以查看哪些进程在监听特定的端口&#xff0c;以及它们的状态。以下是在不同操作系统上可以使用的几种方法&#xff1a; Windows系统 1. 使用netstat命令 打开命令提示符(cmd)&#xff0c;然后输…...

RabbitMQ篇(基本介绍)

目录 一、MQ 1. 什么是MQ 2. 为什么要用MQ【业务场景】 2.1. 异步 2.2. 应用解耦 2.3. 流量削峰 3. MQ的分类 &#xff08;1&#xff09;ActiveMQ &#xff08;2&#xff09;Kafka &#xff08;3&#xff09;RocketMQ &#xff08;4&#xff09;RabbitMQ 4. MQ 的选…...

网站开发 php 书籍 推荐/建站网站

\(\color{#0066ff}{ 题目描述 }\) 小D 被邀请到实验室&#xff0c;做一个跟图片质量评价相关的主观实验。实验用到的图片集一共有 \(N\) 张图片&#xff0c;编号为 \(1\) 到\(N\)。实验分若干轮进行&#xff0c;在每轮实验中&#xff0c;小 D会被要求观看某两张随机选取的图片&…...

天津 网站建设/个人域名注册流程

一种方法是按名字和姓氏分组,然后反转分组以获得单个ID.事先kursort输入,以确保您获得最低的ID.krsort($input);//groupforeach ($input as $id > $person) {// overwrite the id each time, but since the input is sorted by id in descending order,// the last one will…...

电商网站设计图片素材/汕头百度关键词推广

通过进入单用户模式来修改root账户口令&#xff0c;并为grub菜单项设置密码保护功能&#xff1b;直接进入系统后&#xff0c;输入e&#xff0c;进入此界面后&#xff0c;输入e然后输入single&#xff1b;回车键输入之后&#xff0c;在摁b键启动&#xff1b;此时不用输密码&…...

网站搭建公司官网/seo免费培训

计算机常用的基础知识信号是数据的表现形式。信号分为数字信号和模拟信号两类。数字信号是一种离散的脉冲序列&#xff0c;常用一个脉冲表示一位二进制数。模拟信号是一种连续变化的信号&#xff0c;声音就是一种典型的模拟信号。目前&#xff0c;计算机内部处理的信号都是数字…...

dw做企业网站/百度搜索引擎优化

目录 什么是函数对象 函数对象的用处 什么是函数对象 class myprint {public:void operator()(int num){cout << "num " << num << endl;count; }int count 0; };void text2(int num) {cout << "num " << num << e…...

wordpress退出登录/湖北seo网站推广

事务中的加锁顺序问题 事务中分有4中级别的 下面直接引联机帮助 SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE } 参数 READ COMMITTED 指定在读取数据时控制共享锁以避免脏读&…...