SpringBoot启动流程分析之ApplicationEnvironmentPreparedEvent事件发布(四)

news2024/11/20 14:32:50

SpringBoot启动流程分析之ApplicationEnvironmentPreparedEvent事件发布(四)

目录:

文章目录

      • SpringBoot启动流程分析之ApplicationEnvironmentPreparedEvent事件发布(四)
          • 构建环境
          • 1、创建ConfigurableEnvironment对象
          • 2、配置环境
            • 2.1、配置属性源
            • 2.2、配置配置文件
          • 3、发布ApplicationEnvironmentPreparedEvent事件
          • 4、将环境绑定到SpringApplication
          • 5、将配置属性源绑定到Environment
          • 六、SpringApplication.run调用两次

args参数封装执行完成以后,就是构建环境Environment,下面分析其运行流程。

org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {
		try {
			//  开始分析...
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 其余代码忽略了...
		}catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
		return context;
	}
构建环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment 
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
1、创建ConfigurableEnvironment对象
private ConfigurableEnvironment getOrCreateEnvironment() {
        //如果你通过SpringApplication对象的setEnvironment()方法设置了环境,则直接返回该环境
        if (this.environment != null) {
			return this.environment;
		}
        //根据webApplicationType选择
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment(); //标准的servlet环境
		case REACTIVE:
			return new StandardReactiveWebEnvironment(); //标准的响应式web环境
		default:
			return new StandardEnvironment();  //标准环境
		}
	}

在这里插入图片描述

从UML类图看出:StandardServletEnvironment 继承StandardEnvironment 实现ConfigurableWebEnvironment
StandardEnvironment 继承AbstractEnvironment 中构造函数中customizePropertySources()
在StandardServletEnvironment 中有重写:

/** Servlet context init parameters property source name: {@value}. */
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

/** Servlet config init parameters property source name: {@value}. */
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

/** JNDI property source name: {@value}. */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
    
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
    propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
    if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
    }
    super.customizePropertySources(propertySources);
}

StandardEnvironment重写:

/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(
            new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    propertySources.addLast(
            new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

返回的标准servlet环境如下:
在这里插入图片描述

StandardServletEnvironment {
        activeProfiles=[], 
        defaultProfiles=[default], 
        propertySources=[
            StubPropertySource {name='servletConfigInitParams'}, 
            StubPropertySource {name='servletContextInitParams'}, 
            PropertiesPropertySource {name='systemProperties'}, 
            SystemEnvironmentPropertySource {name='systemEnvironment'}
         ]
 }
2、配置环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 开始分析
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    //是否添加转换服务,后面在必要时转换环境
    if (this.addConversionService) {
        //  格式化转换器
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    
    //配置属性源,包括默认配置和命令行配置,默认配置使用SpringApplication的setDefaultProperties(Map<String, Object> defaultProperties)方法设置
    configurePropertySources(environment, args);
    
    //设置配置文件
    configureProfiles(environment, args);
}
    
2.1、配置属性源

org.springframework.boot.SpringApplication#configurePropertySources

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        //得到属性源,即返回的标准环境中的propertySources
		MutablePropertySources sources = environment.getPropertySources();
        //判断默认配置是否为空,不为空就添加到source里即propertySources,name为defaultProperties
        //通过application.setDefaultProperties(defaultProperties);设置
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
			sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
		}
        //判断原始args参数长度是否大于0,addCommandLineProperties 默认为true
		if (this.addCommandLineProperties && args.length > 0) {
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			if (sources.contains(name)) {
              //通过commandLineArgs得到配置
				PropertySource<?> source = sources.get(name);
              //new一个CompositePropertySource,是PropertySource的子类,所以构造方法里传一个name知道啥意思了吧,前面介绍过PropertySource了
              //该类中有一个Set<PropertySource<?>>
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(
						new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                //用composite替换原 source中name为commandLineArgs的配置                        
				composite.addPropertySource(source);
				sources.replace(name, composite);
			}
			else {
              //不存在就new一个SimpleCommandLinePropertySource实例添加
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}
2.2、配置配置文件
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);
        // 开始分析
		configureProfiles(environment, args);
	}
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		environment.getActiveProfiles(); // ensure they are initialized
		// But these ones should go first (last wins in a property key clash)
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        //设置ActiveProfiles
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}

