02-SC
1. new SpringApplication()
package com.listao.spring_boot;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ServletComponentScan
@ComponentScan("com.listao")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
public class SpringApplication {
private Set<Class<?>> primarySources; // com.listao.spring_boot.Application
private WebApplicationType webApplicationType;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Class<?> mainApplicationClass;
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 1. 初始化primarySources集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2... WebApplicationType
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3.. 从`META-INF/spring.factories`读取实例名称集合,set去重并实例化
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 4.. 推断主入口应用类,通过当前调用栈,获取Main方法所在类
// com.listao.spring_boot.Application
this.mainApplicationClass = deduceMainApplicationClass();
}
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
// 1.
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
// 1... SpringFactoriesLoader 读取`META-INF/spring.factories`
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2.. 实例化`META-INF/spring.factories`
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 {
// 1.
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 2.
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
// 推断主入口应用类,通过当前调用栈,获取Main方法所在类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 1.
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
}
1. WebApplicationType
- 在上述构造方法中,判断当前应用程序的类型:
public enum WebApplicationType {
/** 1. 非web项目
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/** 2. servlet_web项目
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/** 3. 响应式web项目
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
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;
}
}
// 1.
return WebApplicationType.SERVLET;
}
}
2. SpringFactoriesLoader
- 加载
spring.factories
public final class SpringFactoriesLoader {
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 1..
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 加载`spring.factories`
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
// 1.
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : // META-INF/spring.factories
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 2.
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
// 3.
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
// ------------------------------------------------------------------------------------------------
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 1..
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
// 2..
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
// 1. 反射实例化
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
}
3. ApplicationContextInitializer
spring-boot-2.2.2.RELEASE.jar
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure-2.2.2.RELEASE.jar
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
4. ApplicationListener
spring-boot-2.2.2.RELEASE.jar
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
spring-boot-autoconfigure-2.2.2.RELEASE.jar
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
2. springApplication.run()
- SpringBoot启动运行方法,为各种运行环境进行准备
public class SpringApplication {
/**
* 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) {
// 1. 创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 初始化ApplicationContext和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 3. 设置系统属性`java.awt.headless`的值
configureHeadlessProperty();
// 4. `spring.factories`中获取`EventPublishingRunListener`,封装进SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 5. 初始化默认应用参数类,处理命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6. 根据监听器、应用参数准备Spring环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 将要忽略的bean的参数打开
configureIgnoreBeanInfo(environment);
// 7. 创建banner打印类
Banner printedBanner = printBanner(environment);
// 8. 创建应用上下文,可以理解为创建一个容器
context = createApplicationContext();
// 9. 准备异常报告器,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 10. 准备应用上下文。将启动类注入容器,为后续开启自动化提供基础
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11. 刷新应用上下文
refreshContext(context);
// 12. 应用上下文刷新后置处理,做一些扩展功能
afterRefresh(context, applicationArguments);
// 13. 停止计时监控类
stopWatch.stop();
// 14. 输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 15. 发布`ApplicationStartedEvent`事件
listeners.started(context);
// 16. 执行所有的Runner运行器
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 17. 发布`ApplicationReadyEvent`事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 18. 返回context
return context;
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置propertySources
configurePropertySources(environment, args);
// 配置profiles
configureProfiles(environment, args);
}
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;
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 应用上下文的environment
context.setEnvironment(environment);
// 应用上下文后处理
postProcessApplicationContext(context);
// 为上下文应用所有初始化器,执行容器中的applicationContextInitializer(spring.factories的实例),将所有的初始化对象放置到context对象中
applyInitializers(context);
// 触发所有SpringApplicationRunListener监听器的ContextPrepared事件方法。添加所有的事件监听器
listeners.contextPrepared(context);
// 记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 注册启动参数bean,将容器指定的参数封装成bean,注入容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 设置banner
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 加载所有资源,指的是启动器指定的参数
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将bean加载到上下文中
load(context, sources.toArray(new Object[0]));
// 触发所有springapplicationRunListener监听器的contextLoaded事件方法
listeners.contextLoaded(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());
}
}
/** 将启动器类加载到spring容器中,为后续的自动化配置奠定基础,之前看到的很多注解也与此相关
* Load beans into the application context.
* @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();
}
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
/** 当前方法的代码是空的,可以做一些自定义的后置处理操作
* Called after the context has been refreshed.
* @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);
}
}
}
}
1. StopWatch.start()
- 创建并启动计时监控类,记录当前任务名称,默认空字符串,然后记录当前SpringBoot应用启动的开始时间
public class StopWatch {
/**
* Identifier of this {@code StopWatch}.
* <p>Handy when we have output from multiple stop watches and need to
* distinguish between them in log or console output.
*/
private final String id;
private boolean keepTaskList = true;
private final List<TaskInfo> taskList = new LinkedList<>();
/** Start time of the current task. */
private long startTimeNanos;
/** Name of the current task. */
@Nullable
private String currentTaskName;
@Nullable
private TaskInfo lastTaskInfo;
private int taskCount;
/** Total running time. */
private long totalTimeNanos;
/**
* Start an unnamed task.
* <p>The results are undefined if {@link #stop()} or timing methods are
* called without invoking this method first.
* @see #start(String)
* @see #stop()
*/
public void start() throws IllegalStateException {
start("");
}
/**
* Start a named task.
* <p>The results are undefined if {@link #stop()} or timing methods are
* called without invoking this method first.
* @param taskName the name of the task to start
* @see #start()
* @see #stop()
*/
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
/**
* Stop the current task.
* <p>The results are undefined if timing methods are called without invoking
* at least one pair of {@code start()} / {@code stop()} methods.
* @see #start()
* @see #start(String)
*/
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.nanoTime() - this.startTimeNanos;
this.totalTimeNanos += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
2. configureHeadlessProperty()
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;
/*
* `java.awt.headless`模式是在缺少显示屏、键盘、鼠标的系统配置
* 应用程序可以执行如下操作:
* 1. 创建轻量级组件
* 2. 收集关于可用的字体、字体指标、字体设置的信息
* 3. 设置颜色来渲染准备图片
* 4. 创造和获取图像,为渲染准备图片
* 5. 使用`java.awt.PrintJob, java.awt.print.*, javax.print.*`类里的方法进行打印
*/
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, // java.awt.headless
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3. getRunListeners()
spring.factories
中获取EventPublishingRunListener
,封装进SpringApplicationRunListener
实例
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 1... SpringApplicationRunListeners => EventPublishingRunListener
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringBoot监听器有如下两类:
# 事件发布运行监听器,唯一一个应用运行监听器
# 作用:通过一个多播器,将SpringBoot运行状态的变化,构建成事件,广播给各个监听器
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener(),\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
1. SpringApplicationRunListeners
class SpringApplicationRunListeners {
// EventPublishingRunListener
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
// 启动上下文事件监听
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
// environment准备完毕事件监听
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
// appContext准备完毕事件监听
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
// appContext配置类加载事件监听
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
// appContext刷新调用事件
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
// appContext刷新完成,在run方法执行完之前调用该事件
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
// 在运行过程中失败调起的事件
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
try {
listener.failed(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
}
2. EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
// 1... SimpleApplicationEventMulticaster
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
}
3. SimpleAppEventMulticaster
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@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();
// 1... AbstractApplicationEventMulticaster 获取和EventType匹配的listener
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
// 异步发送事件
executor.execute(() -> invokeListener(listener, event));
}
else {
// 2.. 同步发送事件
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
// 2..
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 3...
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
}
1. AbsAppEventMulticaster
LoggingApplicationListener
DelegatingApplicationListener
LiquibaseServiceLocatorApplicationListener
BackgroundPreinitializer
- 3. ApplicationListener
- 将
ApplicationListener
注入到this.defaultRetriever.applicationListeners.add(listener);
public abstract class AbstractApplicationEventMulticaster
implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
private final boolean preFiltered;
}
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
// 1.
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
// 1..
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
// 1. 所有listeners
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// Add programmatically registered listeners, including ones coming
// from ApplicationListenerDetector (singleton beans and inner beans).
for (ApplicationListener<?> listener : listeners) {
// 2.. 筛选符合Event的listeners(11 => 4)
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
// Add listeners by bean name, potentially overlapping with programmatically
// registered listeners above - but here potentially with additional metadata.
// skip
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
}
else {
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
else {
// Remove non-matching listeners that originally came from
// ApplicationListenerDetector, possibly ruled out by additional
// BeanDefinition metadata (e.g. factory method generics) above.
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
retriever.applicationListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
// 1. 将listener封装为 => GenericApplicationListener
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
// 2... GenericApplicationListenerAdapter 判断listener是否支持eventType
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
}
2. GenericAppListenerAdapter
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {
private static final Map<Class<?>, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>();
private final ApplicationListener<ApplicationEvent> delegate;
@Nullable
private final ResolvableType declaredEventType;
/**
* Create a new GenericApplicationListener for the given delegate.
* @param delegate the delegate listener to be invoked
*/
@SuppressWarnings("unchecked")
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
Assert.notNull(delegate, "Delegate listener must not be null");
this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
// 1.. listener中提取EventType
this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
@Nullable
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
// 1.. listener的Class
ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {
Class<?> targetClass = AopUtils.getTargetClass(listener);
if (targetClass != listener.getClass()) {
declaredEventType = resolveDeclaredEventType(targetClass);
}
}
return declaredEventType;
}
@Nullable
static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
ResolvableType eventType = eventTypeCache.get(listenerType);
if (eventType == null) {
// 1. listener为ApplicationListener<>子类,获取<>范型
eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();
eventTypeCache.put(listenerType, eventType);
}
return (eventType != ResolvableType.NONE ? eventType : null);
}
// ------------------------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof SmartApplicationListener) {
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
}
else {
// 1. 是否为Listener支持的eventType
return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
}
}
@Override
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
return !(this.delegate instanceof SmartApplicationListener) ||
((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
}
}
3. ApplicationListener
// 需要获取`<ApplicationEnvironmentPreparedEvent>`泛型类型
public class AnsiOutputApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
}
// 直接支持`supportsEventType()`方法
public class LoggingApplicationListener implements GenericApplicationListener {
private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class,
ApplicationFailedEvent.class };
private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class, ApplicationContext.class };
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return isAssignableFrom(sourceType, SOURCE_TYPES);
}
private boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
if (type != null) {
for (Class<?> supportedType : supportedTypes) {
// 1.
if (supportedType.isAssignableFrom(type)) {
return true;
}
}
}
return false;
}
}
4. prepareEnvironment()
// 详细环境的准备
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 1.. Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2... SpringApplication 配置propertySource、activeProfiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3. 增加configurationProperties
ConfigurationPropertySources.attach(environment);
// 4... SpringApplicationRunListeners 广播 ApplicationEnvironmentPreparedEvent
listeners.environmentPrepared(environment);
// 将环境绑定给当前应用程序(作用目前不清楚)
bindToSpringApplication(environment);
// 对当前的环境类型进行判断,如果不一致进行转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 配置propertySource对它自己的递归依赖
ConfigurationPropertySources.attach(environment);
return environment;
}
// 根据appType获取或创建应用环境。servlet环境、标准环境(特殊的非web环境)、响应式环境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
// 1. 根据webApplicationType创建对应的Environment
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
1. StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
/** 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));
}
// 1... StandardEnvironment
super.customizePropertySources(propertySources);
}
}
1. StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {
/** 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) {
// 1... AbstractEnvironment
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
2. AbstractEnvironment
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
private final MutablePropertySources propertySources = new MutablePropertySources();
// ==>> 配置文件
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment() {
// 1... StandardServletEnvironment
customizePropertySources(this.propertySources);
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Map<String, Object> getSystemProperties() {
try {
return (Map) System.getProperties();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
// 1.
return System.getProperty(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system property '" +
attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
}
return null;
}
}
};
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Map<String, Object> getSystemEnvironment() {
if (suppressGetenvAccess()) {
return Collections.emptyMap();
}
try {
return (Map) System.getenv();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
// 1.
return System.getenv(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system environment variable '" +
attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
}
return null;
}
}
};
}
}
// ------------------------------------------------------------------------------------
@Override
public String[] getActiveProfiles() {
// 1..
return StringUtils.toStringArray(doGetActiveProfiles());
}
/**
* Return the set of active profiles as explicitly set through
* {@link #setActiveProfiles} or if the current set of active profiles
* is empty, check for the presence of the {@value #ACTIVE_PROFILES_PROPERTY_NAME}
* property and assign its value to the set of active profiles.
* @see #getActiveProfiles()
* @see #ACTIVE_PROFILES_PROPERTY_NAME
*/
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
// 1.. spring.profiles.active
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
@Override
@Nullable
public String getProperty(String key) {
// 1. null
return this.propertyResolver.getProperty(key);
}
@Override
public void setActiveProfiles(String... profiles) {
Assert.notNull(profiles, "Profile array must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Activating profiles " + Arrays.asList(profiles));
}
synchronized (this.activeProfiles) {
this.activeProfiles.clear();
for (String profile : profiles) {
validateProfile(profile);
// 1.
this.activeProfiles.add(profile);
}
}
}
@Override
public boolean acceptsProfiles(Profiles profiles) {
Assert.notNull(profiles, "Profiles must not be null");
return profiles.matches(this::isProfileActive);
}
// 当前profile是否是`currentActiveProfiles`
protected boolean isProfileActive(String profile) {
validateProfile(profile);
Set<String> currentActiveProfiles = doGetActiveProfiles();
return (currentActiveProfiles.contains(profile) ||
(currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));
}
@Override
public MutablePropertySources getPropertySources() {
return this.propertySources;
}
}
2. configureEnvironment()
public class SpringApplication {
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 1... ApplicationConversionService
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 2..
configurePropertySources(environment, args);
// 3..
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) {
MutablePropertySources sources = environment.getPropertySources();
// 1. skip
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 2. skip
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));
}
}
}
/**
* 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) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
// 1... AbstractEnvironment
profiles.addAll(Arrays.asList(environment.getActiveProfiles())); // []
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
}
1. ApplicationConversionService
public class ApplicationConversionService extends FormattingConversionService {
private static volatile ApplicationConversionService sharedInstance;
public static ConversionService getSharedInstance() {
ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
synchronized (ApplicationConversionService.class) {
sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
// 1..
sharedInstance = new ApplicationConversionService();
ApplicationConversionService.sharedInstance = sharedInstance;
}
}
}
return sharedInstance;
}
public ApplicationConversionService() {
// 1..
this(null);
}
public ApplicationConversionService(StringValueResolver embeddedValueResolver) {
if (embeddedValueResolver != null) {
setEmbeddedValueResolver(embeddedValueResolver);
}
// 1..
configure(this);
}
public static void configure(FormatterRegistry registry) {
// 1... DefaultConversionService
DefaultConversionService.addDefaultConverters(registry);
// 2... DefaultFormattingConversionService
DefaultFormattingConversionService.addDefaultFormatters(registry);
// 3..
addApplicationFormatters(registry);
// 4..
addApplicationConverters(registry);
}
/**
* Add converters useful for most Spring Boot applications.
* @param registry the registry of converters to add to (must also be castable to
* ConversionService, e.g. being a {@link ConfigurableConversionService})
* @throws ClassCastException if the given ConverterRegistry could not be cast to a
* ConversionService
*/
public static void addApplicationConverters(ConverterRegistry registry) {
addDelimitedStringConverters(registry);
// 1... GenericConversionService
registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
}
/**
* Add formatters useful for most Spring Boot applications.
* @param registry the service to register default formatters with
*/
public static void addApplicationFormatters(FormatterRegistry registry) {
// 1... FormattingConversionService
registry.addFormatter(new CharArrayFormatter());
registry.addFormatter(new InetAddressFormatter());
registry.addFormatter(new IsoOffsetFormatter());
}
}
1. GenericConversionService
public class GenericConversionService implements ConfigurableConversionService {
private final Converters converters = new Converters();
@Override
public void addConverter(GenericConverter converter) {
// 1.
this.converters.add(converter);
invalidateCache();
}
}
2. FormattingCvsionService
public class FormattingConversionService extends GenericConversionService
implements FormatterRegistry, EmbeddedValueResolverAware {
@Nullable
private StringValueResolver embeddedValueResolver;
@Override
public void addFormatter(Formatter<?> formatter) {
// 1..
addFormatterForFieldType(getFieldType(formatter), formatter);
}
@Override
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
// 1... GenericConversionService
addConverter(new PrinterConverter(fieldType, formatter, this));
addConverter(new ParserConverter(fieldType, formatter, this));
}
}
3. DefaultConversionService
public class DefaultConversionService extends GenericConversionService {
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
// 1... GenericConversionService
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
}
4. DftFormattingCvsionService
public class DefaultFormattingConversionService extends FormattingConversionService {
public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
// Default handling of number values
formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// Default handling of monetary values
if (jsr354Present) {
// 1... FormattingConversionService
formatterRegistry.addFormatter(new CurrencyUnitFormatter());
formatterRegistry.addFormatter(new MonetaryAmountFormatter());
formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
}
// Default handling of date-time values
// just handling JSR-310 specific date and time types
new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);
if (jodaTimePresent) {
// handles Joda-specific types as well as Date, Calendar, Long
new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
}
else {
// regular DateFormat-based Date, Calendar, Long converters
new DateFormatterRegistrar().registerFormatters(formatterRegistry);
}
}
}
2. env.getActiveProfiles()
3. attach()
public final class ConfigurationPropertySources {
/**
* The name of the {@link PropertySource} {@link #attach(Environment) adapter}.
*/
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
// 1. configurationProperties
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
// 删除 `configurationProperties`
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
// 2. 添加 `configurationProperties` => SpringConfigurationPropertySources
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
}
4. ConfigFileApplicationListener
- 1. SpringApplicationRunListeners
- 2. EventPublishingRunListener
- 4. ApplicationListener
ApplicationEnvironmentPreparedEvent
(应用程序环境准备事件)- 2. SpringFactoriesLoader
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private static final String DEFAULT_PROPERTIES = "defaultProperties";
// Note the order is from least to most specific (last one wins)
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
private static final Set<String> LOAD_FILTERED_PROPERTY;
static {
Set<String> filteredProperties = new HashSet<>();
filteredProperties.add("spring.profiles.active");
filteredProperties.add("spring.profiles.include");
LOAD_FILTERED_PROPERTY = Collections.unmodifiableSet(filteredProperties);
}
// ApplicationEnvironmentPreparedEvent
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 1..
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 1..
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// 2. 添加自身
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
// 3..
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
// 1... SpringFactoriesLoader
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 1..
addPropertySources(environment, application.getResourceLoader());
}
/**
* Add config file property sources to the specified environment.
* @param environment the environment to add source to
* @param resourceLoader the resource loader
* @see #addPostProcessors(ConfigurableApplicationContext)
*/
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 1... RandomValuePropertySource 处理随机数
RandomValuePropertySource.addToEnvironment(environment);
// 2... Loader
new Loader(environment, resourceLoader).load();
}
private class Loader {
private final ConfigurableEnvironment environment;
private final PropertySourcesPlaceholdersResolver placeholdersResolver;
private final ResourceLoader resourceLoader;
private final List<PropertySourceLoader> propertySourceLoaders;
private Deque<Profile> profiles; // 不包含spring.profiles + includeProfiles
private List<Profile> processedProfiles;
private boolean activatedProfiles;
private Map<Profile, MutablePropertySources> loaded; // 后期 => environment
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 1.
// org.springframework.boot.env.PropertiesPropertySourceLoader
// org.springframework.boot.env.YamlPropertySourceLoader
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
// 配置文件加载工作
void load() {
// 1... FilteredPropertySource defaultProperties, (spring.profiles.active, spring.profiles.include)
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> { // `defaultProperties == null`
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 2.. this.profiles添加null、default,size() == 2
initializeProfiles();
// `spring.profiles.active = dev`时,再次进入
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
// 3.. profile添加到`this.environment.activeProfiles`
if (isDefaultProfile(profile)) {
// 4..
addProfileToEnvironment(profile.getName());
}
// 5.. 第一次 `profile == null`
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
// `spring.profiles=! & |`表达式处理
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
// 6..
addLoadedPropertySources();
// 7..
applyActiveProfiles(defaultProperties);
});
}
/**
* Initialize profile information from both the {@link Environment} active
* profiles and any {@code spring.profiles.active}/{@code spring.profiles.include}
* properties that are already set.
*/
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
// 1.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY); // size = 0
Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY); // size = 0
List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty); // size = 0
this.profiles.addAll(otherActiveProfiles);
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
this.profiles.addAll(includedViaProperty);
addActiveProfiles(activatedViaProperty);
if (this.profiles.size() == 1) { // only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true); // default
this.profiles.add(defaultProfile);
}
}
}
private boolean isDefaultProfile(Profile profile) {
return profile != null && !profile.isDefaultProfile(); // 162
}
private void addProfileToEnvironment(String profile) {
for (String activeProfile : this.environment.getActiveProfiles()) {
if (activeProfile.equals(profile)) {
return;
}
}
this.environment.addActiveProfile(profile);
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
/**
* file:./config/
* file:./
* classpath:/config/
* classpath:/
*/
// 1..
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
// 2..
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; // application
// 3..
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
private Set<String> getSearchLocations() {
// spring.config.location
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
// spring.config.additional-location
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
// 1.. classpath:/, classpath:/config/, file:./, file:./config/
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
private Set<String> getSearchNames() {
// spring.config.name
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) { // skip
String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
return asResolvedSet(property, null);
}
// 1.. DEFAULT_NAMES == application
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
// 字符串,分隔,转化为set
private Set<String> asResolvedSet(String value, String fallback) {
List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
(value != null) ? this.environment.resolvePlaceholders(value) : fallback)));
Collections.reverse(list);
return new LinkedHashSet<>(list);
}
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) { // name为空
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
throw new IllegalStateException("File extension of config file location '" + location
+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
+ "a directory, it must end in '/'");
}
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
// 1... PropertiesPropertySourceLoader => properties, xml
// YamlPropertySourceLoader => yaml, yml
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
// 2..
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
// -----------------------------------------------------------------------------------------------------
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 1.. `getPositiveProfileFilter()`
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
// `profile == dev`即`spring.profiles.active=dev`进入
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension; // file:./config/application-dev.yml
// 3.. getPositiveProfileFilter()_1 => `profile == null`
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
// 4.. getPositiveProfileFilter()_2 => `profile == dev`
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
// 2..
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {
try {
// 1... DefaultResourceLoader加载配置文件 => ClassPathResource
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped missing config ", location, resource,
profile);
this.logger.trace(description);
}
// 2. 配置文件不存在
return;
}
if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped empty config extension ", location,
resource, profile);
this.logger.trace(description);
}
return;
}
String name = "applicationConfig: [" + location + "]";
// 3.. 配置文件转化为Document对象
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
// 4... `getPositiveProfileFilter()`
if (filter.match(document)) {
// 5.. 增加激活的profile
addActiveProfiles(document.getActiveProfiles());
// 6.. 增加包含的profile
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
// 7. `addToLoaded()`
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ", location, resource, profile);
this.logger.debug(description);
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
}
}
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
// 1... PropertiesPropertySourceLoader, YamlPropertySourceLoader
List<PropertySource<?>> loaded = loader.load(name, resource);
// 2.. List<PropertySource<?>> 转化为 List<Document>
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
if (loaded == null) {
return Collections.emptyList();
}
return loaded.stream().map((propertySource) -> {
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
this.placeholdersResolver);
// spring.profiles
// spring.profiles.active
// spring.profiles.include
return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
getProfiles(binder, ACTIVE_PROFILES_PROPERTY), getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
}).collect(Collectors.toList());
}
void addActiveProfiles(Set<Profile> profiles) {
if (profiles.isEmpty()) {
// 1.
return;
}
if (this.activatedProfiles) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
}
return;
}
// 2. 增加激活的profile
this.profiles.addAll(profiles);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
}
this.activatedProfiles = true;
// 3.. 清除default的profile
removeUnprocessedDefaultProfiles();
}
private void removeUnprocessedDefaultProfiles() {
// 1.
this.profiles.removeIf((profile) -> (profile != null && profile.isDefaultProfile()));
}
// 增加包含的profile
private void addIncludedProfiles(Set<Profile> includeProfiles) {
// 1. initializeProfiles() => defaultProfile
LinkedList<Profile> existingProfiles = new LinkedList<>(this.profiles);
this.profiles.clear();
this.profiles.addAll(includeProfiles);
this.profiles.removeAll(this.processedProfiles);
this.profiles.addAll(existingProfiles);
}
}
/**
* Factory used to create a {@link DocumentFilter}.
*/
@FunctionalInterface
private interface DocumentFilterFactory {
/**
* Create a filter for the given profile.
* @param profile the profile or {@code null}
* @return the filter
*/
DocumentFilter getDocumentFilter(Profile profile);
}
/**
* Filter used to restrict when a {@link Document} is loaded.
*/
@FunctionalInterface
private interface DocumentFilter {
boolean match(Document document);
}
/**
* Consumer used to handle a loaded {@link Document}.
*/
@FunctionalInterface
private interface DocumentConsumer {
void accept(Profile profile, Document document);
}
private static class Document {
private final PropertySource<?> propertySource;
private String[] profiles; // spring.profiles
private final Set<Profile> activeProfiles; // spring.profiles.active
private final Set<Profile> includeProfiles; // spring.profiles.include
Document(PropertySource<?> propertySource, String[] profiles, Set<Profile> activeProfiles,
Set<Profile> includeProfiles) {
this.propertySource = propertySource;
this.profiles = profiles;
this.activeProfiles = activeProfiles;
this.includeProfiles = includeProfiles;
}
}
private static class Profile {
private final String name;
private final boolean defaultProfile;
}
}
1. FilteredPropertySource.apply()
class FilteredPropertySource extends PropertySource<PropertySource<?>> {
private final Set<String> filteredProperties;
FilteredPropertySource(PropertySource<?> original, Set<String> filteredProperties) {
super(original.getName(), original);
this.filteredProperties = filteredProperties;
}
@Override
public Object getProperty(String name) {
if (this.filteredProperties.contains(name)) {
return null;
}
// 1.
return getSource().getProperty(name);
}
/**
* @param environment
* @param propertySourceName defaultProperties
* @param filteredProperties `spring.profiles.active`, `spring.profiles.include`
* @param operation
*/
static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,
Consumer<PropertySource<?>> operation) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> original = propertySources.get(propertySourceName);
if (original == null) {
// lambda...
operation.accept(null);
return;
}
propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
try {
operation.accept(original);
}
finally {
propertySources.replace(propertySourceName, original);
}
}
}
2. DefaultResourceLoader
public class DefaultResourceLoader implements ResourceLoader {
@Nullable
private ClassLoader classLoader;
private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : getProtocolResolvers()) { // size = 0
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) { // classpath:
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
}
3. RandomValuePropertySource
public class RandomValuePropertySource extends PropertySource<Random> {
/**
* Name of the random {@link PropertySource}.
*/
public static final String RANDOM_PROPERTY_SOURCE_NAME = "random";
private static final String PREFIX = "random.";
public static void addToEnvironment(ConfigurableEnvironment environment) {
environment.getPropertySources().addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, // systemEnvironment
new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME)); // random
logger.trace("RandomValuePropertySource add to Environment");
}
}
4. PropertySourceLoader
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
1. PropertiesPropSourceLoader
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
private static final String XML_FILE_EXTENSION = ".xml";
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
// 1.
Map<String, ?> properties = loadProperties(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
return Collections
.singletonList(new OriginTrackedMapPropertySource(name, Collections.unmodifiableMap(properties), true));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
// 1.
return new OriginTrackedPropertiesLoader(resource).load();
}
}
2. YamlPropertySourceLoader
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
}
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
for (int i = 0; i < loaded.size(); i++) {
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
Collections.unmodifiableMap(loaded.get(i)), true));
}
return propertySources;
}
}
5. getPositiveProfileFilter()
128
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
/**
* 获取可用的profile。`document.getProfiles()`和`profile`比较
*/
private DocumentFilter getPositiveProfileFilter(Profile profile) {
return (Document document) -> {
// (profile == `spring.profiles.active`)判断
if (profile == null) {
// 1. 无`spring.profiles` 返回true
return ObjectUtils.isEmpty(document.getProfiles());
}
// false
// `document.getProfiles()`包含`profile`
// `document.getProfiles()`正则`environment.activeProfile`
return ObjectUtils.containsElement(document.getProfiles(), profile.getName())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles()));
};
}
private DocumentFilter getNegativeProfileFilter(Profile profile) {
// false
// `spring.profiles`存在
// `document.getProfiles()`正则`environment.activeProfile`
return (Document document) -> (profile == null && !ObjectUtils.isEmpty(document.getProfiles())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles())));
}
// environment.activeProfiles=prod,com
this.environment.acceptsProfiles(Profiles.of("com")) // true
this.environment.acceptsProfiles(Profiles.of("prod")) // true
this.environment.acceptsProfiles(Profiles.of("a")) // false
this.environment.acceptsProfiles(Profiles.of("!a")) // true
this.environment.acceptsProfiles(Profiles.of("prod & com")) // true
this.environment.acceptsProfiles(Profiles.of("prod & a")) // false
this.environment.acceptsProfiles(Profiles.of("prod | com")) // true
this.environment.acceptsProfiles(Profiles.of("prod | a")) // true
1. Profiles
@FunctionalInterface
public interface Profiles {
/**
* Test if this {@code Profiles} instance <em>matches</em> against the given
* active profiles predicate.
* @param activeProfiles predicate that tests whether a given profile is
* currently active
*/
boolean matches(Predicate<String> activeProfiles);
/**
* Create a new {@link Profiles} instance that checks for matches against
* the given <em>profile strings</em>.
* <p>The returned instance will {@linkplain Profiles#matches(Predicate) match}
* if any one of the given profile strings matches.
* <p>A profile string may contain a simple profile name (for example
* {@code "production"}) or a profile expression. A profile expression allows
* for more complicated profile logic to be expressed, for example
* {@code "production & cloud"}.
* <p>The following operators are supported in profile expressions:
* <ul>
* <li>{@code !} - A logical <em>not</em> of the profile</li>
* <li>{@code &} - A logical <em>and</em> of the profiles</li>
* <li>{@code |} - A logical <em>or</em> of the profiles</li>
* </ul>
* <p>Please note that the {@code &} and {@code |} operators may not be mixed
* without using parentheses. For example {@code "a & b | c"} is not a valid
* expression; it must be expressed as {@code "(a & b) | c"} or
* {@code "a & (b | c)"}.
* @param profiles the <em>profile strings</em> to include
* @return a new {@link Profiles} instance
*/
static Profiles of(String... profiles) {
return ProfilesParser.parse(profiles);
}
}
2. ProfilesParser
final class ProfilesParser {
private ProfilesParser() {
}
static Profiles parse(String... expressions) {
Assert.notEmpty(expressions, "Must specify at least one profile");
Profiles[] parsed = new Profiles[expressions.length];
for (int i = 0; i < expressions.length; i++) {
parsed[i] = parseExpression(expressions[i]);
}
return new ParsedProfiles(expressions, parsed);
}
private static Profiles parseExpression(String expression) {
Assert.hasText(expression, () -> "Invalid profile expression [" + expression + "]: must contain text");
// `()&|!` 处理
StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);
return parseTokens(expression, tokens);
}
private static Profiles parseTokens(String expression, StringTokenizer tokens) {
return parseTokens(expression, tokens, Context.NONE);
}
private static Profiles parseTokens(String expression, StringTokenizer tokens, Context context) {
List<Profiles> elements = new ArrayList<>();
Operator operator = null;
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (token.isEmpty()) {
continue;
}
switch (token) {
case "(":
Profiles contents = parseTokens(expression, tokens, Context.BRACKET);
if (context == Context.INVERT) {
return contents;
}
elements.add(contents);
break;
case "&":
assertWellFormed(expression, operator == null || operator == Operator.AND);
operator = Operator.AND;
break;
case "|":
assertWellFormed(expression, operator == null || operator == Operator.OR);
operator = Operator.OR;
break;
case "!":
elements.add(not(parseTokens(expression, tokens, Context.INVERT)));
break;
case ")":
Profiles merged = merge(expression, elements, operator);
if (context == Context.BRACKET) {
return merged;
}
elements.clear();
elements.add(merged);
operator = null;
break;
default:
Profiles value = equals(token);
if (context == Context.INVERT) {
return value;
}
elements.add(value);
}
}
return merge(expression, elements, operator);
}
private static Profiles merge(String expression, List<Profiles> elements, @Nullable Operator operator) {
assertWellFormed(expression, !elements.isEmpty());
if (elements.size() == 1) {
return elements.get(0);
}
Profiles[] profiles = elements.toArray(new Profiles[0]);
return (operator == Operator.AND ? and(profiles) : or(profiles));
}
private static Profiles and(Profiles... profiles) {
return activeProfile -> Arrays.stream(profiles).allMatch(isMatch(activeProfile));
}
private static Profiles or(Profiles... profiles) {
return activeProfile -> Arrays.stream(profiles).anyMatch(isMatch(activeProfile));
}
private static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {
return profiles -> profiles.matches(activeProfile);
}
}
6. addToLoaded()
128
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
private Map<Profile, MutablePropertySources> loaded;
/**
* 构建`MutablePropertySources`添加到loaded
*/
private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
boolean checkForExisting) {
return (profile, document) -> {
if (checkForExisting) {
for (MutablePropertySources merged : this.loaded.values()) {
if (merged.contains(document.getPropertySource().getName())) {
return;
}
}
}
// 1.
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
(k) -> new MutablePropertySources());
// 2. `MutablePropertySources.addLast()`
addMethod.accept(merged, document.getPropertySource());
};
}
}
1. MutablePropertySources
- 当抽象方法中有两个参数,且第一个参数是调用者。即可
类::实例方法
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
/**
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
// 1.
this.propertySourceList.add(propertySource);
}
public void addFirst(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
protected void removeIfPresent(PropertySource<?> propertySource) {
this.propertySourceList.remove(propertySource);
}
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}
private void addAtIndex(int index, PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(index, propertySource);
}
}
7. addLoadedPropertySources()
Loader.loaded
添加到environment.propertySources
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private class Loader {
private final ConfigurableEnvironment environment;
private Map<Profile, MutablePropertySources> loaded;
// 将配置文件 => environment
private void addLoadedPropertySources() {
// 1. 向environment中添加配置文件
MutablePropertySources destination = this.environment.getPropertySources();
// 2.
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
for (MutablePropertySources sources : loaded) {
for (PropertySource<?> source : sources) {
if (added.add(source.getName())) {
// 3.
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
private void addLoadedPropertySource(MutablePropertySources destination, String lastAdded, PropertySource<?> source) {
if (lastAdded == null) {
if (destination.contains(DEFAULT_PROPERTIES)) { // defaultProperties
destination.addBefore(DEFAULT_PROPERTIES, source);
}
else {
// 1. 对应系统environment
destination.addLast(source);
}
}
else {
destination.addAfter(lastAdded, source);
}
}
}
}
8. applyActiveProfiles()
private void applyActiveProfiles(PropertySource<?> defaultProperties) {
List<String> activeProfiles = new ArrayList<>();
if (defaultProperties != null) {
Binder binder = new Binder(ConfigurationPropertySources.from(defaultProperties),
new PropertySourcesPlaceholdersResolver(this.environment));
activeProfiles.addAll(getDefaultProfiles(binder, "spring.profiles.include"));
if (!this.activatedProfiles) {
activeProfiles.addAll(getDefaultProfiles(binder, "spring.profiles.active"));
}
}
this.processedProfiles.stream().filter(this::isDefaultProfile).map(Profile::getName)
.forEach(activeProfiles::add);
// environment.activeProfiles设置 ["dev"]
this.environment.setActiveProfiles(activeProfiles.toArray(new String[0]));
}
5. configureIgnoreBeanInfo()
public class SpringApplication {
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
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());
}
}
}
6. printBanner()
public class SpringApplication {
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
// 1.
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 2... SpringApplicationBannerPrinter
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
1. SpringAppBannerPrinter
class SpringApplicationBannerPrinter {
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
this.resourceLoader = resourceLoader;
this.fallbackBanner = fallbackBanner;
}
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
// 1..
Banner banner = getBanner(environment);
// 2... SpringBootBanner
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
// 1..
banners.addIfNotNull(getImageBanner(environment));
// 2..
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
// 3. 自定义banner
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
// 4. SpringBootBanner 默认banner => SpringBootBanner
return DEFAULT_BANNER;
}
private Banner getImageBanner(Environment environment) {
// pring.banner.image.location
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
if (StringUtils.hasLength(location)) {
Resource resource = this.resourceLoader.getResource(location);
return resource.exists() ? new ImageBanner(resource) : null;
}
for (String ext : IMAGE_EXTENSION) {
// 1. [banner.gif, banner.jpg, banner.png]
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
return null;
}
private Banner getTextBanner(Environment environment) {
// 1. spring.banner.location, banner.txt
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
Resource resource = this.resourceLoader.getResource(location);
if (resource.exists()) {
// 2.
return new ResourceBanner(resource);
}
return null;
}
private static class Banners implements Banner {
private final List<Banner> banners = new ArrayList<>();
void addIfNotNull(Banner banner) {
if (banner != null) {
this.banners.add(banner);
}
}
boolean hasAtLeastOneBanner() {
return !this.banners.isEmpty();
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
for (Banner banner : this.banners) {
// 6.
banner.printBanner(environment, sourceClass, out);
}
}
}
}
2. SpringBootBanner
class SpringBootBanner implements Banner {
private static final String[] BANNER = { "", " . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
// 1.
printStream.println(line);
}
// 2.
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
3. ResourceBanner
public class ResourceBanner implements Banner {
private static final Log logger = LogFactory.getLog(ResourceBanner.class);
private Resource resource;
public ResourceBanner(Resource resource) {
Assert.notNull(resource, "Resource must not be null");
Assert.isTrue(resource.exists(), "Resource must exist");
this.resource = resource;
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
try {
String banner = StreamUtils.copyToString(this.resource.getInputStream(),
environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));
for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
banner = resolver.resolvePlaceholders(banner);
}
// 1.
out.println(banner);
}
catch (Exception ex) {
logger.warn(LogMessage.format("Banner not printable: %s (%s: '%s')", this.resource, ex.getClass(),
ex.getMessage()), ex);
}
}
}
4. ImageBanner
public class ImageBanner implements Banner {
private static final String PROPERTY_PREFIX = "spring.banner.image.";
private static final Log logger = LogFactory.getLog(ImageBanner.class);
private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };
private final Resource image;
public ImageBanner(Resource image) {
Assert.notNull(image, "Image must not be null");
Assert.isTrue(image.exists(), "Image must exist");
this.image = image;
}
private void printBanner(Environment environment, PrintStream out) throws IOException {
int width = getProperty(environment, "width", Integer.class, 76);
int height = getProperty(environment, "height", Integer.class, 0);
int margin = getProperty(environment, "margin", Integer.class, 2);
boolean invert = getProperty(environment, "invert", Boolean.class, false);
BitDepth bitDepth = getBitDepthProperty(environment);
PixelMode pixelMode = getPixelModeProperty(environment);
Frame[] frames = readFrames(width, height);
for (int i = 0; i < frames.length; i++) {
if (i > 0) {
resetCursor(frames[i - 1].getImage(), out);
}
printBanner(frames[i].getImage(), margin, invert, bitDepth, pixelMode, out);
sleep(frames[i].getDelayTime());
}
}
private <T> T getProperty(Environment environment, String name, Class<T> targetType, T defaultValue) {
return environment.getProperty(PROPERTY_PREFIX + name, targetType, defaultValue);
}
private Frame[] readFrames(int width, int height) throws IOException {
try (InputStream inputStream = this.image.getInputStream()) {
try (ImageInputStream imageStream = ImageIO.createImageInputStream(inputStream)) {
return readFrames(width, height, imageStream);
}
}
}
private Frame[] readFrames(int width, int height, ImageInputStream stream) throws IOException {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
Assert.state(readers.hasNext(), "Unable to read image banner source");
ImageReader reader = readers.next();
try {
ImageReadParam readParam = reader.getDefaultReadParam();
reader.setInput(stream);
int frameCount = reader.getNumImages(true);
Frame[] frames = new Frame[frameCount];
for (int i = 0; i < frameCount; i++) {
frames[i] = readFrame(width, height, reader, i, readParam);
}
return frames;
}
finally {
reader.dispose();
}
}
private Frame readFrame(int width, int height, ImageReader reader, int imageIndex, ImageReadParam readParam)
throws IOException {
BufferedImage image = reader.read(imageIndex, readParam);
BufferedImage resized = resizeImage(image, width, height);
int delayTime = getDelayTime(reader, imageIndex);
return new Frame(resized, delayTime);
}
private void printBanner(BufferedImage image, int margin, boolean invert, BitDepth bitDepth, PixelMode pixelMode,
PrintStream out) {
AnsiElement background = invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT;
out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
out.print(AnsiOutput.encode(background));
out.println();
out.println();
AnsiElement lastColor = AnsiColor.DEFAULT;
AnsiColors colors = new AnsiColors(bitDepth);
for (int y = 0; y < image.getHeight(); y++) {
for (int i = 0; i < margin; i++) {
out.print(" ");
}
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y), false);
AnsiElement ansiColor = colors.findClosest(color);
if (ansiColor != lastColor) {
out.print(AnsiOutput.encode(ansiColor));
lastColor = ansiColor;
}
out.print(getAsciiPixel(color, invert, pixelMode));
}
out.println();
}
out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
out.println();
}
}
7. createApplicationContext()
- 根据不同的应用类型初始化不同的上下文应用类
public class SpringApplication {
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
/**
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context or application context
* class before falling back to a suitable default.
* @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:
// 1. `AnnotationConfigServletWebServerApplicationContext`
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);
}
}
// 2.
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
}
1. AnnoCfgSvltWebServerAC
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
private String[] basePackages;
/**
* Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
* to be populated through {@link #register} calls and then manually
* {@linkplain #refresh refreshed}.
*/
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
/**
* {@inheritDoc}
* <p>
* Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and
* {@link ClassPathBeanDefinitionScanner} members.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
}
2. AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/** Environment used by this context. */
@Nullable
private ConfigurableEnvironment environment;
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
}
3. GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
/**
* Create a new GenericApplicationContext.
* @see #registerBeanDefinition
* @see #refresh
*/
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
}
8. SpringBootExceptionReporter
- 准备异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
# Error Reporters 错误报告器
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
9. prepareContext()
AnnotationConfigServletWebServerApplicationContext
- 准备应用上下文
public class SpringApplication {
private Set<Class<?>> primarySources;
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 1.
context.setEnvironment(environment);
// 2..
postProcessApplicationContext(context);
// 3..
applyInitializers(context);
// 4...
listeners.contextPrepared(context);
// 5..
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 6..
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 7.. ** `com.listao.spring_boot.Application`注册到IOC **
load(context, sources.toArray(new Object[0]));
// 8... ConfigFileApplicationListener
listeners.contextLoaded(context);
}
/**
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
* apply additional processing as required.
* @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) {
// 1..
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
// 1. 7个Initializers
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 2... SharedMetadataReaderFactoryContextInitializer
initializer.initialize(context);
}
}
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
// 1. `class com.listao.spring_boot.Application`
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
}
1. applyInitializers()
class SharedMetadataReaderFactoryContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
public static final String BEAN_NAME = "org.springframework.boot.autoconfigure."
+ "internalCachingMetadataReaderFactory";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 1.
applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
}
/**
* {@link BeanDefinitionRegistryPostProcessor} to register the
* {@link CachingMetadataReaderFactory} and configure the
* {@link ConfigurationClassPostProcessor}.
*/
private static class CachingMetadataReaderFactoryPostProcessor
implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
}
}
public class ConfigurationWarningsApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
// 1.
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
/**
* {@link BeanDefinitionRegistryPostProcessor} to report warnings.
*/
protected static final class ConfigurationWarningsPostProcessor
implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
}
}
2. listeners.contextPrepared()
本质上什么也没做 => ApplicationContextInitializedEvent
3. logStartupInfo
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
public class SpringApplication {
private Class<?> mainApplicationClass; // com.listao.spring_boot.Application
// 1... LogFactory => Slf4jLocationAwareLog
private static final Log logger = LogFactory.getLog(SpringApplication.class);
protected void logStartupInfo(boolean isRoot) {
if (isRoot) {
// 1... StartupInfoLogger
new StartupInfoLogger(this.mainApplicationClass).logStarting(getApplicationLog());
}
}
/**
* Called to log active profile information.
* @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));
}
}
}
/**
* Returns the {@link Log} for the application. By default will be deduced.
* @return the application log
*/
protected Log getApplicationLog() {
if (this.mainApplicationClass == null) {
return logger;
}
return LogFactory.getLog(this.mainApplicationClass);
}
}
1. StartupInfoLogger
class StartupInfoLogger {
private final Class<?> sourceClass;
StartupInfoLogger(Class<?> sourceClass) {
this.sourceClass = sourceClass;
}
void logStarting(Log applicationLog) {
Assert.notNull(applicationLog, "Log must not be null");
// 1..
applicationLog.info(LogMessage.of(this::getStartingMessage));
// 2..
applicationLog.debug(LogMessage.of(this::getRunningMessage));
}
void logStarted(Log applicationLog, StopWatch stopWatch) {
if (applicationLog.isInfoEnabled()) {
// 1..
applicationLog.info(getStartedMessage(stopWatch));
}
}
private CharSequence getStartingMessage() {
StringBuilder message = new StringBuilder();
// 1.
message.append("Starting ");
appendApplicationName(message);
appendVersion(message, this.sourceClass);
appendOn(message);
appendPid(message);
appendContext(message);
return message;
}
private CharSequence getRunningMessage() {
StringBuilder message = new StringBuilder();
// 1.
message.append("Running with Spring Boot");
appendVersion(message, getClass());
message.append(", Spring");
appendVersion(message, ApplicationContext.class);
return message;
}
// 1. Started Application in 12269.483 seconds (JVM running for 12269.742)
private CharSequence getStartedMessage(StopWatch stopWatch) {
StringBuilder message = new StringBuilder();
message.append("Started ");
appendApplicationName(message);
message.append(" in ");
message.append(stopWatch.getTotalTimeMillis() / 1000.0);
message.append(" seconds");
try {
double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
message.append(" (JVM running for ").append(uptime).append(")");
}
catch (Throwable ex) {
// No JVM time available
}
return message;
}
private void appendApplicationName(StringBuilder message) {
String name = (this.sourceClass != null) ? ClassUtils.getShortName(this.sourceClass) : "application";
message.append(name);
}
}
2. LogFactory
public abstract class LogFactory {
/**
* Convenience method to return a named logger.
* @param clazz containing Class from which a log name will be derived
*/
public static Log getLog(Class<?> clazz) {
// 1..
return getLog(clazz.getName());
}
/**
* Convenience method to return a named logger.
* @param name logical name of the <code>Log</code> instance to be returned
*/
public static Log getLog(String name) {
// 1... LogAdapter
return LogAdapter.createLog(name);
}
}
3. LogAdapter
final class LogAdapter {
private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogApi logApi;
private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}
static {
if (isPresent(LOG4J_SPI)) {
if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
// however, we still prefer Log4j over the plain SLF4J API since
// the latter does not have location awareness support.
// 1.
logApi = LogApi.SLF4J_LAL;
}
else {
// Use Log4j 2.x directly, including location awareness support
logApi = LogApi.LOG4J;
}
}
else if (isPresent(SLF4J_SPI)) {
// Full SLF4J SPI including location awareness support
logApi = LogApi.SLF4J_LAL;
}
else if (isPresent(SLF4J_API)) {
// Minimal SLF4J API without location awareness support
logApi = LogApi.SLF4J;
}
else {
// java.util.logging as default
logApi = LogApi.JUL;
}
}
private static boolean isPresent(String className) {
try {
// 1.
Class.forName(className, false, LogAdapter.class.getClassLoader());
return true;
}
catch (ClassNotFoundException ex) {
return false;
}
}
/**
* Create an actual {@link Log} instance for the selected API.
* @param name the logger name
*/
public static Log createLog(String name) {
switch (logApi) {
case LOG4J:
return Log4jAdapter.createLog(name);
case SLF4J_LAL:
// 2.
return Slf4jAdapter.createLocationAwareLog(name);
case SLF4J:
return Slf4jAdapter.createLog(name);
default:
// Defensively use lazy-initializing adapter class here as well since the
// java.logging module is not present by default on JDK 9. We are requiring
// its presence if neither Log4j nor SLF4J is available; however, in the
// case of Log4j or SLF4J, we are trying to prevent early initialization
// of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
// trying to parse the bytecode for all the cases of this switch clause.
return JavaUtilAdapter.createLog(name);
}
}
@SuppressWarnings("serial")
private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable {
}
}
4. load()
main()
方法所在类的对象,进行 IOC 注入
public class SpringApplication {
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
/**
* Load beans into the application context.
* @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));
}
// 1..
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) { // skip
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) { // skip
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) { // skip
loader.setEnvironment(this.environment);
}
// 2... BeanDefinitionLoader
loader.load();
}
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
// 1.
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
// 1.
return new BeanDefinitionLoader(registry, sources);
}
}
1. BeanDefinitionLoader
class BeanDefinitionLoader {
// com.listao.spring_boot.Application
private final Object[] sources;
private final AnnotatedBeanDefinitionReader annotatedReader;
private final XmlBeanDefinitionReader xmlReader;
private BeanDefinitionReader groovyReader;
private final ClassPathBeanDefinitionScanner scanner;
private ResourceLoader resourceLoader;
/**
* Create a new {@link BeanDefinitionLoader} that will load beans into the specified
* {@link BeanDefinitionRegistry}.
* @param registry the bean definition registry that will contain the loaded beans
* @param sources the bean sources
*/
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
// 1.
this.sources = sources;
// 2.
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
// 3.
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
// 4.
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
/**
* Load the sources into the reader.
* @return the number of loaded beans
*/
int load() {
int count = 0;
for (Object source : this.sources) {
// 1..
count += load(source);
}
return count;
}
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
// 1..
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
// SpringBoot优先选择groovy加载方式,其次java方式
private int load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // skip
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
load(loader);
}
// 1..
if (isComponent(source)) {
// 2. `com.listao.spring_boot.Application`注册到IOC
this.annotatedReader.register(source);
return 1;
}
return 0;
}
private boolean isComponent(Class<?> type) {
// This has to be a bit of a guess. The only way to be sure that this type is
// eligible is to make a bean definition out of it and try to instantiate it.
// 1.
if (MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class)) {
return true;
}
// Nested anonymous classes are not eligible for registration, nor are groovy
// closures
return !type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass()
&& type.getConstructors() != null && type.getConstructors().length != 0;
}
}
2. AnnotatedBDReader
public class AnnotatedBeanDefinitionReader {
/**
* Register one or more component classes to be processed.
* <p>Calls to {@code register} are idempotent; adding the same
* component class more than once has no additional effect.
* @param componentClasses one or more component classes,
* e.g. {@link Configuration @Configuration} classes
*/
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
// 1..
registerBean(componentClass);
}
}
/**
* Register a bean from the given bean class, deriving its metadata from
* class-declared annotations.
* @param beanClass the class of the bean
*/
public void registerBean(Class<?> beanClass) {
// 1..
doRegisterBean(beanClass, null, null, null, null);
}
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
// 1.
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
// 2.
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 3.
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
}
5. listeners.contextLoaded()
- 1. SpringApplicationRunListeners
- 2. EventPublishingRunListener
ApplicationPreparedEvent
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
/**
* Add appropriate post-processors to post-configure the property-sources.
* @param context the context to configure
*/
protected void addPostProcessors(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(context));
}
}
10. refreshContext()
public class SpringApplication {
private void refreshContext(ConfigurableApplicationContext context) {
// 1..
refresh(context);
if (this.registerShutdownHook) {
try {
// 2... AbstractApplicationContext
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 1... ServletWebServerApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
}
}
1. ServletWebServerAC
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
// 1... AbstractApplicationContext
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 1.. Tomcat
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
}
2. AbsApplicationContext
Spring.refresh()
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";
/** Synchronization monitor for the "refresh" and "destroy". */
private final Object startupShutdownMonitor = new Object();
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 刷新、初始化上下文环境,对系统的环境变量、系统属性进行准备和校验
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 初始化BF,解析xml,相当于之前的xmlBeanfactory操作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备BF,对BF的各种功能进行填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 1... ServletWebServerApplicationContext
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化非延迟加载的bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新过程,通知声明周期处理器刷新过程,同时发出ContextRefreshEvent
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
/**
* Register a shutdown hook {@linkplain Thread#getName() named}
* {@code SpringContextShutdownHook} with the JVM runtime, closing this
* context on JVM shutdown unless it has already been closed at that time.
* <p>Delegates to {@code doClose()} for the actual closing procedure.
* @see Runtime#addShutdownHook
* @see ConfigurableApplicationContext#SHUTDOWN_HOOK_THREAD_NAME
* @see #close()
* @see #doClose()
*/
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// 1. No shutdown hook registered yet.
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) { // SpringContextShutdownHook
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
}
11. afterRefresh()
- 拓展使用
public class SpringApplication {
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
}
12. StopWatch.stop()
- 停止计时监控类:计时监听器停止,并统计一些任务执行信息
- 输出日志记录执行主类名、时间信息
- 1. StopWatch.start()
13. logStarted()
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
14. listeners.started()
- 1. SpringApplicationRunListeners
- 2. EventPublishingRunListener
ApplicationStartedEvent
15. callRunners()
- 执行所有
ApplicationRunner
和CommandLineRunner
两种运行器
public class SpringApplication {
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)) { // (size == 0)
if (runner instanceof ApplicationRunner) {
// 1..
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
// 2..
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
// 1.
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
// 1.
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
}
16. listeners.running()
- 1. SpringApplicationRunListeners
- 2. EventPublishingRunListener
ApplicationReadyEvent
3. spring.factories
spring-boot-2.2.2.RELEASE.jar
spring-boot-autoconfigure-2.2.2.RELEASE.jar
1. spring-boot
# PropertySource Loaders(properties文件加载器、yaml文件加载器)
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners(运行时的监听器。程序运行过程中所有监听通知都是通过此类进行回调)
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters(错误报告器)
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
# 1. 报告Spring容器一些常见的错误配置
# 2. 设置SpringApplicationContext的ID
# 3 4. 使用环境属性context.initializer.classes指定初始化器进行初始化规则
# 5. 将内置servlet容器实际使用的监听端口写入到environment属性中
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
# 1. AppContext加载完成后对缓存做清除工作,响应事件ContextRefreshEvent
# 2 3. 监听双亲AppContext的关闭事件并往子AppContext中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent
# 4. 如果系统文件编码与环境变量中指定的不同则终止应用启动
# 比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感)
# 5. `spring.output.ansi.enabled`参数配置AnsiOutput
# 6. 从约定的位置读取配置文件。`application.properties, application.yml`等配置文件
# 7. 监听到事件后转发给环境变量`context.listener.classes`指定的事件监听器
# 8. 对环境就绪事件ApplicationEnvironmentPreparedEvent/应用失败事件ApplicationFailedEvent做出响应
# 往日志DEBUG级别输出TCCL(thread context class loader)的classpath
# 9. 检测正在使用的日志系统,默认时logback,此时日志系统还没有初始化
# 10. 使用一个可以和SpringBoot可执行jar包配合工作的版本替换liquibase ServiceLocator
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
2. spring-boot-autoconfigure
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
# 单独启动一个线程实例化并调用run方法,包括验证器、消息转换器等
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider