/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot;
import java.lang.reflect.Constructor;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.CachedIntrospectionResults;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.Banner.Mode;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.env.CommandLinePropertySource;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.StandardServletEnvironment;
/**
*可用于从 Java 主程序引导和启动 Spring 应用程序的类
* 方法。默认情况下,类将执行以下步骤来引导您的
* 应用:
*
* <ul>
* <li>创建一个合适的 {@link ApplicationContext} 实例(取决于你的
* 类路径)</li>
* <li>注册一个 {@link CommandLinePropertySource} 以将命令行参数公开为
* 弹簧属性</li>
* <li>刷新应用上下文,加载所有单例bean</li>
* <li>触发任何 {@link CommandLineRunner} bean</li>
* </ul>
*
* 在大多数情况下,可以调用静态 {@link #run(Class, String[])} 方法
* 直接从您的 {@literal main} 方法引导您的应用程序:
*
* <pre class="code">
* @配置
* @EnableAutoConfiguration
* 公共类 MyApplication {
*
* // ... Bean 定义
*
* public static void main(String[] args) {
* SpringApplication.run(MyApplication.class, args);
* }
* }
* </pre>
*
* <p>
* 对于更高级的配置,可以创建一个 {@link SpringApplication} 实例并
* 运行前自定义:
*
* <pre class="code">
* public static void main(String[] args) {
* SpringApplication application = new SpringApplication(MyApplication.class);
* // ... 在此处自定义应用程序设置
* application.run(args)
* }
* </pre>
*
* {@link SpringApplication} 可以从各种不同的来源读取 bean。这是
* 一般建议使用单个 {@code @Configuration} 类来引导
* 您的应用程序,但是,您也可以从以下位置设置 {@link #getSources() 源}:
* <ul>
* <li>要加载的完全限定类名
* {@link AnnotatedBeanDefinitionReader}</li>
* <li>要由 {@link XmlBeanDefinitionReader} 加载的 XML 资源的位置,或
* 一个由 {@link GroovyBeanDefinitionReader} 加载的 groovy 脚本</li>
* <li>要被{@link ClassPathBeanDefinitionScanner}扫描的包名</li>
* </ul>
*
* 配置属性也绑定到 {@link SpringApplication}。这使它
* 可以动态设置 {@link SpringApplication} 属性,如附加
* 来源 ("spring.main.sources" - 一个 CSV 列表) 表示网络环境的标志
* ("spring.main.web-application-type=none") 或关闭横幅的标志
*(“spring.main.banner-mode=off”)。
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @author Christian Dupuis
* @author Stephane Nicoll
* @author Jeremy Rickard
* @author Craig Burke
* @author Michael Simons
* @author Madhura Bhave
* @author Brian Clozel
* @author Ethan Rubinson
* @since 1.0.0
* @see #run(Class, String[])
* @see #run(Class[], String[])
* @see #SpringApplication(Class...)
*/
public class SpringApplication {
/**
* 默认情况下将用于非Web *环境的应用程序上下文的类名称。
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* Web *环境默认使用的应用程序上下文的类名称。
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* 默认情况下,用于响应式Web 环境的应用程序上下文的类名称。
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
/**
* 默认横幅位置。
*/
public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
/**
* 标语位置属性键。
*/
public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private static final Log logger = LogFactory.getLog(SpringApplication.class);
private Set<Class<?>> primarySources;
private Set<String> sources = new LinkedHashSet<>();
private Class<?> mainApplicationClass;
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
private boolean logStartupInfo = true;
private boolean addCommandLineProperties = true;
private boolean addConversionService = true;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
private WebApplicationType webApplicationType;
private boolean headless = true;
private boolean registerShutdownHook = true;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private Set<String> additionalProfiles = new HashSet<>();
private boolean allowBeanDefinitionOverriding;
private boolean isCustomEnvironment = false;
private boolean lazyInitialization = false;
/**
* 创建一个新的{@link SpringApplication}实例。应用程序上下文将从指定的主要来源加载*
* bean(有关详细信息,请参见{@link SpringApplication class-level}
* *文档。可以在调用之前自定义实例。
* {@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);
}
/**
* 创建一个新的 {@link SpringApplication} 实例。应用程序上下文将加载
* 来自指定主要来源的 bean(参见 {@link SpringApplication class-level}
* 有关详细信息的文档。调用前可以自定义实例
* {@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环境类型,
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从spring.factories文件中加载ApplicationContextInitializer初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从spring.factories文件中加载ApplicationListener初始化器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//根据堆栈跟踪信息获取执行的main方法的class
this.mainApplicationClass = deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
/**
* 运行 Spring 应用程序,创建并刷新一个新的
* {@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();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//在spring.factories文件获取配置的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//调用所有监听器的starting方法
listeners.starting();
try {
//将参数封装为applicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建配置环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//环境中设置忽略的属性
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//创建webServlet上下文环境
context = createApplicationContext();
//在spring.factories文件获取SpringBootExceptionReporter的配置
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
//准备上下文环境
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文环境
refreshContext(context);
//刷新后处理
afterRefresh(context, applicationArguments);
//计时器结束
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//执行监听器的started运行方法
listeners.started(context);
//通知runner,启动后执行
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//执行监听器的running方法
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建和配置环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
switch (this.webApplicationType) {
case SERVLET:
return StandardServletEnvironment.class;
case REACTIVE:
return StandardReactiveWebEnvironment.class;
default:
return StandardEnvironment.class;
}
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置上下文环境
context.setEnvironment(environment);
//上下文后处理,子类可对其进行拓展
postProcessApplicationContext(context);
//调用容器初始化监听器接口,ApplicationContextInitializer
applyInitializers(context);
//容器上下文准备时调用
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加启动特定的单例 bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//如果延迟加载设置添加beanFactory后置处理器LazyInitializationBeanFactoryPostProcessor
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
//获取所有的源,源是在初始化的时候源就是传入的实现了main方法启动的class
//这里也可以传入其他的类型,
//一般用的是javaConfig
//可以是处理Package,SpringApplication.run(Package.getPackage("com.leone.chapter.profiles"), args);
//可以是处理Resource,new ClassPathResource("applicationContext.xml")
//可以是处理String类型,SpringApplication.run("classpath:/applicationContext.xml", args);
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//调用spring运行监听器的contextLoaded方法
listeners.contextLoaded(context);
}
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[]{});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
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();
}
}
/**
* 委托给的模板方法
* {@link #configurePropertySources(ConfigurableEnvironment, String[])} 和
* {@link #configureProfiles(ConfigurableEnvironment, String[])} 按此顺序。
* 覆盖此方法以完全控制环境自定义,或其中之一
* 以上分别用于对属性源或配置文件进行细粒度控制。
*
* @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) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
/**
* 添加、删除或重新排序此应用程序中的任何 {@link PropertySource}
* 环境。
*
* @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) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
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));
}
}
}
/**
* 配置此应用程序哪些配置文件处于活动状态(或默认情况下处于活动状态)
* 环境。在配置文件期间可能会激活其他配置文件
* 通过 {@code spring.profiles.active} 属性处理。
*
* @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) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
/**
* 将环境绑定到 {@link SpringApplication}。
*
* @param environment the environment to bind
*/
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);
}
}
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
/**
* 用于创建 {@link ApplicationContext} 的策略方法。默认这个
* 方法将尊重任何明确设置的应用程序上下文或应用程序上下文
* 回退到合适的默认值之前的类。
*
* @return the application context (not yet refreshed)
* @see #setApplicationContextClass(Class)
*/
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
/**
* 应用任何相关的后处理 {@link ApplicationContext}。子类可以
* 根据需要应用额外的处理。
*
* @param context the application context
*/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
/**
* 将任何 {@link ApplicationContextInitializer} 应用到上下文之前
* 焕然一新。
*
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({"rawtypes", "unchecked"})
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
/**
* 调用以记录启动信息,子类可以重写以添加额外的
* 记录。
*
* @param isRoot true if this application is the root of a context hierarchy
*/
protected void logStartupInfo(boolean isRoot) {
if (isRoot) {
new StartupInfoLogger(this.mainApplicationClass).logStarting(getApplicationLog());
}
}
/**
* 调用以记录活动配置文件信息。
*
* @param context the application context
*/
protected void logStartupProfileInfo(ConfigurableApplicationContext context) {
Log log = getApplicationLog();
if (log.isInfoEnabled()) {
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
if (ObjectUtils.isEmpty(activeProfiles)) {
String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
log.info("No active profile set, falling back to default profiles: "
+ StringUtils.arrayToCommaDelimitedString(defaultProfiles));
} else {
log.info("The following profiles are active: "
+ StringUtils.arrayToCommaDelimitedString(activeProfiles));
}
}
}
/**
* 返回应用程序的 {@link Log}。默认会推导出来。
*
* @return the application log
*/
protected Log getApplicationLog() {
if (this.mainApplicationClass == null) {
return logger;
}
return LogFactory.getLog(this.mainApplicationClass);
}
/**
* 将 bean 加载到应用程序上下文中。
*
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
/**
* 将在 ApplicationContext 中使用的 ResourceLoader。
*
* @return the resourceLoader the resource loader that will be used in the
* ApplicationContext (or null if the default)
*/
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
/**
* 将在 ApplicationContext 中使用的 ClassLoader(如果
* {@link #setResourceLoader(ResourceLoader) resourceLoader} 已设置,或上下文
* 类加载器(如果不为 null),或 Spring {@link ClassUtils} 类的加载器。
*
* @return a ClassLoader (never null)
*/
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
/**
* 获取 bean 定义注册表。
*
* @param context the application context
* @return the BeanDefinitionRegistry if it can be determined
*/
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
/**
* 用于创建 {@link BeanDefinitionLoader} 的工厂方法。
*
* @param registry the bean definition registry
* @param sources the sources to load
* @return the {@link BeanDefinitionLoader} that will be used to load beans
*/
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
return new BeanDefinitionLoader(registry, sources);
}
/**
* 刷新底层 {@link ApplicationContext}。
*
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
/**
* 在上下文刷新后调用。
*
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
try {
for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
registerLoggedException(failure);
return;
}
}
} catch (Throwable ex) {
// Continue with normal handling of the original failure
}
if (logger.isErrorEnabled()) {
logger.error("Application run failed", failure);
registerLoggedException(failure);
}
}
/**
* 注册给定的异常已被记录。默认情况下,如果运行
* 主线程,此方法将抑制堆栈跟踪的额外打印。
*
* @param exception the exception that was logged
*/
protected void registerLoggedException(Throwable exception) {
SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
if (handler != null) {
handler.registerLoggedException(exception);
}
}
private void handleExitCode(ConfigurableApplicationContext context, Throwable exception) {
int exitCode = getExitCodeFromException(context, exception);
if (exitCode != 0) {
if (context != null) {
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
if (handler != null) {
handler.registerExitCode(exitCode);
}
}
}
private int getExitCodeFromException(ConfigurableApplicationContext context, Throwable exception) {
int exitCode = getExitCodeFromMappedException(context, exception);
if (exitCode == 0) {
exitCode = getExitCodeFromExitCodeGeneratorException(exception);
}
return exitCode;
}
private int getExitCodeFromMappedException(ConfigurableApplicationContext context, Throwable exception) {
if (context == null || !context.isActive()) {
return 0;
}
ExitCodeGenerators generators = new ExitCodeGenerators();
Collection<ExitCodeExceptionMapper> beans = context.getBeansOfType(ExitCodeExceptionMapper.class).values();
generators.addAll(exception, beans);
return generators.getExitCode();
}
private int getExitCodeFromExitCodeGeneratorException(Throwable exception) {
if (exception == null) {
return 0;
}
if (exception instanceof ExitCodeGenerator) {
return ((ExitCodeGenerator) exception).getExitCode();
}
return getExitCodeFromExitCodeGeneratorException(exception.getCause());
}
SpringBootExceptionHandler getSpringBootExceptionHandler() {
if (isMainThread(Thread.currentThread())) {
return SpringBootExceptionHandler.forCurrentThread();
}
return null;
}
private boolean isMainThread(Thread currentThread) {
return ("main".equals(currentThread.getName()) || "restartedMain".equals(currentThread.getName()))
&& "main".equals(currentThread.getThreadGroup().getName());
}
/**
* 返回已推导或显式配置的主应用程序类。
*
* @return the main application class or {@code null}
*/
public Class<?> getMainApplicationClass() {
return this.mainApplicationClass;
}
/**
* 设置将用作日志源的特定主应用程序类
* 获取版本信息。默认情况下,将推导出主应用程序类。
* 如果没有明确的应用程序类,可以设置为 {@code null}。
*
* @param mainApplicationClass the mainApplicationClass to set or {@code null}
*/
public void setMainApplicationClass(Class<?> mainApplicationClass) {
this.mainApplicationClass = mainApplicationClass;
}
/**
* 返回正在运行的 Web 应用程序的类型。
*
* @return the type of web application
* @since 2.0.0
*/
public WebApplicationType getWebApplicationType() {
return this.webApplicationType;
}
/**
* 设置要运行的 Web 应用程序的类型。如果没有明确设置网页的类型
* 应用程序将根据类路径推导出来。
*
* @param webApplicationType the web application type
* @since 2.0.0
*/
public void setWebApplicationType(WebApplicationType webApplicationType) {
Assert.notNull(webApplicationType, "WebApplicationType must not be null");
this.webApplicationType = webApplicationType;
}
/**
* 通过注册具有相同名称的定义来设置是否覆盖 bean 定义
* 作为现有定义,应该被允许。默认为 {@code false}。
*
* @param allowBeanDefinitionOverriding if overriding is allowed
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean)
* @since 2.1.0
*/
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
}
/**
* 设置 bean 是否应该延迟初始化。默认为 {@code false}。
*
* @param lazyInitialization if initialization should be lazy
* @see BeanDefinition#setLazyInit(boolean)
* @since 2.2
*/
public void setLazyInitialization(boolean lazyInitialization) {
this.lazyInitialization = lazyInitialization;
}
/**
* 设置应用程序是否无头并且不应实例化 AWT。默认为
* {@code true} 防止出现 java 图标。
*
* @param headless if the application is headless
*/
public void setHeadless(boolean headless) {
this.headless = headless;
}
/**
* 设置创建的 {@link ApplicationContext} 是否应该有一个关闭钩子
* 挂号的。默认为 {@code true} 以确保处理 JVM 关闭
* 优雅。
*
* @param registerShutdownHook if the shutdown hook should be registered
*/
public void setRegisterShutdownHook(boolean registerShutdownHook) {
this.registerShutdownHook = registerShutdownHook;
}
/**
* 设置 {@link Banner} 实例,当没有时将用于打印横幅
* 提供静态横幅文件。
*
* @param banner the Banner instance to use
*/
public void setBanner(Banner banner) {
this.banner = banner;
}
/**
* 设置应用程序运行时用于显示横幅的模式。默认为
* {@code Banner.Mode.CONSOLE}。
*
* @param bannerMode the mode used to display the banner
*/
public void setBannerMode(Banner.Mode bannerMode) {
this.bannerMode = bannerMode;
}
/**
* 设置应用程序启动时是否应记录应用程序信息。
* 默认为 {@code true}。
*
* @param logStartupInfo if startup info should be logged.
*/
public void setLogStartupInfo(boolean logStartupInfo) {
this.logStartupInfo = logStartupInfo;
}
/**
* 设置是否应将 {@link CommandLinePropertySource} 添加到应用程序中
* 上下文以公开参数。默认为 {@code true}。
*
* @param addCommandLineProperties if command line arguments should be exposed
*/
public void setAddCommandLineProperties(boolean addCommandLineProperties) {
this.addCommandLineProperties = addCommandLineProperties;
}
/**
* 设置是否应将 {@link ApplicationConversionService} 添加到应用程序中
* 上下文的 {@link Environment}。
*
* @param addConversionService if the application conversion service should be added
* @since 2.1.0
*/
public void setAddConversionService(boolean addConversionService) {
this.addConversionService = addConversionService;
}
/**
* 设置默认环境属性,除了在
* 现有 {@link Environment}。
*
* @param defaultProperties the additional properties to set
*/
public void setDefaultProperties(Map<String, Object> defaultProperties) {
this.defaultProperties = defaultProperties;
}
/**
* {@link #setDefaultProperties(Map)} 的便捷替代方案。
*
* @param defaultProperties some {@link Properties}
*/
public void setDefaultProperties(Properties defaultProperties) {
this.defaultProperties = new HashMap<>();
for (Object key : Collections.list(defaultProperties.propertyNames())) {
this.defaultProperties.put((String) key, defaultProperties.get(key));
}
}
/**
* 设置要使用的其他配置文件值(在系统或命令行中设置的那些值之上
* 特性)。
*
* @param profiles the additional profiles to set
*/
public void setAdditionalProfiles(String... profiles) {
this.additionalProfiles = new LinkedHashSet<>(Arrays.asList(profiles));
}
/**
* 设置生成 bean 名称时应使用的 bean 名称生成器。
*
* @param beanNameGenerator the bean name generator
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator = beanNameGenerator;
}
/**
* 设置应该与创建的应用程序一起使用的底层环境
* 语境。
*
* @param environment the environment
*/
public void setEnvironment(ConfigurableEnvironment environment) {
this.isCustomEnvironment = true;
this.environment = environment;
}
/**
* 向主要来源添加其他项目,这些项目将被添加到
* ApplicationContext 当 {@link #run(String...)} 被调用时。
* <p>
* 此处的源添加到构造函数中设置的源中。大多数用户
* 应该考虑使用 {@link #getSources()}/{@link #setSources(Set)} 而不是
* 调用这个方法。
*
* @param additionalPrimarySources the additional primary sources to add
* @see #SpringApplication(Class...)
* @see #getSources()
* @see #setSources(Set)
* @see #getAllSources()
*/
public void addPrimarySources(Collection<Class<?>> additionalPrimarySources) {
this.primarySources.addAll(additionalPrimarySources);
}
/**
* 返回将添加到 ApplicationContext 的一组可变源
* 当 {@link #run(String...)} 被调用时。
* <p>
* 此处设置的源除了在
* 构造函数。
*
* @return the application sources.
* @see #SpringApplication(Class...)
* @see #getAllSources()
*/
public Set<String> getSources() {
return this.sources;
}
/**
* 设置将用于创建 ApplicationContext 的其他源。来源
* 可以是:类名、包名或 XML 资源位置。
* <p>
* 此处设置的源除了在
* 构造函数。
*
* @param sources the application sources to set
* @see #SpringApplication(Class...)
* @see #getAllSources()
*/
public void setSources(Set<String> sources) {
Assert.notNull(sources, "Sources must not be null");
this.sources = new LinkedHashSet<>(sources);
}
/**
* 返回将被添加到一个的所有源的不可变集合
* ApplicationContext 当 {@link #run(String...)} 被调用时。这种方法结合了任何
* 在构造函数中指定的主要来源以及具有
* 已{@link #setSources(Set) 显式设置}。
*
* @return an immutable set of all sources
*/
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
/**
* 设置加载资源时应使用的 {@link ResourceLoader}。
*
* @param resourceLoader the resource loader
*/
public void setResourceLoader(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
/**
* 设置将创建的 Spring {@link ApplicationContext} 的类型。如果不
* 指定默认为 {@link #DEFAULT_SERVLET_WEB_CONTEXT_CLASS} 基于网络
* 非基于 Web 的应用程序或 {@link AnnotationConfigApplicationContext}
* 应用程序。
*
* @param applicationContextClass the context class to set
*/
public void setApplicationContextClass(Class<? extends ConfigurableApplicationContext> applicationContextClass) {
this.applicationContextClass = applicationContextClass;
this.webApplicationType = WebApplicationType.deduceFromApplicationContext(applicationContextClass);
}
/**
* 设置将应用于 Spring 的 {@link ApplicationContextInitializer}
* {@link ApplicationContext}。
*
* @param initializers the initializers to set
*/
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
/**
* Add {@link ApplicationContextInitializer} 将应用于 Spring
* {@link ApplicationContext}。
*
* @param initializers the initializers to add
*/
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
this.initializers.addAll(Arrays.asList(initializers));
}
/**
* 返回 {@link ApplicationContextInitializer} 的只读有序集合
* 将应用于 Spring {@link ApplicationContext}。
*
* @return the initializers
*/
public Set<ApplicationContextInitializer<?>> getInitializers() {
return asUnmodifiableOrderedSet(this.initializers);
}
/**
* 设置将应用于 SpringApplication 的 {@link ApplicationListener}
* 并注册到 {@link ApplicationContext}。
*
* @param listeners the listeners to set
*/
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
/**
* 添加 {@link ApplicationListener} 以应用于 SpringApplication 和
* 注册到 {@link ApplicationContext}。
*
* @param listeners the listeners to add
*/
public void addListeners(ApplicationListener<?>... listeners) {
this.listeners.addAll(Arrays.asList(listeners));
}
/**
* 返回 {@link ApplicationListener} 的只读有序集合,这些 {@link ApplicationListener} 将被
* 应用于 SpringApplication 并注册到 {@link ApplicationContext}
* .
*
* @return the listeners
*/
public Set<ApplicationListener<?>> getListeners() {
return asUnmodifiableOrderedSet(this.listeners);
}
/**
* 可用于运行 {@link SpringApplication} 的静态助手
* 使用默认设置指定源。
*
* @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);
}
/**
* 可用于运行 {@link SpringApplication} 的静态助手
* 使用默认设置和用户提供的参数指定源。
*
* @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);
}
/**
* 可用于启动应用程序的基本主程序。这种方法在以下情况下很有用
* 应用程序源是通过 {@literal --spring.main.sources} 命令行定义的
* 争论。
* <p>
* 大多数开发人员都希望定义自己的 main 方法并调用
* {@link #run(Class, String...) run} 方法代替。
*
* @param args command line arguments
* @throws Exception if the application cannot be started
* @see SpringApplication#run(Class[], String[])
* @see SpringApplication#run(Class, String...)
*/
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class<?>[0], args);
}
/**
* 可用于退出 {@link SpringApplication} 并获取
* 表示成功 (0) 或其他的代码。不抛出异常但应该
* 打印任何遇到的堆栈跟踪。应用指定的
* {@link ExitCodeGenerator} 除了任何实现
* {@link ExitCodeGenerator}。在多个退出代码的情况下,最高值
* 将被使用(或者如果所有值都是负数,则将使用最小值)
*
* @param context the context to close if possible
* @param exitCodeGenerators exist code generators
* @return the outcome (0 if successful)
*/
public static int exit(ApplicationContext context, ExitCodeGenerator... exitCodeGenerators) {
Assert.notNull(context, "Context must not be null");
int exitCode = 0;
try {
try {
ExitCodeGenerators generators = new ExitCodeGenerators();
Collection<ExitCodeGenerator> beans = context.getBeansOfType(ExitCodeGenerator.class).values();
generators.addAll(exitCodeGenerators);
generators.addAll(beans);
exitCode = generators.getExitCode();
if (exitCode != 0) {
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
} finally {
close(context);
}
} catch (Exception ex) {
ex.printStackTrace();
exitCode = (exitCode != 0) ? exitCode : 1;
}
return exitCode;
}
private static void close(ApplicationContext context) {
if (context instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext closable = (ConfigurableApplicationContext) context;
closable.close();
}
}
private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
List<E> list = new ArrayList<>(elements);
list.sort(AnnotationAwareOrderComparator.INSTANCE);
return new LinkedHashSet<>(list);
}
}
springboot源码-SpringApplication
最新推荐文章于 2022-06-21 19:03:38 发布