可以看到得到的配置文件就是配置项spring.profiles.active配置的。会判断this.activeProfiles是否为空,如果你是通过context.getEnvironment().setActiveProfiles(profiles)方法设置的,那就直接返回了。
org.springframework.core.env.AbstractEnvironment#getDefaultProfiles

public String[] getDefaultProfiles() {
    return StringUtils.toStringArray(doGetDefaultProfiles());
}
protected Set<String> doGetDefaultProfiles() {
    synchronized (this.defaultProfiles) {
        if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
            String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
            if (StringUtils.hasText(profiles)) {
                setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
                        StringUtils.trimAllWhitespace(profiles)));
            }
        }
        return this.defaultProfiles;
    }
}
public void setDefaultProfiles(String... profiles) {
    Assert.notNull(profiles, "Profile array must not be null");
    synchronized (this.defaultProfiles) {
        this.defaultProfiles.clear();
        for (String profile : profiles) {
            validateProfile(profile);
            this.defaultProfiles.add(profile);
        }
    }
}

3、发布ApplicationEnvironmentPreparedEvent事件

org.springframework.boot.SpringApplication#prepareEnvironment

listeners.environmentPrepared(environment);

org.springframework.boot.SpringApplicationRunListeners#environmentPrepared

public void environmentPrepared(ConfigurableEnvironment environment) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}

事件的具体发布过程: 由于listener具体是实现类:EventPublishingRunListener

public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster
            .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

this.initialMulticaster.multicastEvent 的具体实现前面有讲解:

@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
        // 重点是由于getApplicationListeners 方法,通过获取具体对应的简体类, 后面进行
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

调用的堆栈信息:
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:93)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:340)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at cn.wingsing.RegisterApplication.main(RegisterApplication.java:14)
在这里插入图片描述

调用监听实现类:
BootstrapApplicationListener
org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent


在这里插入图片描述

org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext

