SpringBoot系列 启动流程

news2024/10/3 14:37:40

文章目录

  • 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();
	// 创建 BootstrapContext
	DefaultBootstrapContext 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> bootstrapRegistryInitializers
	this.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) {
	// 创建ConfigurableEnvironment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 配置ConfigurableEnvironment
	configureEnvironment(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 beans
	ConfigurableListableBeanFactory 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这个BeanFactoryPostProcessor
	context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
	// Load the sources
	Set<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#setRequiredProperties
	getEnvironment().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 {
		// 创建BeanFactory
		DefaultListableBeanFactory 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 => conversionService
	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));
	}

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

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

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

相关文章

Kotlin 处理字符串和正则表达式(二十一)

导读大纲 1.1 处理字符串和正则表达式1.1.1 分割字符串1.1.2 正则表达式和三引号字符串1.1.3 多行三引号字符串IntelliJ IDEA 和 Android Studio 中三重引号字符串内部的语法高亮显示 1.1 处理字符串和正则表达式 Kotlin 字符串与 Java 字符串完全相同 可以将 Kotlin 代码中创建…

【算法】哈希映射(C/C++)

目录 算法引入&#xff1a; 算法介绍&#xff1a; 优点&#xff1a; 缺点&#xff1a; 哈希映射实现&#xff1a; map unordered_map 题目链接&#xff1a;“蓝桥杯”练习系统 解析&#xff1a; 代码实现&#xff1a; 哈希映射算法是一种通过哈希函数将键映射到数组索…

JAVA基础语法 Day11

一、Set集合 Set特点&#xff1a;无序&#xff08;添加数据的顺序和获取出的数据顺序不一致&#xff09;&#xff0c;不重复&#xff0c;无索引 public class demo1 {public static void main(String[] args) {//1.创建一个集合//HashSet特点&#xff1a;无序&#xff0c;不重…

Redis: 集群架构,优缺点和数据分区方式和算法

集群 集群指的就是一组计算机作为一个整体向用户提供一组网络资源 我就举一个简单的例子&#xff0c;比如百度&#xff0c;在北京和你在上海访问的百度是同一个服务器吗&#xff1f;答案肯定是不是的&#xff0c;每一个应用可以部署在不同的地方&#xff0c;但是我们提供的服务…

C0010.Qt5.15.2下载及安装方法

1. 下载及安装 Qt 添加链接描述下载地址&#xff1a;http://download.qt.io/ 选择 archive 目录 安装Qt **注意&#xff1a;**本人使用的是Qt5.15.2版本&#xff0c;可以按如下方法找到该版本&#xff1b;

华为OD机试 - 信号发射和接收 - 矩阵(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

【AIGC】AI时代的数据安全:使用ChatGPT时的自查要点

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;法律法规背景中华人民共和国保守秘密法中华人民共和国网络安全法中华人民共和国个人信息保护法遵守法律法规的重要性 &#x1f4af;ChatGPT的数据使用特点ChatGPT数据安全…

华为OD机试 - 分班问题(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

无人机电力巡检:点亮电力巡检新视野!

一、无人机电力巡查的优势 提高巡检效率&#xff1a;无人机可以搭载高清摄像头、红外热像仪等先进设备&#xff0c;实时拍摄和传输图像&#xff0c;帮助巡检人员快速发现潜在问题&#xff0c;如电线破损、绝缘子污损、设备过热等&#xff0c;从而大大缩短了巡检周期。 降低人…

Tiny Transformer:从零开始构建简化版Transformer模型

引言 自然语言处理&#xff08;NLP&#xff09;与计算机视觉&#xff08;CV&#xff09;有显著差异&#xff0c;各自任务的独特性决定了它们适用的模型架构。在CV中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;长期占据主导地位&#xff0c;而在NLP领域&#xff0c;循…

基于微信小程序的四六级词汇+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;四六级词汇小程序被用户普遍使用&#xff0c;为方便用户能…

Python入门--判断语句

目录 1. 布尔类型和比较运算符 2. if语句的基本格式 3. if-else语句 4. if-elif-else语句 5. 判断语句的嵌套 6. 应用--猜数字游戏 进行逻辑判断&#xff0c;是生活中常见的行为。同样&#xff0c;在程序中&#xff0c;进行逻辑判断也是最为基础的功能。 1. 布尔类型和比…

OceanBase—02(入门篇——对于单副本单节点,由1个observer扩容为3个observer集群)——之前的记录,当初有的问题未解决,目前新版未尝试

OceanBase—02&#xff08;入门篇——对于单副本单节点&#xff0c;由1个observer扩容为3个observer集群&#xff09;——之前的记录&#xff0c;有的问题未解决&#xff0c;新版未尝试 1、前言—安装单副本单节点集群1.1 docker安装OB 2、查看现有集群情况2.1 进入容器&#x…

设置服务器走本地代理

勾选&#xff1a; 然后&#xff1a; git clone https://github.com/rofl0r/proxychains-ng.git./configure --prefix/home/wangguisen/usr --sysconfdir/home/wangguisen/etcmakemake install# 在最后配置成本地代理地址 vim /home/wangguisen/etc/proxychains.confsocks4 17…

Python编写的贪吃蛇小游戏

安装包 pip install pygame完整代码 import pygame import randompygame.init()# 定义颜色 white (255, 255, 255) black (0, 0, 0) red (213, 50, 80) green (0, 255, 0) blue (50, 153, 213)# 定义屏幕大小 dis_width 800 dis_height 600dis pygame.display.set_mo…

【数据结构】什么是平衡二叉搜索树(AVL Tree)?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;AVL树的概念 &#x1f4cc;AVL树的操作 &#x1f38f;AVL树的插入操作 ↩️右单旋 ↩️↪️右左双旋 ↪️↩️左右双旋 ↪️左单旋 &#x1f38f;AVL树的删…

CTF刷题buuctf

[WUSTCTF2020]颜值成绩查询 拿到相关题目&#xff0c;其实根据功能和参数分析。需要传入一个学号然后进行针对于对应的学号进行一个查询&#xff0c;很可能就会存在sql注入。 其实这道题最难的点&#xff0c;在于过滤了空格&#xff0c;因此我们使用 /**/来过滤空格的限制。…

智能化焊接数据管理系统:系统功能设计与应用场景,OEM定制

在快速发展的工业4.0时代&#xff0c;智能化技术正以前所未有的速度改变着各行各业&#xff0c;其中焊接行业也不例外。随着物联网、大数据、人工智能等技术的不断融合&#xff0c;智能化焊接数据管理系统应运而生&#xff0c;成为提高焊接效率、保障焊接质量、优化生产流程的重…

半监督学习与数据增强(论文复现)

半监督学习与数据增强&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 半监督学习与数据增强&#xff08;论文复现&#xff09;概述算法原理核心逻辑效果演示使用方式 概述 本文复现论文提出的半监督学习方法&#xff0c;半监督学习&…

C题(二)字符串转数字 --- atoi

———————————————————**目录**—————————————————— 一、 atoi函数介绍 功能函数原型使用示例 二、题解之一 三、留言 问题引入&#x1f449; 输入样例&#x1f449; 5 01234 00123 00012 00001 00000 输出样例&#x1f449; 1234 123 …