入口
创建好上下文对象和异常报告对象后,开始准备上下文对象。 看下入口:
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
再看下准备方法(这里想吐槽下,这么大串代码写个注释嘛,尽管是private的):
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
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
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
setEnvironment
第一行setEnvironment看下接口和实现类的注释好了:
//org.springframework.context.ConfigurableApplicationContext#setEnvironment
/**
* 为此应用程序上下文设置Environment 。
* @param environment the new environment
* @since 3.1
*/
void setEnvironment(ConfigurableEnvironment environment);
//org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#setEnvironment
/**
* 为此应用程序上下文设置Environment 。
* 将给定环境委托给基础的AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner成员。
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
//org.springframework.context.support.AbstractApplicationContext#setEnvironment
/**
*为此应用程序上下文设置Environment 。
* 默认值由createEnvironment()确定。 用此方法替换默认值是一个选项,但也应考虑通过getEnvironment()配置。 无论哪种情况,都应在refresh()之前执行此类修改。
* @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
后期处理应用程序上下文
/**
* 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) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
这里beanNameGenerator 是null,resourceLoader也是null,只有addConversionService是true,那就debug先看下吧:
//org.springframework.beans.factory.support.AbstractBeanFactory#setConversionService
@Override
public void setConversionService(@Nullable ConversionService conversionService) {
this.conversionService = conversionService;
}
貌似也没什么看的。
执行启动器
/**
* 在刷新之前,将任何ApplicationContextInitializer应用于上下文。
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
//org.springframework.boot.SpringApplication#getInitializers
/**
* Returns read-only ordered Set of the {@link ApplicationContextInitializer}s that
* will be applied to the Spring {@link ApplicationContext}.
* @return the initializers
*/
public Set<ApplicationContextInitializer<?>> getInitializers() {
return asUnmodifiableOrderedSet(this.initializers);
}
这个时候发现,我们SpringApplication在初始化的时候设置的Initializers在这个时候将执行初始化了
我们看看这7个执行器的执行初始化都干了什么吧:跟我一起阅读SpringBoot源码(九)——初始化执行器
这里留个想法,我们是不是可以在项目里面配置源里面配置加个源文件(META-INF\spring.factories)配置这些初始化器什么的就可以让spring在启动的时候给实例化了呢?
发布上下文已初始化事件
在完成上面的初始化器的初始化工作后,spring将这个事件多播到所有注册好的监听器上去。下面看看事件发布过程。
这里想起这就是
监听者模式的一个实现示例。
看下发布入口,通过准备上下文传进来的SpringApplicationRunListeners执行contextPrepared进行事件发布。
listeners.contextPrepared(context);
如果没记错,前面好像是获取了一个侦听器放在listeners里面,在debug看下是哪个侦听器:
刚好是事件发布的侦听器。
大意应该就是说SpringApplicationRunListener用于发布SpringApplicationEvent事件,在上下文实际刷新前,用内部的ApplicationEventMulticaster去取触发这些事件。
接下来看下发布过程:
//org.springframework.boot.SpringApplicationRunListeners#contextPrepared
//注册的所有监听器都将该事件进行发布
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
用多播的方式将ApplicationContextInitializedEvent事件进行发布出去。看下发布出去后监听者是怎么进行处理的:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
//先解析事件类型,如果多播的时候没有发送事件类型则采用该事件的默认类型。
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//获取执行器,但是此时此处是null
Executor executor = getTaskExecutor();
//获取监听该事件的监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//如果获取到执行器,则用执行器来执行侦听器的方法,这里是什么方法就不贴源码了,直接贴结果:onApplicationEvent
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
//没有执行器就直接执行,对于这执行器下面再讲。
else {
invokeListener(listener, event);
}
}
}
/**
* 返回与给定事件类型匹配的ApplicationListeners的集合。 不匹配的听众会尽早被排除在外。
* @param event the event to be propagated. Allows for excluding
* non-matching listeners early, based on cached matching information.
* @param eventType the event type
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
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);
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);
}
}
取得过程不多讲了,反正就是从先前注册的侦听器里面取,这里缓存里面有4个侦听器:
发布事件后这4个侦听器做了什么就不在这里描述了,单独为侦听器写了篇文章,去这里面看吧。
跟我一起阅读SpringBoot源码(十)——侦听器
logStartupInfo
上下文初始化完成后,启动日志信息
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
日志信息是否启动可以通过设置logStartupInfo标致来控制
/**
* Called to log startup information, subclasses may override to add additional
* logging.
* @param isRoot true if this application is the root of a context hierarchy
*/
protected void logStartupInfo(boolean isRoot) {
if (isRoot) {
new StartupInfoLogger(this.mainApplicationClass).logStarting(getApplicationLog());
}
}
void logStarting(Log applicationLog) {
Assert.notNull(applicationLog, "Log must not be null");
applicationLog.info(LogMessage.of(this::getStartingMessage));
applicationLog.debug(LogMessage.of(this::getRunningMessage));
}
如果当前上下文是根,那么实例化一个StartupInfoLogger来记录启动类的信息,并启动日志系统。
/**
* 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));
}
}
}
接着打印启动使用的配置文件信息,激活的配置文件获取逻辑是:
- 根据
spring.profiles.active配置来获取激活的配置文件,多个激活文件用逗号分隔。
/**
* 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()) {
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
- 如果没有配置激活文件activeProfiles,则获取默认配置文件:
/**
* Return the set of default profiles explicitly set via
* {@link #setDefaultProfiles(String...)} or if the current set of default profiles
* consists only of {@linkplain #getReservedDefaultProfiles() reserved default
* profiles}, then check for the presence of the
* {@value #DEFAULT_PROFILES_PROPERTY_NAME} property and assign its value (if any)
* to the set of default profiles.
* @see #AbstractEnvironment()
* @see #getDefaultProfiles()
* @see #DEFAULT_PROFILES_PROPERTY_NAME
* @see #getReservedDefaultProfiles()
*/
protected Set<String> doGetDefaultProfiles() {
synchronized (this.defaultProfiles) {
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
}
}
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
/**
* Return the set of reserved default profile names. This implementation returns
* {@value #RESERVED_DEFAULT_PROFILE_NAME}. Subclasses may override in order to
* customize the set of reserved names.
* @see #RESERVED_DEFAULT_PROFILE_NAME
* @see #doGetDefaultProfiles()
*/
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
/**
* Name of property to set to specify profiles active by default: {@value}. Value may
* be comma delimited.
* <p>Note that certain shell environments such as Bash disallow the use of the period
* character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource}
* is in use, this property may be specified as an environment variable as
* {@code SPRING_PROFILES_DEFAULT}.
* @see ConfigurableEnvironment#setDefaultProfiles
*/
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
看上面的代码应该不难,看完总结一点就是,Spring默认的激活配置文件是default,然后这个默认配置可以通过spring.profiles.default进行修改。