首页 > 其他分享 >springboot启动之-本地配置(一)

springboot启动之-本地配置(一)

时间:2024-06-06 22:57:55浏览次数:26  
标签:resource springboot 启动 bootstrapContext environment 本地 new null event

没有前言,只有源码~~ ,依然是最简单的依赖

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
}
java {
	sourceCompatibility = '17'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
}

EnvironmentPostProcessorApplicationListener怎么来的

	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));
		// 扫描spring.factories文件拿到 ApplicationListener的子类  
		// 放入private List<ApplicationListener<?>> listeners;属性
		// 这里差点漏了
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

环境类
这里要注意实例化 EnvironmentPostProcessorApplicationListener 的时候 它的postProcessorsFactory = SpringFactoriesEnvironmentPostProcessorsFactory 后面用的到

	public EnvironmentPostProcessorApplicationListener() {
		this(EnvironmentPostProcessorsFactory::fromSpringFactories);
	}

	private EnvironmentPostProcessorApplicationListener(
			Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory) {
		this.postProcessorsFactory = postProcessorsFactory;
		this.deferredLogs = new DeferredLogs();
	}

	static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
		return new SpringFactoriesEnvironmentPostProcessorsFactory(
			SpringFactoriesLoader.forDefaultResourceLocation(classLoader));
	}

SpringApplicationRunListener怎么来的

public ConfigurableApplicationContext run(String... args) {
		Startup startup = Startup.create();
		if (this.registerShutdownHook) {
			SpringApplication.shutdownHook.enableShutdownHookAddition();
		}
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		// 配置从这一行开始看
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 入口应该是这里注意 listeners 被传入了
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
	}
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
		argumentResolver = argumentResolver.and(String[].class, args);
		// 获取监听器,随处可见的spi
		List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
				argumentResolver);
		SpringApplicationHook hook = applicationHook.get();
		SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
		if (hookListener != null) {
			listeners = new ArrayList<>(listeners);
			listeners.add(hookListener);
		}
		return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
	}

这个方法目前就获取了一个SpringApplicationRunListener
监听器

开始找配置application.yaml

进入org.springframework.boot.SpringApplication#prepareEnvironment

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		// 关键看这一行
		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;
	}
	void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
		doWithListeners("spring.boot.application.environment-prepared",
				(listener) -> listener.environmentPrepared(bootstrapContext, environment));
	}

这个方法干了什么,也就是循环使用前面获得的listener调用environmentPrepared方法,刚刚也看到了目前就一个也就是org.springframework.boot.context.event.EventPublishingRunListener

	@Override
	public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		multicastInitialEvent(
				// 注意事件类是 ApplicationEnvironmentPreparedEvent
				new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
	}

注意这里传递了ApplicationEnvironmentPreparedEvent事件

	EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
	}

在实例化EventPublishingRunListener的时候,指定了initialMulticaster SimpleApplicationEventMulticaster
进入SimpleApplicationEventMulticastermulticastEvent方法

	private void multicastInitialEvent(ApplicationEvent event) {
		// 注意这个方法补充了一些listeners到 SimpleApplicationEventMulticaster
		// 的  Set<ApplicationListener<?>> defaultRetriever.applicationListeners
		refreshApplicationListeners();
		this.initialMulticaster.multicastEvent(event);
	}
	@Override
	public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
		Executor executor = getTaskExecutor();
		// 循环 ApplicationListener 子类 invokeListener方法 让所有的listener去调用 onApplicationEvent(event);
		// 这里就用到EnvironmentPostProcessorApplicationListener
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null && listener.supportsAsyncExecution()) {
				try {
					executor.execute(() -> invokeListener(listener, event));
				}
				catch (RejectedExecutionException ex) {
					// Probably on shutdown -> invoke listener locally instead
					invokeListener(listener, event);
				}
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
/*
这里要描述一下 getApplicationListeners(event, type) 如何得到 EnvironmentPostProcessorApplicationListener
不然莫名奇妙。
首先这个类来自 AbstractApplicationEventMulticaster 的 defaultRetriever属性的 applicationListeners 属性
而SimpleApplicationEventMulticaster是它的子类。
找一下applicationListeners 的add()方法调用位置,索性只有一个地方那就是AbstractApplicationEventMulticaster#addApplicationListener  方法,
加个断点就可以看到所有添加进来的listeners了,可以看到它是refreshApplicationListeners()来的,看这个方法发现listener来自
SpringApplication 的 List<ApplicationListener<?>> listeners;
listeners 什么时候添加的这个类呢,看本文开始

*/

其中有一个listenerEnvironmentPostProcessorApplicationListener
再进入org.springframework.boot.env.EnvironmentPostProcessorApplicationListener#onApplicationEvent

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
			// 判断事件
			onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent();
		}
		if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}
	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		SpringApplication application = event.getSpringApplication();
		// ConfigDataEnvironmentPostProcessor又是怎么来的,看下面注释
		for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
				event.getBootstrapContext())) {
			// postProcessor = ConfigDataEnvironmentPostProcessor的时候
			postProcessor.postProcessEnvironment(environment, application);
		}
	}

