接上一篇

一. getRunListeners()

在run() 方法中调用了 getRunListeners(args) 方法, 先看一下这个方法干了什么

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

加载配置 spring.factories 中的配置,

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

然后创建配置中的 EventPublishingRunListener , 封装到  SpringApplicationRunListeners 类中的 this.listeners 属性中.

1. 创建 EventPublishingRunListener

public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}

1. 这里创建了一个多播器: SimpleApplicationEventMulticaster

2. 将容器中10个监听器放入多播器中.

上一篇提到过, 容器中加载了10个监听器, 放在 this.listeners 属性中.

这里的顺序与配置的读取顺序不同, 是经过排序过的.

addApplicationListener()
@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);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
ListenerRetriever 是 AbstractApplicationEventMulticaster 的一个内部类. 所以监听器是放在 一个内部类的 applicationListeners 属性中:
public final Set<ApplicationListener<?>> applicationListeners;

二. listeners.starting()

public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}

此处的 this.listeners 中的 this -> SpringApplicationRunListeners .

所以调用的是 EventPublishingRunListener的starting() 方法.

//EventPublishingRunListener.java
@Override
public void starting() {
   //创建Application的启动事件, 并进行多波
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}

看一下 multicastEvent 方法:

//SimpleApplicationEventMulticaster.java
@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));
  // getApplicationListeners 会对监听器进行过滤
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
     //获取线程池, 暂时为null
Executor executor = getTaskExecutor();
if (executor != null) {
       //异步发送事件
executor.execute(() -> invokeListener(listener, event));
}
else {
       //同步发送事件
invokeListener(listener, event);
}
}
}
getApplicationListeners()
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

1.  判断监听器是否为 GenericApplicationListener 类型

  |-> 如果是, 则调用其 supportsEventType() 和 supportsSourceType() , 且同时满足, 才可以. 否则会被过滤掉

  |-> 如果不是, 则 使用GenericApplicationListenerAdapter 进行适配转换, 然后 调用上面两个方法, 同时满足, 才可以. 否则会被过滤掉

满足条件的有4个监听器:

此处看一下 LoggingApplicationListener 的两个方法执行:

1. supportsEventType()

private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class,
ContextClosedEvent.class, ApplicationFailedEvent.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) {
if (supportedType.isAssignableFrom(type)) {
return true;
}
}
}
return false;
}

可以看到, 这里他支持5中类型, 其中正好就有当前发布的 ApplicationStartingEvent 事件.

2. supportsSourceType()

private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class,
ApplicationContext.class };
@Override
public boolean supportsSourceType(Class<?> sourceType) {
  //这里调用的还是上面的那个方法, 只是传入参数不同
return isAssignableFrom(sourceType, SOURCE_TYPES);
}
invokeListener() 
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  //不管 errorhandler 是否为null, 都会调用 doInvokeListener 方法
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
} private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// 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.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}

这里就是调用 监听器的  onApplicationEvent 方法, 并传入要多波的事件.

这里仍然来看 LoggingApplicationListener 的 onApplicationEvent 方法:

@Override
public void onApplicationEvent(ApplicationEvent event) {
  //Application启动
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
  //环境准备完成
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
  //Application 准备完
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
  //容器关闭
else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
.getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
  //Application启动失败
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}

不同的事件进来, 执行不同的方法.