private ConfigurableApplicationContext bootstrapServiceContext(
			ConfigurableEnvironment environment, final SpringApplication application,
			String configName) {
		StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
		MutablePropertySources bootstrapProperties = bootstrapEnvironment
				.getPropertySources();
		for (PropertySource<?> source : bootstrapProperties) {
			bootstrapProperties.remove(source.getName());
		}
		String configLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
		Map<String, Object> bootstrapMap = new HashMap<>();
		bootstrapMap.put("spring.config.name", configName);
		// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
		// will fail
		// force the environment to use none, because if though it is set below in the
		// builder
		// the environment overrides it
		bootstrapMap.put("spring.main.web-application-type", "none");
		if (StringUtils.hasText(configLocation)) {
			bootstrapMap.put("spring.config.location", configLocation);
		}
		bootstrapProperties.addFirst(
				new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
		for (PropertySource<?> source : environment.getPropertySources()) {
			if (source instanceof StubPropertySource) {
				continue;
			}
			bootstrapProperties.addLast(source);
		}
		// TODO: is it possible or sensible to share a ResourceLoader?
		SpringApplicationBuilder builder = new SpringApplicationBuilder()
				.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
				.environment(bootstrapEnvironment)
				// Don't use the default properties in this builder
				.registerShutdownHook(false).logStartupInfo(false)
				.web(WebApplicationType.NONE);
		final SpringApplication builderApplication = builder.application();
		if (builderApplication.getMainApplicationClass() == null) {
			// gh_425:
			// SpringApplication cannot deduce the MainApplicationClass here
			// if it is booted from SpringBootServletInitializer due to the
			// absense of the "main" method in stackTraces.
			// But luckily this method's second parameter "application" here
			// carries the real MainApplicationClass which has been explicitly
			// set by SpringBootServletInitializer itself already.
			builder.main(application.getMainApplicationClass());
		}
		if (environment.getPropertySources().contains("refreshArgs")) {
			// If we are doing a context refresh, really we only want to refresh the
			// Environment, and there are some toxic listeners (like the
			// LoggingApplicationListener) that affect global static state, so we need a
			// way to switch those off.
			builderApplication
					.setListeners(filterListeners(builderApplication.getListeners()));
		}
		builder.sources(BootstrapImportSelectorConfiguration.class);
		final ConfigurableApplicationContext context = builder.run();
		// gh-214 using spring.application.name=bootstrap to set the context id via
		// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
		// spring.application.name
		// during the bootstrap phase.
		context.setId("bootstrap");
		// Make the bootstrap context a parent of the app context
		addAncestorInitializer(application, context);
		// It only has properties in it now that we don't want in the parent so remove
		// it (and it will be added back later)
		bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
		mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
		return context;
	}

在这里插入图片描述

ConfigFileApplicationListener:得到EnvironmentPostProcessor接口的实现类实例,List集合,并添加本身,根据order排序,循环执行postProcessEnvironment方法。

得到的EnvironmentPostProcessor接口实现类有三个,加上ConfigFileApplicationListener,一共四个,如下。
在这里插入图片描述

SystemEnvironmentPropertySourceEnvironmentPostProcessor:从标准环境中得到name为systemEnvironment的PropertySource,然后将SystemEnvironmentPropertySource替换为OriginAwareSystemEnvironmentPropertySource,用于提供从系统环境加载的项对原始属性名的访问。即可以通过原始属性名的到属性值。

SpringApplicationJsonEnvironmentPostProcessor:循环数组String[] CANDIDATES={spring.application.json,SPRING_APPLICATION_JSON},确认propertySource中是否存在这两个系统属性,如果存在new一个JsonPropertyValue对象,该类为内部类,将name和value封装,如果不存在返回空,然后过滤是否为空,找到第一个确认是否有这个值,如果有就解析json封装成JsonPropertySource并添加到环境(environment)中,如果不存在不执行任何操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CloudFoundryVcapEnvironmentPostProcessor:判断环境中是否存在VCAP_APPLICATION或者VCAP_SERVICES。如果有就添加Cloud Foundry的配置;没有就不执行任何操作。

ConfigFileApplicationListener:得到PropertySourceLoader的实现类PropertiesPropertySourceLoader、YamlPropertySourceLoader,然后加载配置文件,包括application.properties/yml(或yaml)/xml以及application-{dev,prod,test}.properties/yml(或yaml)/xml

AnsiOutputApplicationListener:将spring.output.ansi.enabled的值跟环境自绑定,再根据绑定值配置ANSI输出。

有三个值:

DETECT:尝试检测ANSI着色功能是否可用,默认是这个

ALWAYS:启用ANSI彩色输出

NEVER:禁用ANSI彩色输出

然后根据spring.output.ansi.console-available的值设置System.Console()是否可用。

LoggingApplicationListener:调用initialize()方法完成日志系统的初始化,前面ApplicationStartingEvent事件发布的时候已经调用了beforeInitialize()方法。如果没有配置日志,则使用默认配置,默认情况下,日志输出只写入控制台。如果需要日志文件,logging.path和logging.file属性可以使用。

ClasspathLoggingApplicationListener:在调试级别记录线程上下文类加载器(TCCL)的类路径,对ApplicationEnvironmentPreparedEvent和ApplicationFailedEvent做出反应。
在这里插入图片描述

BackgroundPreinitializer:貌似在这一步没做什么事,在ApplicationStartingEvent事件发布的时候已经执行了。
DelegatingApplicationListener:如果是ApplicationEnvironmentPreparedEvent事件,在getListeners方法中会得到配置项"context.listener.classes"设置的listener,得到实例对象添加到多播器multicaster中。否则判断多播器multicaster是否为空,不为空继续广播事件。
FileEncodingApplicationListener:默认情况下不起什么作用,也就是啥都不执行。但是如果spring.mandatory_file_encoding的值和file.encoding属性值不同时会引发报错。
在这里插入图片描述

4、将环境绑定到SpringApplication
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
       // 开始分析
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
    try {
        Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
    }
    catch (Exception ex) {
        throw new IllegalStateException("Cannot bind to SpringApplication", ex);
    }
}
5、将配置属性源绑定到Environment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
        // 开始分析
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
public static void attach(Environment environment) {
    Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
    MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
    PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
    if (attached != null && attached.getSource() != sources) {
        sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
        attached = null;
    }
    if (attached == null) {
        sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                new SpringConfigurationPropertySources(sources)));
    }
}

总结:
整个prepareEnvironment()方法走完以后得到的Environment如下:

StandardServletEnvironment {
        activeProfiles=[], 
        defaultProfiles=[default], 
        propertySources=[
            StubPropertySource {name='servletConfigInitParams'}, 
            StubPropertySource {name='servletContextInitParams'}, 
            PropertiesPropertySource {name='systemProperties'}, 
            SystemEnvironmentPropertySource {name='systemEnvironment'}
         ]
 }

其中ApplicationEnvironmentPreparedEvent事件的发布主要做的就是加载配置文件,初始化日志系统。

