当前位置: 代码迷 >> 综合 >> Spring Boot源码阅读(一)
  详细解决方案

Spring Boot源码阅读(一)

热度:87   发布时间:2023-10-14 01:52:50.0
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

这是一个标准的SpringBoot应用。main方法中调用了org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)方法,参数是当前类和main方法传入的参数。

    /*** Static helper that can be used to run a {@link SpringApplication} from the* specified source using default settings.* @param primarySource the primary source to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}/*** Static helper that can be used to run a {@link SpringApplication} from the* specified sources using default settings and user supplied arguments.* @param primarySources the primary sources to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}

 

通过查看该方法我们可以看到,实际的调用是创建了一个SpringApplication实例,接着调用该实例的run方法。接下来让我们来看看创建SpringApplication都做了哪些操作。

    /*** Create a new {@link SpringApplication} instance. The application context will load* beans from the specified primary sources (see {@link SpringApplication class-level}* documentation for details. The instance can be customized before calling* {@link #run(String...)}.* @param primarySources the primary bean sources* @see #run(Class, String[])* @see #SpringApplication(ResourceLoader, Class...)* @see #setSources(Set)*/public SpringApplication(Class<?>... primarySources) {this(null, primarySources);}/*** Create a new {@link SpringApplication} instance. The application context will load* beans from the specified primary sources (see {@link SpringApplication class-level}* documentation for details. The instance can be customized before calling* {@link #run(String...)}.* @param resourceLoader the resource loader to use* @param primarySources the primary bean sources* @see #run(Class, String[])* @see #setSources(Set)*/@SuppressWarnings({ "unchecked", "rawtypes" })public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 判断应用类型,普通应用 web应用 和 react-web应用this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

SpringApplication的构造方法主要做了一些初始化操作

  • 对实例变量中的资源加载器进行赋值
  • 判断主配置是否为空
  • 存储主配置到实例变量中去,以便后续使用
  • 判断应用类型,并存储到webApplicationType字段中去
  • 初始化Bootstraper的实现类,存储到bootstrappers字段中去(主要通过spring.factories配置文件要加载,用于定制BootstrapContext上下文,存储一些ApplicationContext初始化完成前的一些实例对象)
  • 设置ApplicationContextInitializer的实现类,用于定制ApplicationContext,加载方式同Bootstraper
  • 设置ApplicationListener的实现类,Spring监听器,加载方式同上
  • 最后对main方法入口类进行判断

让我们来看看SpringBoot是如何对应用的类型进行判断的

    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}

从代码中我们可以看到,通过应用的ClassPath中是否存在某几个类来判断具体应用类型。我们当前的应用没有引用任何的Web包,所以判断下来应该是WebApplicationType.NONE类型。