ConfigDataEnvironmentPostProcessor哪里来的?spi还是spi

	List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
			ConfigurableBootstrapContext bootstrapContext) {
		ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
		EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
		// 前面讲到了  postProcessorsFactory = SpringFactoriesEnvironmentPostProcessorsFactory
		return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
	}
class SpringFactoriesEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory {

	private final SpringFactoriesLoader loader;

	SpringFactoriesEnvironmentPostProcessorsFactory(SpringFactoriesLoader loader) {
		this.loader = loader;
	}

	@Override
	public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
			ConfigurableBootstrapContext bootstrapContext) {
		ArgumentResolver argumentResolver = ArgumentResolver.of(DeferredLogFactory.class, logFactory);
		argumentResolver = argumentResolver.and(ConfigurableBootstrapContext.class, bootstrapContext);
		argumentResolver = argumentResolver.and(BootstrapContext.class, bootstrapContext);
		argumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);
		// SpringFactoriesLoader 
		return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
	}

}

无尽的spi

进入 ConfigDataEnvironmentPostProcessor#postProcessEnvironment

	void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
			Collection<String> additionalProfiles) {
		this.logger.trace("Post-processing environment to add config data");
		resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
		// 得到 ConfigDataEnvironment 实例 走processAndApply方法
		getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
	}

	ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
			Collection<String> additionalProfiles) {
		return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
				additionalProfiles, this.environmentUpdateListener);
	}

继续走 ConfigDataEnvironment#processAndApply

	void processAndApply() {
		ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
				this.loaders);
		registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
		// 看这里
		ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
		ConfigDataActivationContext activationContext = createActivationContext(
				contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
		contributors = processWithoutProfiles(contributors, importer, activationContext);
		activationContext = withProfiles(contributors, activationContext);
		contributors = processWithProfiles(contributors, importer, activationContext);
		applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
				importer.getOptionalLocations());
	}
	private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
			ConfigDataImporter importer) {
		this.logger.trace("Processing initial config data environment contributors without activation context");
		// 看这里
		contributors = contributors.withProcessedImports(importer, null);
		registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
		return contributors;
	}

继续,不要停

ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
			ConfigDataActivationContext activationContext) {
		ImportPhase importPhase = ImportPhase.get(activationContext);
		this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
				(activationContext != null) ? activationContext : "no activation context"));
		ConfigDataEnvironmentContributors result = this;
		int processed = 0;
		while (true) {
			ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
			if (contributor == null) {
				this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
				return result;
			}
			if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
				ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext);
				result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
						result.getRoot().withReplacement(contributor, bound), this.conversionService);
				continue;
			}
			ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
					result, contributor, activationContext);
			ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
			List<ConfigDataLocation> imports = contributor.getImports();
			this.logger.trace(LogMessage.format("Processing imports %s", imports));
			// 看这里
			Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
					locationResolverContext, loaderContext, imports);
			this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));
			ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
					asContributors(imported));
			result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
					result.getRoot().withReplacement(contributor, contributorAndChildren), this.conversionService);
			processed++;
		}
	}
	Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
			ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
			List<ConfigDataLocation> locations) {
		try {
			Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
			List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
			// load方法
			return load(loaderContext, resolved);
		}
		catch (IOException ex) {
			throw new IllegalStateException("IO error on loading imports from " + locations, ex);
		}
	}