六、SpringApplication.run调用两次
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:300)
	  at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:139)
	  - locked <0x1ba2> (a java.util.concurrent.atomic.AtomicBoolean)
	  at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:203)
	  at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:114)
	  at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71)
	  at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
	  at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
	  at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
	  at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
	  at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
	  at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
	  at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:340)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
	  at cn.wingsing.RegisterApplication.main(RegisterApplication.java:14)

以上调用的堆栈信息,调用两次的原因:
第一次调用是服务启动的入库main方法的正常调用;
第二次调用的原因:
org.springframework.boot.SpringApplication#prepareEnvironment
中listeners.environmentPrepared(environment);
实现的EventPublishingRunListener事件监听的environmentPrepared方法,通过 this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); 广播ApplicationEnvironmentPreparedEvent。
BootstrapApplicationListener.onApplicationEvent的方法监听
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext中

// 配置启动的
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
// Don’t use the default properties in this builder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE); // 注意配置NONE
final ConfigurableApplicationContext context = builder.run(); 调用了run()方法.

前面讲的过 SpringApplicationBuilder 是SpringApplication的包装类,其实就是SpringApplication.run调用

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

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

相关文章

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.3,4 SPI驱动实验-I.MX6U SPI 寄存器

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

宠物空气净化器推荐:性价比首选,希喂、小米、352真实PK

宠物浮毛和异味常常困扰着宠物家庭&#xff0c;不仅会在家中四处散布&#xff0c;还可能成为过敏原&#xff0c;引发如打喷嚏和皮肤痒等不适症状。宠物的尿液、粪便气味以及它们自身散发的体味&#xff0c;同样会降低室内空气质量&#xff0c;影响居住的舒适度。为了解决这些问…

linux系统常用压缩和解压命令

文章目录 Ubuntu 系统中的文件压缩与解压指南一、常用的压缩和解压工具二、tar 工具三、gzip 工具四、bzip2 工具五、zip 和 unzip 工具六、7z 工具乱码批量解压脚本七、总结 Ubuntu 系统中的文件压缩与解压指南 在 Ubuntu 系统中&#xff0c;文件压缩与解压是日常操作中非常常…

Varjo XR-4功能详解:由凝视驱动的XR自动对焦相机系统

Varjo是XR市场中拥有领先技术的虚拟现实设备供应商&#xff0c;其将可变焦距摄像机直通系统带入到虚拟和混合现实场景中。在本篇文章中&#xff0c;Varjo的技术工程师维尔蒂莫宁详细介绍了这项在Varjo XR-4焦点版中投入应用的技术。 对可变焦距光学系统的需求 目前所有其他XR头…

国内信创web中间件生态

国内信创web中间件生态 东方通 官网https://www.tongtech.com/pctype/25.html 宝蓝德 官网https://www.bessystem.com/product/0ad9b8c4d6af462b8d15723a5f25a87d/info?p101 金蝶天燕 官网 https://www.apusic.com/list-117.html 中创 官网http://www.inforbus.com…

【C++】<知识点> 标准模板库STL(下)

