为什么要用Spring boot
用官方的说法:Spring boot提供了一种更简单的方式去创建并运行 “独立的” ,“工业级”的基于Spring平台应用/服务。
更少的配置
- 可以不用生成任何代码
- 可以不需要配置任何xml文件
- 自动化配置Spring和第三方依赖
内置容器
不需要再去部署war文件,spring boot内置了这些容器
- tomcat
- jetty
- undertow
工业级的特性
- metrics
- health checks
- externalized configuration
Spring boot启动过程源码
如何搭建spring boot的源码阅读环境参考我的另一篇文章
我们以spring boot 源码工程(分支 : 2.1.x)中的spring-boot-samples/spring-boot-sample-simple为例讲解
这个例子其实涉及spring两个重要的知识点:
- spring的启动过程
- spring 依赖注入
启动入口在SpringApplication.run(Class<?> primarySource, String… args)这个静态方法中,返回一个配置上下文ConfigurableApplicationContext。
下面逐步解读一个这个run方法
1. 创建一个SpringApplication 实例
(1)我们先来看this.webApplicationType = WebApplicationType.deduceFromClasspath();
这里主要确定这个web应用的类型,一共有三种
deduceFromClasspath()方法中主要用方法ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader)进行判断
forName()方法调用成功则返回true,抛出异常怎返回false,再看一下forName()方法,也太长了,看重点部分,通过代码的debug会看到其实就是走到下面这一段代码
这段代码其实就做了以下几件事情:
1.找到类加载器,没有就用系统默认的类加载器
2.用这个类加载器加载这个名字的类
3.加载不到则加载这个类的innerClass,单步调试一下会发现:
org.springframework.web.reactive.DispatcherHandler
对应的innerClass为 org.springframework.web.reactive$DispatcherHandler
4.如果还是加载不到这个innerClass则抛异常
可以看到在这个例子中最后返回WebApplicationType.NONE,也就是不会启动任何容器,如果去单步调试spring-boot-sample-servlet 这个例子,会发现返回WebApplicationType.SERVLET,进而启动一个servlet类型的web应用程序。
那么其实到这里我们可以看到一个关键问题,在Class.forName(name, false, clToUse)中jvm到底是如何判断有没有这个名字的类呢?
这里就要去看下jdk中Class.forName这个方法,这个方法用默认的bootstract classloader去加载类并生成该类对应的Class 对象,那值得注意的就是类加载器加载类时的一个规则:
可以看到spring-boot-sample-servlet这个例子其实就是依赖了Servlet这个类的
(2) 确定了启动什么类型的应用程序后,接着就是设置初始化器和监听器
这里其实都用到一个方法getSpringFactoriesInstances,看样子这个方法的作用就是得到spring工厂实例,可以看到这两行代码用了同一个方法getSpringFactoriesInstances,下面重点解析这个函数,这个函数跟进去:
- 获取工厂类的名字
- 然后根据工厂类的名字去初始化工厂实例:
根据class对象的名字用反射的方式获取构造器,然后初始化实例 - 对工厂实例进行排序
AnnotationAwareOrderComparator可以对打了@Order注解和@Priority注解的类的对象进行排序,数字越小,优先级越高,目的应该是为了控制spring的注册顺序
(3) 最后通过deduceMainApplicationClass获取main方法的类,设置到SpringApplication的mainApplicationClass属性中
2. 执行SpringApplication的run()方法
这个方法先看个大概,可以看到主要做了以下几事情
1.开启定时器StopWatch计时
2.新建并启动运行时监听器:SpringApplicationRunListeners
3.新建应用参数:ApplicationArguments
4.初始化环境:ConfigurableEnvironment
5.构造运行时上下文ConfigurableApplicationContext并对这个上下文进行初始化,刷新上下文
6.调用ApplicationRunner的run方法(run the bean)
2.1运行时监听器的初始化过程
SpringApplicationRunListeners listeners = getRunListeners(args);
这个方法跟进去,还是到了getSpringFactoriesInstances这个方法,这个方法多个地方都用到了,所以还是要研究一下这个方法
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;
}
(1)这个方法先获取类加载器,这里应用springboot启动时没有指定resourceLoader,所以会直接获得线程上下文类加载器Thread.currentThread().getContextClassLoader()
(2)然后到了,Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
再跟进去SpringFactoriesLoader.loadFactoryNames(type, classLoader)这个方法里面,跟进去。参考https://blog.csdn.net/shefron/article/details/29208551
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
classLoader.getResources把这个META-INF/spring.factories资源文件加载进来
2.2 运行时监听器的启动
listeners.starting();
初始化完SpringApplicationRunListeners 后,就要对这个监听器进行启动,这里实例化出来的是一个EventPublishingRunListener对象,我们再看看这个对象的starting方法
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
这里涉及JAVA中一个重要的编程机制,事件机制,参考这篇文章https://blog.csdn.net/weisong530624687/article/details/89511395:
java的事件机制包括三个部分:EventObject、EventListener、Source
- EventObject:所有事件的基类,所以就是指这个事件,或者动作,或者行为;
- EventListener:所有事件的监听器,接口,就是指监听到对应事件后采取的措施,指具体的行动或响应处理;
- Source:源,事件源,事件的发生者。需要注册监听器
这里面的ApplicationStartingEvent最后继承的是EventObject这个类,multicastEvent这个方法广播这个事件,到multicastEvent这个方法里面去
这个方法先得到事件的类型,ResolvableType eventType = org.springframework.boot.context.event.ApplicationStartingEvent,然后根据这个事件和事件类型获取跟这个事件相关所有的监听器,然后最终调用这些listeners的onApplicationEvent(event)方法处理事件
其实这里比较关键的地方就是怎么找到event和eventType对应的listeners的呢,跟进去这个代码
/**
* Return a Collection of ApplicationListeners matching the given
* event type. Non-matching listeners get excluded early.
* @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);
}
}
这段代码的主要思路先从缓存retrieverCache中尝试获取ListenerRetriever对象,如果存在则直接返回这个retriever中保存的applicationListeners。如果不存在则判断缓存xxxx
1)是,则创建ListenerRetriever实例,通过retrieveApplicationListeners方法刷新这个实例并加到缓存中(这里要进行加锁)
2)不是,则直接调用retrieveApplicationListeners方法返回一个ApplicationListener集合
获取listeners的关键方法是retrieveApplicationListeners(),再跟进去
/**
* Actually retrieve the application listeners for the given event and source type.
* @param eventType the event type
* @param sourceType the event source type
* @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)
* @return the pre-filtered list of application listeners for the given event and source type
*/
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) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, 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);
}
}
}
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;
}
上面提到过在new SpringApplication时设置了SpringApplication的listeners
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
已经把这几个listeners都加载了进来:
而在初始化EventPublishingRunListener时候会把SpringApplication中的listeners都加到initialMulticaster里面
this.initialMulticaster.addApplicationListener进而加到this.defaultRetriever.applicationListeners里面
回到retrieveApplicationListeners()方法里面,就知道会执行这段代码
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
那么关键方法是supportsEvent()里面
最后的判断条件是
(smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType))
supportsEventType和supportsSourceType都是GenericApplicationListener提供的抽象方法,由各个listener实现,比如LoggingApplicationListener的实现
在这个类里面定义了两个数组
最后调用supportedType.isAssignableFrom(type)方法,判断supportedType是否为参数type(就是传进来的)的超类或超接口,传进来的 ResolvableType eventType参数是ApplicationStartingEvent,Class<?> sourceType参数是SpringApplication.class,自然都是满足的。
2.3 准备各种环境和上下文
ConfigurableEnvironment相关的代码
//初始化环境(包含初始化各种属性),这个方法会重点分析
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//根据上面初始化出来的环境向系统类System中设置spring.beaninfo.ignore属性(属性有何用?)
configureIgnoreBeanInfo(environment);
//这个就是向控制台中打印spring boot信息
Banner printedBanner = printBanner(environment);
跟到prepareEnvironment方法中去
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
//更新environment里面的configurationProperties为name的属性
ConfigurationPropertySources.attach(environment);
//也是通过multicastEvent广播一个事件,事件中包含environment对象
listeners.environmentPrepared(environment);
//绑定environment对象到SpringApplication中
bindToSpringApplication(environment);
//判断是否需要转换这个environment的类型:
//1.StandardServletEnvironment.class(webApplicationType = SERVLET)
//2.StandardReactiveWebEnvironment.class(webApplicationType=REACTIVE)
//3.StandardEnvironment.class(webApplicationType=其它)
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//
ConfigurationPropertySources.attach(environment);
return environment;
}
在getOrCreateEnvironment方法中做的事情其实比较简单,存在ConfigurableEnvironment则直接返回,不存在则根据WebApplicationType属性(NONE,SERVLET,REACTIVE)创建相应的环境,configureEnvironment()我们也跟进去看一下:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
这里有几个概念:
- ConversionService:对象转换服务
- PropertySources :一个抽象类(类似于map),存储着name/value属性对
- Profiles :一个string set,存一些从命令行带进来的参数等
得到ConversionService的方法其实是一个经典的懒加载过程,这个ConversionService是一个接口,实现这个接口的类要提供一个线程安全的方法去把对象转换成其它类型
public static ConversionService getSharedInstance() {
ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
synchronized (ApplicationConversionService.class) {
sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
sharedInstance = new ApplicationConversionService();
ApplicationConversionService.sharedInstance = sharedInstance;
}
}
}
return sharedInstance;
}
PropertySources是一个抽象类,有两个属性:
看一下这个configurePropertySources方法
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
其实就是很简单的一些添加到MutablePropertySources这个对象的操作,这个SampleSimpleApplication例子其实在这个方法里面什么都没有做。
再来看一下configureProfiles这个方法,这个方法的作用是配置生效的profiles文件,通过spring.profiles.active可以添加生效的profiles文件。
/**
* 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) {
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key *)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
很简单的集合操作,最后设置到environment的activeProfiles属性中。
后面的几个函数语句的作用都已经在代码里面标记了,篇幅比较长,有兴趣的朋友可以深入阅读。
ConfigurableApplicationContext相关代码
//根据webApplicationType创建不同的ConfigurableApplicationContext实例
context = createApplicationContext();
//这个getSpringFactoriesInstances方法应该很熟悉了,初始化一个SpringBootExceptionReporter的实例
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文,后面重点分析方法
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文,也要重点分析一下
refreshContext(context);
//这个方法为空,实际什么都没做
afterRefresh(context, applicationArguments);
//停止计时
stopWatch.stop();
if (this.logStartupInfo) {
//打印启动日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//还是广播ApplicationStartedEvent事件
listeners.started(context);
//调用实现了CommandLineRunner或者ApplicationRunner的类的run()方法
callRunners(context, applicationArguments);
(1)prepareContext(context, environment, listeners, applicationArguments, printedBanner);方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//把上面创建好的environment设置到context中
context.setEnvironment(environment);
//context后处理,目前只会设置一下conversionService
postProcessApplicationContext(context);
//用初始化器初始化context
applyInitializers(context);
//广播一个applicationContext事件出去
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 注册一些单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 加载资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//广播ApplicationPreparedEvent
listeners.contextLoaded(context);
}
看一下applyInitializers(context)方法·
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
//获取初始化器在实现接口时,指定接口中的范型类型,这里为ConfigurableApplicationContext,因为所有Initializer都实现了ApplicationContextInitializer(public class ConfigurationWarningsApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>)
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
//判断context是否是ConfigurableApplicationContext类型
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//调用initializer的initialize方法
initializer.initialize(context);
}
}
下面我们来看一下这6个Initializer对context做了什么处理
- DelegatingApplicationContextInitializer实际上没有处理context
- SharedMetadataReaderFactoryContextInitializer也主要添加了一个beanFactory的后处理器CachingMetadataReaderFactoryPostProcessor后处理器到applicationContext中
- ContextIdApplicationContextInitializer中,调用DefaultSingletonBeanRegistry的注册方法注册一个单例bean
- ConfigurationWarningsApplicationContextInitializer也是要添加一个beanFactory的后处理器ConfigurationWarningsPostProcessor到applicationContext中
- ServerPortInfoApplicationContextInitializer本身也实现了ApplicationListener,它的initialize方法就是把自身添加到applicationContext的applicationListeners属性中
- ConditionEvaluationReportLoggingListener也是向applicationContext的applicationListeners属性中新增一个ConditionEvaluationReportListener对象,主要是处理报告打印的
这些初始化器都应用到applicationContext后,就是执行
listeners.contextPrepared(context);
这里也是调用SimpleApplicationEventMulticaster.multicastEvent方法广播一个ApplicationContextInitializedEvent事件,有两个listener会处理这个事件
这两个listener的onApplicationEvent方法中,这两个listener其实什么都没做。
之后会打印启动信息,向beanFactory中注册一些单例bean,然后加载资源,加载资源时重点关注一下
load(context, sources.toArray(new Object[0]));
一直单步调试到最后发现调用了AnnotatedBeanDefinitionReader.doRegisterBean()方法注册SampleSimpleApplication这个bean,在spring中bean是通过BeanDefinition对象来描述的,doRegisterBean()方法会在DefaultListableBeanFactory.beanDefinitionMap中添加一个<k,v>对,key=sampleSimpleApplication,value=AnnotatedGenericBeanDefinition(SampleSimpleApplication)
prepareContext完成之后,开始refreshContext,我们来看下refreshContext(context)都做了些什么事(这个方法包含了spring整个ioc的过程,也就是所有bean的创建过程都在这里了)
public void refresh() throws BeansException, IllegalStateException {
//获取锁
synchronized (this.startupShutdownMonitor) {
//主要做了三件事:1.设置启动日志 2.设置激活标记 3.初始化资源
prepareRefresh();
// Tell the subclass to refresh the internal bean factory. 这里的子类就是GenericApplicationContext,跟进去最终在context的beanFactory的serializableFactories中添加一个<k,v>对,key = serializationId,value=new WeakReference(this), 当一个对象仅仅被weak reference指向, 而没有任何其他strong reference指向的时候, 如果GC运行, 那么这个对象就会被回收(这个是context的beanFactory)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 留给子类实现,GenericApplicationContext这个类没有覆盖这个方法
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.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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();
}
}
}
这个refresh方法就是spring依赖注入(DI)/控制反转(IOC)的核心实现,是这个例子的第二个重要知识点,在另外一篇文章中分析这个方法。
最后通过context.publishEvent把应用ready事件广播出去:
listeners.running(context);
调用context的广播事件方法
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
最终执行把SpringApplicationAdminMXBeanRegistrar中的ready标记位设为true
void onApplicationReadyEvent(ApplicationReadyEvent event) {
if (this.applicationContext.equals(event.getApplicationContext())) {
this.ready = true;
}
}