渐入佳境

	private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
			List<ConfigDataResolutionResult> candidates) throws IOException {
		Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
		for (int i = candidates.size() - 1; i >= 0; i--) {
			ConfigDataResolutionResult candidate = candidates.get(i);
			ConfigDataLocation location = candidate.getLocation();
			ConfigDataResource resource = candidate.getResource();
			this.logger.trace(LogMessage.format("Considering resource %s from location %s", resource, location));
			if (resource.isOptional()) {
				this.optionalLocations.add(location);
			}
			if (this.loaded.contains(resource)) {
				this.logger
					.trace(LogMessage.format("Already loaded resource %s ignoring location %s", resource, location));
				this.loadedLocations.add(location);
			}
			else {
				try {
					// 看这里
					ConfigData loaded = this.loaders.load(loaderContext, resource);
					if (loaded != null) {
						this.logger.trace(LogMessage.format("Loaded resource %s from location %s", resource, location));
						this.loaded.add(resource);
						this.loadedLocations.add(location);
						result.put(candidate, loaded);
					}
				}
				catch (ConfigDataNotFoundException ex) {
					handle(ex, location, resource);
				}
			}
		}
		return Collections.unmodifiableMap(result);
	}
public class StandardConfigDataLoader implements ConfigDataLoader<StandardConfigDataResource> {

	private static final PropertySourceOptions PROFILE_SPECIFIC = PropertySourceOptions.always(Option.PROFILE_SPECIFIC);

	private static final PropertySourceOptions NON_PROFILE_SPECIFIC = PropertySourceOptions.ALWAYS_NONE;

	@Override
	public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource)
			throws IOException, ConfigDataNotFoundException {
		if (resource.isEmptyDirectory()) {
			return ConfigData.EMPTY;
		}
		ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource());
		StandardConfigDataReference reference = resource.getReference();
		Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(),
				Origin.from(reference.getConfigDataLocation()));
		String name = String.format("Config resource '%s' via location '%s'", resource,
				reference.getConfigDataLocation());
		// 走这里
		List<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
		PropertySourceOptions options = (resource.getProfile() != null) ? PROFILE_SPECIFIC : NON_PROFILE_SPECIFIC;
		return new ConfigData(propertySources, options);
	}

}
public class YamlPropertySourceLoader implements PropertySourceLoader {

	@Override
	public String[] getFileExtensions() {
		return new String[] { "yml", "yaml" };
	}

	@Override
	public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
		if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", getClass().getClassLoader())) {
			throw new IllegalStateException(
					"Attempted to load " + name + " but snakeyaml was not found on the classpath");
		}
		// resource是什么,就是application.yaml,看它如何将application.yaml文件解析成Map
		List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
		if (loaded.isEmpty()) {
			return Collections.emptyList();
		}
		List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
		for (int i = 0; i < loaded.size(); i++) {
			String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
			propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
					Collections.unmodifiableMap(loaded.get(i)), true));
		}
		return propertySources;
	}

}

最终进入 org.springframework.beans.factory.config.YamlProcessor#process(org.springframework.beans.factory.config.YamlProcessor.MatchCallback, org.yaml.snakeyaml.Yaml, org.springframework.core.io.Resource)

演示配置

yaml解析器

标签:resource,springboot,启动,bootstrapContext,environment,本地,new,null,event
From: https://blog.csdn.net/z2802533142/article/details/139454548