文章目录 六、set与multiset 1. 常用成员函数 2. pair模板 3. set 4. multiset 七、map与multimap 1. map 2. multimap 3. 应用实例 八、容器适配器 1. stack 2. queue 3. priority_queue 九、算法 六、set与multiset 1. 常用成员函数 iterator find(const T&am…

(C11) 泛型表达式

文章目录 ⭐语法⭐举例&#x1f6a9;判断对象类型&#x1f6a9;判断指针&#x1f6a9;函数重载&#x1f6a9;嵌套使用 END ⭐语法 Ref: 泛型选择 (C11 起) - cppreference.com 关键词&#xff1a; Genericdefault _Generic(控制表达式 , 关联列表) (C11 起) 关联列表 类型名:…

SQLI-labs-第二十三关

第二十三关 目录 第二十三关 1、判断注入点 2、判断数据库 3、判断表名 4、判断字段名 5、获取数据库的信息 6、使用group_concat() 和concat_ws() 知识点&#xff1a;注释符过滤绕过 思路&#xff1a; 分析源码可知&#xff0c;使用了preg_replace()函数过滤了注释符…

计算机图形学入门01:概述

1.什么是图形学? The use of computers to synthesize and manipulate visual information. 图形学是合成和操纵视觉信息的计算机应用。 百度百科&#xff1a;计算机图形学(Computer Graphics&#xff0c;简称CG)是一种使用数学算法将二维或三维图形转化为计算机显示器的栅格…

2024年统计、数据分析与大数据技术国际会议(SDBT 2024)

2024年统计、数据分析与大数据技术国际会议&#xff08;SDBT 2024&#xff09; 2024 International Conference on Statistics, Data Analysis, and Big Data Technology 【重要信息】 大会地点&#xff1a;广州 大会时间&#xff1a;2024年7月22日 大会官网&#xff1a;http…

鸿蒙开发接口UI界面:【@ohos.router (页面路由)】

页面路由 说明开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。页面路由需要在页面渲染完…

VUE3+TS+elementplus+Django+MySQL实现从数据库读取数据,显示在前端界面上

一、前言 前面通过VUE3和elementplus创建了一个table&#xff0c;VUE3TSelementplus创建table&#xff0c;纯前端的table&#xff0c;以及使用VUE3TSelementplus创建一个增加按钮&#xff0c;使用前端的静态数据&#xff0c;显示在表格中。今天通过从后端获取数据来显示在表格…

Keras深度学习框架第二十四讲:KerasNLP概述

1、KerasNLP简介 KerasNLP是一个与TensorFlow深度集成的库&#xff0c;旨在简化NLP&#xff08;自然语言处理&#xff09;任务的建模过程。它提供了一系列高级API&#xff0c;用于预处理文本数据、构建序列模型和执行常见的NLP任务&#xff0c;如情感分析、命名实体识别和机器…

PgMP:项目集管理,哪些人适合学习?

美国项目管理协会&#xff08;PMI&#xff09;对项目集经理&#xff08;Program Manager&#xff09;的角色做出如下的定义&#xff1a; 在最少的领导/监督下&#xff0c;项目集经理PgMP负责在商业和组织目的下协调管理多个相关项目。这些项目含有跨部门、组织、地理区域…

C 基础环境配置(vscode || vs)

目录 一.发展 二. 环境设置 1.vs2022 2.vscode (1.)首先下载VsCode (2)安装vsCode插件 (3)下载MinGW-W64 (4)配置文件 (5)注意把里面配置的:mingw64路径改为自己的路径 (6)示例代码 三.总结 一.发展 编程语言的发展 机器语言(打孔纸带编程),汇编语言,高级语言,一步步…

猫耳 WebSocket 跨端优化实践

前言 在现代的移动应用程序中&#xff0c;长连接是一种不可或缺的能力&#xff0c;包括但不限于推送、实时通信、信令控制等常见场景。在猫耳FM的直播业务中&#xff0c;我们同样使用了 WebSocket 长连接作为我们实时通信的基础。 在我们推进用户体验优化的工作中&#xff0c;…

利用AI办公工具类API,大幅提高办公效率

AI办公工具类API是一项革命性的技术&#xff0c;利用人工智能的力量为办公场景提供了许多创新的解决方案。借助AI办公工具类API&#xff0c;用户可以实现自动化的文档处理、语音转文字、图像识别、数据分析等多种功能&#xff0c;大大提高了办公效率和工作质量。此外&#xff0…

LiveGBS流媒体平台GB/T28181用户手册-国标级联:添加上级平台、选择通道、推送通道级联会话、搜索、删除

LiveGBS流媒体平台GB/T28181用户手册-国标级联:添加上级平台、选择通道、推送通道级联会话、搜索、删除 1、国标级联1.1、添加上级平台1.2、注册状态1.3、选择通道1.4、推送通道1.5、级联会话1.6、搜索1.7、删除 2、搭建GB28181视频直播平台 1、国标级联 1.1、添加上级平台 点…

【golang学习之旅】go mod tidy

系列文章 【golang学习之旅】报错&#xff1a;a declared but not used 【golang学习之旅】Go 的基本数据类型 【golang学习之旅】深入理解字符串string数据类型 目录 系列文章go mod tidy的作用 go mod tidy的作用 把项目所依赖的包添加到go.mod文件中去掉go.mod文件中项目不…

使用 RT 矩阵进行 3D 点云变换详解(基于 PCL 和 Eigen 库)

在 3D 点云处理中&#xff0c;RT 矩阵是一个常用的工具&#xff0c;用于对点云进行旋转和平移操作。本文将详细介绍 RT 矩阵的概念&#xff0c;并通过一个示例程序演示如何基于 PCL 和 Eigen 库将一帧点云进行矩阵变换再输出。 本教程的示例代码和点云数据可在 GitHub 下载。 什…