org.springframework.util.ClassUtils#isPresent判断某个类是否存在,主要通过java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)的方式来判断,若能没有抛出异常则判定该类是存在的,当然Spring在判断的健壮性上做了很多逻辑这里就不详细展开了,有兴趣的同学可以自己去看下源码。

    public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {try {forName(className, classLoader);return true;} catch (IllegalAccessError var3) {throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);} catch (Throwable var4) {return false;}}public static Class<?> forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError {Assert.notNull(name, "Name must not be null");Class<?> clazz = resolvePrimitiveClassName(name);if (clazz == null) {clazz = (Class)commonClassCache.get(name);}if (clazz != null) {return clazz;} else {Class elementClass;String elementName;if (name.endsWith("[]")) {elementName = name.substring(0, name.length() - "[]".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[L") && name.endsWith(";")) {elementName = name.substring("[L".length(), name.length() - 1);elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[")) {elementName = name.substring("[".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else {ClassLoader clToUse = classLoader;if (classLoader == null) {clToUse = getDefaultClassLoader();}try {return Class.forName(name, false, clToUse);} catch (ClassNotFoundException var9) {int lastDotIndex = name.lastIndexOf(46);if (lastDotIndex != -1) {String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);try {return Class.forName(innerClassName, false, clToUse);} catch (ClassNotFoundException var8) {}}throw var9;}}}}

接下来我们来看下Bootstrapper

/*** Callback interface that can be used to initialize a {@link BootstrapRegistry} before it* is used.** @author Phillip Webb* @since 2.4.0* @see SpringApplication#addBootstrapper(Bootstrapper)* @see BootstrapRegistry*/
public interface Bootstrapper {/*** Initialize the given {@link BootstrapRegistry} with any required registrations.* @param registry the registry to initialize*/void intitialize(BootstrapRegistry registry);}/*** A simple object registry that is available during startup and {@link Environment}* post-processing up to the point that the {@link ApplicationContext} is prepared.* <p>* Can be used to register instances that may be expensive to create, or need to be shared* before the {@link ApplicationContext} is available.* <p>* The registry uses {@link Class} as a key, meaning that only a single instance of a* given type can be stored.* <p>* The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener* that can perform actions when {@link BootstrapContext} has been closed and the* {@link ApplicationContext} is fully prepared. For example, an instance may choose to* register itself as a regular Spring bean so that it is available for the application to* use.** @author Phillip Webb* @since 2.4.0* @see BootstrapContext* @see ConfigurableBootstrapContext*/
public interface BootstrapRegistry {// ...略过该接口定义的方法
}

从这两个接口的注释中我们可以看出来,Bootstrapper的实现类主要用于对BootstrapRegistry实例进行一些初始化定制,而BootstrapRegistry主要用于在ApplicationContext初始化完成前注册一些实例来使用,并且可以添加一个监听器来监听BootstrapContext(我理解为应用启动期间的启动上下文环境)上下文关闭的事件,用于对BootstrapContext关闭时做一些操作,如将一些BootstrapContext期间注册的一些实例注册到ApplicationContext中去。

public class DemoBootstrapper implements Bootstrapper {@Overridepublic void intitialize(BootstrapRegistry registry) {registry.register(DemoBean.class, context -> {DemoBean demoBean = new DemoBean();demoBean.setText("demo");return demoBean;});registry.addCloseListener(event -> {BootstrapContext bootstrapContext = event.getBootstrapContext();ApplicationContext applicationContext = event.getApplicationContext();DemoBean demoBean = bootstrapContext.get(DemoBean.class);((ConfigurableApplicationContext)applicationContext).getBeanFactory().registerSingleton("demoBean", demoBean);});}
}// META-INF/spring.factories
org.springframework.boot.Bootstrapper=com.example.demo.bootstrap.DemoBootstrapper

如上面demo中所示的代码,就会在BootstrapContext中注册一个DemoBean,并且在BootstrapContext关闭时将DemoBean实例注册到ApplicationContext中去。SpringBoot如此设计为开发者提供了极高的可扩展性,如SpringCloud中就利用了该特性在应用初始化前做了一些拓展。

/*** Callback interface for initializing a Spring {@link ConfigurableApplicationContext}* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.** <p>Typically used within web applications that require some programmatic initialization* of the application context. For example, registering property sources or activating* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support* for declaring a "contextInitializerClasses" context-param and init-param, respectively.** <p>{@code ApplicationContextInitializer} processors are encouraged to detect* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been* implemented or if the {@link org.springframework.core.annotation.Order @Order}* annotation is present and to sort instances accordingly if so prior to invocation.** @author Chris Beams* @since 3.1* @param <C> the application context type* @see org.springframework.web.context.ContextLoader#customizeContext* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers*/
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {/*** Initialize the given application context.* @param applicationContext the application to configure*/void initialize(C applicationContext);}
ApplicationContextInitializer提供对ApplicationContext的初始化定制,在ApplicationContext#refresh之前。让我们看下面这个典型的例子,就能明白该接口可用于怎样的扩展了。

/*** {@link ApplicationContextInitializer} for setting the servlet context.** @author Dave Syer* @author Phillip Webb* @since 2.0.0*/
public class ServletContextApplicationContextInitializerimplements ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered {private int order = Ordered.HIGHEST_PRECEDENCE;private final ServletContext servletContext;private final boolean addApplicationContextAttribute;/*** Create a new {@link ServletContextApplicationContextInitializer} instance.* @param servletContext the servlet that should be ultimately set.*/public ServletContextApplicationContextInitializer(ServletContext servletContext) {this(servletContext, false);}/*** Create a new {@link ServletContextApplicationContextInitializer} instance.* @param servletContext the servlet that should be ultimately set.* @param addApplicationContextAttribute if the {@link ApplicationContext} should be* stored as an attribute in the {@link ServletContext}* @since 1.3.4*/public ServletContextApplicationContextInitializer(ServletContext servletContext,boolean addApplicationContextAttribute) {this.servletContext = servletContext;this.addApplicationContextAttribute = addApplicationContextAttribute;}public void setOrder(int order) {this.order = order;}@Overridepublic int getOrder() {return this.order;}@Overridepublic void initialize(ConfigurableWebApplicationContext applicationContext) {applicationContext.setServletContext(this.servletContext);if (this.addApplicationContextAttribute) {this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,applicationContext);}}}
ServletContextApplicationContextInitializer将servletContext存储到ApplicationContext的实例变量中,并且如果addApplicationContextAttribute为true,会将整个ApplicationContext置入到servletContext中。

ApplicationListener想必大家都已经比较熟悉了,这里就不展开叙述了。

接下来让我们看看SpringBoot在org.springframework.boot.SpringApplication#run(java.lang.String...)方法具体做了哪些操作

    /*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {// 应用初始化计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 创建一个初始化上下文环境DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;// 配置Headless模式,在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。configureHeadlessProperty();// 加载SpringApplicationRunListener监听器SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 封装main方法传入的参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 创建Spring环境信息ConfigurableEnvironmentConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 打印SpringBoot BannerBanner printedBanner = printBanner(environment);// 创建ApplicationContextcontext = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 配置ApplicationContextprepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 刷新ApplicationContextrefreshContext(context);// 刷新后的一些处理afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 通知SpringApplicationRunListener ApplicationContext启动完成listeners.started(context);// 执行RunnercallRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 通知SpringApplicationRunListener ApplicationContext运行中listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}

从代码上来看整体逻辑还是比较清晰的,方法调用上基本都能见名知意,就让我们来看几个关键的方法。

先来看看DefaultBootstrapContext bootstrapContext = createBootstrapContext();

private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));return bootstrapContext;
}

比较简单,就创建了一个DefaultBootstrapContext对象,然后循环调用SpringApplication构造方法中加载并存储的Bootstrapper实现类。

接下来我们来看下SpringApplicationRunListeners listeners = getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),this.applicationStartup);
}

SpringApplicationRunListeners 是一个封装对象,用于封装SpringApplicationRunListener集合,采用面向对象的思想,隐藏了多个SpringApplicationRunListener循环监听某个事件的具体细节,使代码更加清晰,可扩展性更高。

/*** Listener for the {@link SpringApplication} {@code run} method.* {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}* and should declare a public constructor that accepts a {@link SpringApplication}* instance and a {@code String[]} of arguments. A new* {@link SpringApplicationRunListener} instance will be created for each run.** @author Phillip Webb* @author Dave Syer* @author Andy Wilkinson* @since 1.0.0*/
public interface SpringApplicationRunListener {/*** Called immediately when the run method has first started. Can be used for very* early initialization.* @param bootstrapContext the bootstrap context*/default void starting(ConfigurableBootstrapContext bootstrapContext) {starting();}/*** Called immediately when the run method has first started. Can be used for very* early initialization.* @deprecated since 2.4.0 in favor of {@link #starting(ConfigurableBootstrapContext)}*/@Deprecateddefault void starting() {}/*** Called once the environment has been prepared, but before the* {@link ApplicationContext} has been created.* @param bootstrapContext the bootstrap context* @param environment the environment*/default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {environmentPrepared(environment);}/*** Called once the environment has been prepared, but before the* {@link ApplicationContext} has been created.* @param environment the environment* @deprecated since 2.4.0 in favor of* {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}*/@Deprecateddefault void environmentPrepared(ConfigurableEnvironment environment) {}/*** Called once the {@link ApplicationContext} has been created and prepared, but* before sources have been loaded.* @param context the application context*/default void contextPrepared(ConfigurableApplicationContext context) {}/*** Called once the application context has been loaded but before it has been* refreshed.* @param context the application context*/default void contextLoaded(ConfigurableApplicationContext context) {}/*** The context has been refreshed and the application has started but* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner* ApplicationRunners} have not been called.* @param context the application context.* @since 2.0.0*/default void started(ConfigurableApplicationContext context) {}/*** Called immediately before the run method finishes, when the application context has* been refreshed and all {@link CommandLineRunner CommandLineRunners} and* {@link ApplicationRunner ApplicationRunners} have been called.* @param context the application context.* @since 2.0.0*/default void running(ConfigurableApplicationContext context) {}/*** Called when a failure occurs when running the application.* @param context the application context or {@code null} if a failure occurred before* the context was created* @param exception the failure* @since 2.0.0*/default void failed(ConfigurableApplicationContext context, Throwable exception) {}}

SpringApplicationRunListener定义了SpringApplication各个生命周期的监听方法,如开始运行、环境加载完毕、上下文创建等。EventPublishingRunListener就是一个实现该接口来完成广播Spring事件的实现类,后面会对该类进行详细的解析。

所以listeners.starting(bootstrapContext, this.mainApplicationClass);方法的作用就比较明了了。

private final List<SpringApplicationRunListener> listeners;void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});
}private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,Consumer<StartupStep> stepAction) {StartupStep step = this.applicationStartup.start(stepName);this.listeners.forEach(listenerAction);if (stepAction != null) {stepAction.accept(step);}step.end();
}