相关文章

  • JVM运行时数据区域与本地内存概述
    文章目录0x00前言0x10程序计数器0x20栈0x21局部变量表0x22操作数栈0x23帧数据0x30堆0x40方法区0x41方法区的信息存放在哪?0x42字符串常量池0x43方法区的内存回收0x50本地内存0x00前言JVM在运行时会有一块专用的内存空间,称之为运行时数据区域。本文通过......
  • 利用ollama本地部署Llama3大语言模型
    Meta在开源大模型方面越战越勇,近日推出的Llama3在各方面都被公认为是最出色的。利用ollama在本地部署后使用了一会,感觉确实是行云流水。简单介绍下本地部署的流程:1、下载ollama:https://ollama.com/在这里下载win环境下的.exe文件,下载后直接安装即可。2、部署Llama3:......
  • 本地如何通过Ollama部署llama3、phi3等本地大模型?
    一、ollama是什么?在本地启动并运行大型语言模型。运行Llama3,Mistral,Gemma,CodeLlama和其他模型。自定义并创建您自己的。优势如下:•快速下载+容器自动运行大模型,现在下载,马上上手。•本地利用cpu运行大模型,本地安全可靠。•ollama命令,管理大模型相对方......
  • 非常可靠,手把手教你本地部署AI大模型-llama3:70b
    Meta公司一直致力于这样一个理念:“thatopensourcenotonlyprovidesgreattechnologyfordevelopers,butalsobringsthebestoutinpeople”,翻译过来就是开源不仅为开发人员提供了出色的技术,而且还将给人们带来更好的。但是前几天李彦宏说开源模型没有未来?我们的......
  • 基于springboot的相亲网站管理系统,相亲管理系统,附源码+数据库+论文+开题报告+任务书+P
    1、项目介绍相亲网站根据使用权限的角度进行功能分析,并运用用例图来展示各个权限需要操作的功能。管理员权限操作的功能包括管理婚礼公司,管理婚礼公司预约信息,管理结婚案例,管理相亲信息,管理相亲留言,管理用户等。用户权限操作的功能包括预约婚礼公司,收藏婚礼公司,查看结婚......
  • 为什么我们需要在软件本地化过程中使用术语服务?
    你知道软件翻译和本地化的术语服务吗?此解决方案涵盖源术语和目标术语的创建、开发和维护。所有术语都存储在具有多个字段的数据库中,包括术语定义、用法示例、上下文和历史记录。这使我们能够正确处理每个术语的创建或更改请求,避免创建重复的术语或多次更改单个术语。如果您仍......
  • JavaWeb_SpringBootWeb案例
    环境搭建:开发规范接口风格-Restful:统一响应结果-Result:开发流程:        第一步应该根据需求定义表结构和定义接口文档注意:    本文代码从上往下一直添加功能,后面的模块下的代码包括前面的模块,并不是某个模块中的代码只有当前功能。部门管理查......
  • 【SpringBoot + Vue 尚庭公寓实战】项目初始化准备(二)
    尚庭公寓SpringBoot+Vue项目实战】项目初始化准备(二)文章目录尚庭公寓SpringBoot+Vue项目实战】项目初始化准备(二)1、导入数据库2、创建工程3、项目初始配置3.1、SpringBoot依赖配置3.2、创建application.yml文件3.3、创建SpringBoot启动类4、MyBatisPlus配置4.1......
  • 【入门教程】5分钟教你快速学会集成Java springboot ~
    介绍ApacheDolphinScheduler是一个分布式易扩展的开源分布式调度系统,支持海量数据处理,具有任务流程调度、任务流程编排、任务监控告警、工作流引擎等功能。本文将介绍如何将ApacheDolphinScheduler集成到JavaSpringboot项目中,以实现更灵活和便捷的调度功能。步骤步骤一:添......
  • ubuntu系统(香蕉派)设置开机自启动
    以下介绍两种设置开机自启动的方法,分别对应界面中配置和在命令中配置方法1:编辑开启自启动命令sudonano/etc/rc.local#在该文件中添加启动执行命令,需要在exit0之前。添加命令后的&用于设置其支持后台运行。/bin/bash/path/run.sh&#如果没有正常执行权限,更爱rc.loca......