srpingboot web - 启动(3) 监听器的更多相关文章

  1. springboot web - 启动(2) run()

    接上一篇 在创建 SpringApplication 之后, 调用了 run() 方法. public ConfigurableApplicationContext run(String... arg ...

  2. WEB启动时就加载servlet的dopost方法

    web启动的时候可以加载servlet的init方法,无法加载dopost方法,如果你需要什么内容在启动的时候执行,可以将内容放到init方法里面,dopost方法,是在客户端使用post请求的时候才 ...

  3. 从web启动winform程序

    最近有个客户提出想从网站上启动一个客户端的程序,研究了下,实现方法如下: 1. 注入注册表 try                {                    string appPath ...

  4. Jenkins新建节点,启动方式没有“通过Java Web启动代理”选项怎么办?

    在Jenkins中,打开“系统管理”→“管理节点”→“新建节点”页面时,“启动方式”选项没有“通过Java Web启动代理”,怎么办? 打开“系统管理”,进入“全局安全配置”页面. 1. “JNLP代 ...

  5. Jenkins新建节点找不到通过Java web启动代理?

    参考博客:Jenkins新建节点,启动方式没有“通过Java Web启动代理”选项怎么办? 在Jenkins中,打开“系统管理”→“管理节点”→“新建节点”页面时,“启动方式”选项没有“通过Java ...

  6. 启动oracle11监听器错误

    启动oracle11监听器错误:本地计算机上的OracleOraDb11g_home1TNSListener服务启动后又停止了解决方案 . 关键字:启动oracle10监听器错误:本地计算机上的Ora ...

  7. 启动web项目,监听器、过滤器、拦截器启动顺序

    启动顺序:监听器 > 过滤器 > 拦截器 记忆技巧:接到命令,监听电报,过滤敌情,拦截行动.

  8. web.xml中监听器配置

    <!-- 监听器的配置:监听器配置完以后,应用系统在启动的时候就会开启这些监听器. 监听器的理解:监听器好比一个卫兵,卫兵一直站在那里等待长官的命令,当卫兵收到长官的命令以后,立即执行 之前已经 ...

  9. Web中的监听器【Listener】

    Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext.HttpSession和ServletRequest等域对象的创建与销毁事件,以及 ...

随机推荐

  1. python3 控制结构知识及范例

    (一)三种基本控制结构 1.顺序结构:按照程序语句编写的顺序依次逐条执行 2.选择结构:根据特定的条件选择某一个分支 3.循环结构:反复执行某个或者某些操作,关注点:在什么情况下,反复执行哪一部分的操 ...

  2. Codeforces_845

    A.排序,比较中间两个大小. #include<bits/stdc++.h> using namespace std; ]; int main() { ios::sync_with_std ...

  3. JAVA编程思想——分析阅读

    需要源码.JDK1.6 .编码风格参考阿里java规约 7/12开始 有点意识到自己喜欢理论大而泛的模糊知识的学习,而不喜欢实践和细节的打磨,是因为粗心浮躁导致的么? cron表达式使用 设计能力.领 ...

  4. Mysql事务结合spring管理

    spring事务相关问题记录 遇到情况: 在本地单体应用调试代码时,发现在一个加了@transaction注解的方法里进行先更新后查询的操作,查询的结果是可以看到更新的内容的.而在微服务环境中同样的代 ...

  5. JMeter+Grafana+Influxdb搭建可视化性能测试监控平台(待继续完善。。。)

    influxdb下载.安装.配置.启动 InfluxDB是一个当下比较流行的时序数据库,InfluxDB使用 Go 语言编写,无需外部依赖,安装配置非常方便,适合构建大型分布式系统的监控系统. 下载: ...

  6. Spring Cloud(七):服务网关zuul过滤器

    上文介绍了Zuul的基本使用与路由功能,本文接着介绍Zuul的核心概念 -- Zuul过滤器(filter). Zuul的功能基本通过Zuul过滤器来实现(类比于Struts的拦截器,只是Struts ...

  7. 深入理解幂等性及Restful风格API的幂等性问题详解

    什么是幂等性 HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外).也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同. ...

  8. NCE L3

    单词 课文

  9. Java日志介绍(4)-Log4j2

    Log4j2是Log4j的升级版,相比其前身Log4j 1.x提供了显著的改进,并提供了在Logback中提供的许多改进,同时修复了Logback体系结构中的一些固有问题. Log4j2的内容很多,本 ...

  10. 「Flink」Flink 1.9 WebUI运行作业界面分析

    运行作业界面 在以下界面中,可以查看到作业的名称.作业的启动时间.作业总计运行时长.作业一共有多少个任务.当前正在运行多少个任务.以及作业的当前状态. 这里的程序:一共有17个任务,当前正在运行的是1 ...