最主要的就是这段代码,this.listeners.forEach(listenerAction),循环调用SpringApplicationRunListenerstarting方法。

这里引入了一个类ApplicationStartup,主要用于在应用程序启动期间标记步骤,并收集有关执行上下文或其处理时间的数据的,这里就不展开叙述了。

接下来我们来看下一个比较关键的方法,ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// 创建ConfigurableEnvironment实例ConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);configureAdditionalProfiles(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}// 根据前面检测的webApplicationType判断创建哪种环境
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}switch (this.webApplicationType) {case SERVLET:return new StandardServletEnvironment();case REACTIVE:return new StandardReactiveWebEnvironment();default:return new StandardEnvironment();}
}

getOrCreateEnvironment();比较简单,主要根据之前检测的webApplicationType来判断创建哪种类型的Environment,我们这里的话创建得到是StandardEnvironment

我们着重来看下configureEnvironment(environment, applicationArguments.getSourceArgs());都做了些什么。

    /*** Template method delegating to* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.* Override this method for complete control over Environment customization, or one of* the above for fine-grained control over property sources or profiles, respectively.* @param environment this application's environment* @param args arguments passed to the {@code run} method* @see #configureProfiles(ConfigurableEnvironment, String[])* @see #configurePropertySources(ConfigurableEnvironment, String[])*/protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {// 设置ConversionServiceif (this.addConversionService) {ConversionService conversionService = ApplicationConversionService.getSharedInstance();environment.setConversionService((ConfigurableConversionService) conversionService);}configurePropertySources(environment, args);configureProfiles(environment, args);}/*** Add, remove or re-order any {@link PropertySource}s in this application's* environment.* @param environment this application's environment* @param args arguments passed to the {@code run} method* @see #configureEnvironment(ConfigurableEnvironment, String[])*/protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {// 添加SpringApplication中的defaultPropertiesMutablePropertySources sources = environment.getPropertySources();if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));}// 如果需要添加命令参数到环境中并且命令参数(main方法传入的参数)个数大于0if (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));}}}/*** Configure which profiles are active (or active by default) for this application* environment. Additional profiles may be activated during configuration file* processing via the {@code spring.profiles.active} property.* @param environment this application's environment* @param args arguments passed to the {@code run} method* @see #configureEnvironment(ConfigurableEnvironment, String[])* @see org.springframework.boot.context.config.ConfigFileApplicationListener*/protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {// 将SpringApplication传入的additionalProfiles置入EnvironmentSet<String> profiles = new LinkedHashSet<>(this.additionalProfiles);profiles.addAll(Arrays.asList(environment.getActiveProfiles()));environment.setActiveProfiles(StringUtils.toStringArray(profiles));}

主要做了如下几个操作:

  • 将设置的defaultProperties置入Environment。(我们可以通过org.springframework.boot.SpringApplication#setDefaultProperties(java.util.Map<java.lang.String,java.lang.Object>)或者org.springframework.boot.SpringApplication#setDefaultProperties(java.util.Properties)传入该字段)
  • 将命令参数(main方法传入的参数)置入Environment。如果已经存在了,则进行合并置入。
  • additionalProfiles置入到EnvironmentActiveProfiles列表中。(我们可以通过org.springframework.boot.SpringApplication#setAdditionalProfiles传入该字段)

接下来逻辑就比较简单了,主要对Environment进行挂载和通知org.springframework.boot.SpringApplicationRunListeners环境准备完毕。

  相关解决方案