spring4.1.8扩展实战之三:广播与监听
提到广播与监听,我们常常会想到RabbitMQ、Kafka等消息中间件,这些常用于分布式系统中多个应用之间,有时候应用自身内部也有广播和监听的需求(例如某个核心数据发生变化后,有些业务模块希望立即被感知),这时候spring提供的基于ApplicationContext的广播与监听就派上用场了,接下来我们从原理到实践,来了解spring提供的这套机制吧。
原文地址:https://blog.csdn.net/boling_cavalry/article/details/81697314
全文概览
总的来说本章内容由以下部分构成:
1. 学习广播服务的核心接口;
2. spring的容器内广播和监听的源码查看;
3. spring是如何支持自定义广播的;
4. spring是如何支持自定义监听的;
5. 开发一个自定义广播;
6. 开发一个自定义监听;
ApplicationEventPublisher:广播服务的核心能力
提到spring容器中的广播和监听,就不得不先看看ApplicationEventPublisher这个接口,今天的故事都是围绕此君展开:
@FunctionalInterface
public interface ApplicationEventPublisher {
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as RequestHandledEvent) or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an event.
* <p>If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* @param event the event to publish
* @since 4.2
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
从上述代码可以看出,发送消息实际上就是在调用ApplicationEventPublisher 接口实现类的publishEvent方法,接下来看看类图,了解在spring容器的设计中ApplicationEventPublisher的定位:
从上图可见,ApplicationContext继承了ApplicationEventPublisher,那么ApplicationContext的实现类自然也就具备了发送广播的能力,按照这个思路去看一下关键的抽象类AbstractApplicationContext,果然找到了方法实现:
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
1
2
3
4
如何广播一条消息
来看一个具体的广播的例子吧,spring容器初始化结束的时候,会执行AbstractApplicationContext类的finishRefresh方法,里面就会广播一条代表初始化结束的消息,代码如下:
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// 广播一条消息,类型ContextRefreshedEvent代表spring容器初始化结束
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
继续深入publichEvent方法内部,看到的代码如下图所示:
上图中的绿色部分的判断不成立,初始化过程中的registerListeners方法就会把成员变量earlyApplicationEvents设置为空,因此广播消息执行的是getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType),在ApplicationContext的实现类中,真正的广播操作交给了成员变量applicationEventMulticaster来完成,没有特殊配置的话,applicationEventMulticaster是SimpleApplicationEventMulticaster类型的实例(详情请看initApplicationEventMulticaster方法);
打开的源码,如下所示,是否有种豁然开朗的感觉?广播的实现是如此简单明了,找到已注册的ApplicationListener,逐个调用invokeListener方法,将ApplicationListener和事件作为入参传进去就完成了广播;
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
看到这里,invokeListener方法所做的事情已经很容易猜到了,ApplicationListener是代表监听的接口,只要调用这个接口的方法并且将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;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
另外还有个细节需要补充,这是在后面做自定义广播的时候考虑到的:如果多线程同时发广播,会不会有线程同步的问题?发送广播的源码一路看下来,发现操作全部在发送的线程中执行的,对各种成员变量的操作也没有出现线程同步问题,唯一有可能出现问题的地方在获取ApplicationListener的时候,这里用到了缓存,有缓存更新的逻辑,而spring已经做好了锁来确保线程同步(双重判断也做了),来看看源码,注意中文注释:
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);
//retrieverCache是个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)))) {
// 这里有锁
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
//抢到锁之后再做一次判断,因为有可能在前面BLOCK的时候,另一个抢到锁的线程已经设置好了缓存
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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
看到这里,我们对spring容器内的广播与监听已经有所了解,小结如下:
1. 广播的核心接口是ApplicationEventPublisher;
2. 我们见过了如何发起一条广播,监听器如何响应广播;
接下来还不能马上动手实战,我们还有两件事情要想弄清楚:
1. 我想发自定义的广播消息,如何具备这个发送能力?
2. 我想接收特定的广播,如何具备这个接收能力?
接下来,继续结合spring源码去找上面两个问题的答案;
源码分析(如何具备消息发送能力)
先看消息生产相关的源码,在上一章《spring4.1.8扩展实战之二:Aware接口揭秘》中,我们看到spring容器初始化的时候会对实现了Aware接口的bean做相关的特殊处理,其中就包含ApplicationEventPublisherAware这个与广播发送相关的接口,代码如下(ApplicationContextAwareProcessor.java):
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
//如果想在应用内发送广播,需要开发一个实现了ApplicationEventPublisherAware接口的bean,就会在此被注入一个ApplicationEventPublisher对象,借助这个对象就能发送广播了
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
由以上代码可见,我们可以创建一个bean,实现了ApplicationEventPublisherAware接口,那么该bean的setApplicationEventPublisher方法就会被调用,通过该方法可以接收到ApplicationEventPublisher类型的入参,借助这个ApplicationEventPublisher就可以发消息了;
源码分析(如何具备消息接收能力)
在spring容器初始化的时候,AbstractApplicationContext类的prepareBeanFactory方法中为所有bean准备了一个后置处理器ApplicationListenerDetector,来看看它的postProcessAfterInitialization方法的代码,也就是bean在实例化之后要做的事情:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
//注册监听器,其实就是保存在成员变量applicationEventMulticaster的成员变量defaultRetriever的集合applicationListeners中
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
}
else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
this.singletonNames.remove(beanName);
}
}
return bean;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
如上所示,如果当前bean实现了ApplicationListener接口,就会调用this.applicationContext.addApplicationListener方法将当前bean注册到applicationContext的监听器集合中,后面有广播就直接找到这些监听器,调用每个监听器的onApplicationEvent方法;
现在把广播与监听的关键代码都看过了,可以开始实战了么?稍等,还有最后一个疑问要弄明白:自定义的消息监听器可以指定消息类型,所有的广播消息中,这个监听器只会收到自己指定的消息类型的广播,spring是如何做到这一点的?
如何做到只接收指定类型的消息
此问题的答案就在spring源码中,但是看源码之前先自己猜测一下,自定义监听器只接收指定类型的消息,以下两种方案都可以实现:
1. 注册监听器的时候,将监听器和消息类型绑定;
2. 广播的时候,按照这条消息的类型去找指定了该类型的监听器,但不可能每条广播都去所有监听器里面找一遍,应该是说广播的时候会触发一次监听器和消息的类型绑定;
带着上述猜测去spring源码中寻找答案吧,先看注册监听器的代码,按照之前的分析,注册监听发生在后置处理器ApplicationListenerDetector中,看看this.applicationContext.addApplicationListener这一行代码的内部逻辑:
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
else {
this.applicationListeners.add(listener);
}
}
1
2
3
4
5
6
7
8
9
10
最终跟踪到AbstractApplicationEventMulticaster类的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) {
//如果因为AOP导致创建了监听类的代理,那么就要在注册列表中清除代理类
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
//把监听器加入集合defaultRetriever.applicationListeners中,这是个LinkedHashSet实例
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
从以上代码我们发现了两个关键点:
1. AOP是通过代理技术实现的,此时可能通过CGLIB生成了监听类的代理类,此类的实例如果也被注册到监听器集合中,那么广播的时候,按照消息类型就会取出两个监听器实例来了,到时候的效果就是一个消息被两个实例消费了,因此这里要先清理掉代理类;
2. 所谓的注册监听器,其实就是把ApplicationListener的实现类放入一个LinkedHashSet的集合,此处没有任何与消息类型相关的操作,因此,监听器注册的时候并没有将消息类型和监听器绑定;
监听器注册的代码看过了,没有绑定,那么只好去看广播消息的代码了,好在前面才看过广播的代码,印象还算深刻,再次来到SimpleApplicationEventMulticaster的multicastEvent方法:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//根据消息类型取出对应的所有监听器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
如上述代码所示,解开我们疑问的关键点就在getApplicationListeners(event, type)这段代码了,也就是说在发送消息的时候根据类型去找所有对应的监听器,展开这个方法一探究竟:
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
//缓存的key有两个维度:消息来源+消息类型(关于消息来源可见ApplicationEvent构造方法的入参)
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// retrieverCache是ConcurrentHashMap对象,所以是线程安全的,
// ListenerRetriever中有个监听器的集合,并有些简单的逻辑封装,调用它的getApplicationListeners方法返回的监听类集合是排好序的(order注解排序)
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
//如果retrieverCache中找到对应的监听器集合,就立即返回了
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
//如果retrieverCache中没有数据,就在此查出数据并放入缓存,
//先加锁
synchronized (this.retrievalMutex) {
//双重判断的第二重,避免自己在BLOCK的时候其他线程已经将数据放入缓存了
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
//新建一个ListenerRetriever对象
retriever = new ListenerRetriever(true);
//retrieveApplicationListeners方法复制找出某个消息类型加来源类型对应的所有监听器
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
//存入retrieverCache
this.retrieverCache.put(cacheKey, retriever);
//返回结果
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
上述代码解开了所有疑惑:在广播消息的时刻,如果某个类型的消息在缓存中找不到对应的监听器集合,就调用retrieveApplicationListeners方法去找出符合条件的所有监听器,然后放入这个集合;这和之前的猜测匹配度挺高的…
retrieveApplicationListeners方法在面对各种监听器的时候处理逻辑过于复杂,就不在这里展开了;
至此,我们对广播和监听的原理已经有了较全面的认识,可以动手实战了;
开发一个自定义广播
现在基于maven创建一个SpringBoot的web工程customizeapplicationevent,在工程中自定义一个发送广播的bean,在收到一个web请求时,利用该bean发出广播;
1. 创建工程customizeapplicationevent,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bolingcavalry</groupId>
<artifactId>customizeapplicationevent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>customizeapplicationevent</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2. 自定义一个消息类型CustomizeEvent:
package com.bolingcavalry.customizeapplicationevent.event;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;
/**
* @Description : 自定义的消息类型
* @Author : zq2599@gmail.com
* @Date : 2018-08-16 06:09
*/
public class CustomizeEvent extends ApplicationContextEvent {
public CustomizeEvent(ApplicationContext source) {
super(source);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3. 自定义广播发送者CustomizePublisher,除了用到ApplicationEventPublisher,还要用到ApplicationContext(构造CustomizeEvent对象的时候要用),所以要实现两个Aware接口:
package com.bolingcavalry.customizeapplicationevent.publish;
import com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent;
import com.bolingcavalry.customizeapplicationevent.util.Utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
/**
* @Description : 自定义的广播发送器
* @Author : zq2599@gmail.com
* @Date : 2018-08-16 06:09
*/
@Service
public class CustomizePublisher implements ApplicationEventPublisherAware, ApplicationContextAware {
private ApplicationEventPublisher applicationEventPublisher;
private ApplicationContext applicationContext;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
Utils.printTrack("applicationEventPublisher is set : " + applicationEventPublisher);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 发送一条广播
*/
public void publishEvent(){
applicationEventPublisher.publishEvent(new CustomizeEvent(applicationContext));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
4. 做一个controller,这样就能通过web请求来触发一次广播了:
package com.bolingcavalry.customizeapplicationevent.controller;
import com.bolingcavalry.customizeapplicationevent.publish.CustomizePublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* @Description : 收到web请求后发送一条广播
* @Author : zq2599@gmail.com
* @Date : 2018-08-16 06:45
*/
@RestController
public class CustomizePublishEventController {
@Autowired
private CustomizePublisher customizePublisher;
@RequestMapping("/publish")
public String publish(){
customizePublisher.publishEvent();
return "publish finish, "
+ new Date();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
自定义广播的代码已经完成了,不过现在即使把消息发出去了也不能验证是否发送成功,先不要运行工程,接下来把自定义消息监听也做了吧;
开发两个自定义监听器
我们要做两个自定义监听器;
1. 第一个监听类在泛型中定义的类型是ApplicationEvent,即接收所有广播消息;
package com.bolingcavalry.customizeapplicationevent.listener;
import com.bolingcavalry.customizeapplicationevent.util.Utils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
/**
* @Description : 自定义的系统广播监听器,接收所有类型的消息
* @Author : zq2599@gmail.com
* @Date : 2018-08-15 14:53
*/
@Service
public class AllEventListener implements ApplicationListener<ApplicationEvent>{
@Override
public void onApplicationEvent(ApplicationEvent event) {
//为了便于了解调用栈,在日志中打印当前堆栈
Utils.printTrack("onApplicationEvent : " + event);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2. 第二个监听类在泛型中定义的类型是CustomizeEvent,即只接收CustomizeEvent类型的消息;
package com.bolingcavalry.customizeapplicationevent.listener;
import com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent;
import com.bolingcavalry.customizeapplicationevent.util.Utils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
/**
* @Description : 自定义的系统广播监听器,只接受CustomizeEvent类型的消息
* @Author : zq2599@gmail.com
* @Date : 2018-08-15 14:53
*/
@Service
public class CustomizeEventListener implements ApplicationListener<CustomizeEvent>{
@Override
public void onApplicationEvent(CustomizeEvent event) {
//为了便于了解调用栈,在日志中打印当前堆栈
Utils.printTrack("onApplicationEvent : " + event);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
3. 把工程运行起来,先见到CustomizePublisher被注入ApplicationEventPublisher对象的日志,然后看到AllEventListener收到广播后打印的日志,如下所示,可通过日志中的堆栈信息印证我们之前看过的广播监听相关源码:
2018-08-16 18:35:15.656 INFO 7864 --- [ main] c.b.c.util.Utils : applicationEventPublisher is set : org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@37e547da: startup date [Thu Aug 16 18:35:14 CST 2018]; root of context hierarchy
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.publish.CustomizePublisher.setApplicationEventPublisher() 27 <-
org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces() 114 <-
org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization() 96 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization() 416 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean() 1,691 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean() 573 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean() 495 <-
org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0() 317 <-
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton() 222 <-
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean() 315 <-
org.springframework.beans.factory.support.AbstractBeanFactory.getBean() 199 <-
org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate() 251 <-
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency() 1,135 <-
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency() 1,062 <-
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject() 583 <-
org.springframework.beans.factory.annotation.InjectionMetadata.inject() 91 <-
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues() 372 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean() 1,341 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean() 572 <-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean() 495 <-
org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0() 317 <-
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton() 222 <-
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean() 315 <-
org.springframework.beans.factory.support.AbstractBeanFactory.getBean() 199 <-
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons() 759 <-
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization() 869 <-
org.springframework.context.support.AbstractApplicationContext.refresh() 550 <-
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh() 140 <-
org.springframework.boot.SpringApplication.refresh() 762 <-
org.springframework.boot.SpringApplication.refreshContext() 398 <-
org.springframework.boot.SpringApplication.run() 330 <-
org.springframework.boot.SpringApplication.run() 1,258 <-
org.springframework.boot.SpringApplication.run() 1,246 <-
com.bolingcavalry.customizeapplicationevent.CustomizeapplicationeventApplication.main() 10
************************************************************
2018-08-16 18:35:15.732 INFO 7864 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-16 18:35:15.875 INFO 7864 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@37e547da: startup date [Thu Aug 16 18:35:14 CST 2018]; root of context hierarchy
2018-08-16 18:35:15.919 INFO 7864 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/publish]}" onto public java.lang.String com.bolingcavalry.customizeapplicationevent.controller.CustomizePublishEventController.publish()
2018-08-16 18:35:15.924 INFO 7864 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-08-16 18:35:15.925 INFO 7864 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-08-16 18:35:15.945 INFO 7864 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-16 18:35:15.946 INFO 7864 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-16 18:35:16.059 INFO 7864 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-16 18:35:16.069 INFO 7864 --- [ main] c.b.c.util.Utils : onApplicationEvent : org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@37e547da: startup date [Thu Aug 16 18:35:14 CST 2018]; root of context hierarchy]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.context.support.AbstractApplicationContext.finishRefresh() 888 <-
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh() 161 <-
org.springframework.context.support.AbstractApplicationContext.refresh() 553 <-
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh() 140 <-
org.springframework.boot.SpringApplication.refresh() 762 <-
org.springframework.boot.SpringApplication.refreshContext() 398 <-
org.springframework.boot.SpringApplication.run() 330 <-
org.springframework.boot.SpringApplication.run() 1,258 <-
org.springframework.boot.SpringApplication.run() 1,246 <-
com.bolingcavalry.customizeapplicationevent.CustomizeapplicationeventApplication.main() 10
************************************************************
2018-08-16 18:35:16.094 INFO 7864 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-08-16 18:35:16.096 INFO 7864 --- [ main] c.b.c.util.Utils : onApplicationEvent : org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@1c25b8a7]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh() 164 <-
org.springframework.context.support.AbstractApplicationContext.refresh() 553 <-
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh() 140 <-
org.springframework.boot.SpringApplication.refresh() 762 <-
org.springframework.boot.SpringApplication.refreshContext() 398 <-
org.springframework.boot.SpringApplication.run() 330 <-
org.springframework.boot.SpringApplication.run() 1,258 <-
org.springframework.boot.SpringApplication.run() 1,246 <-
com.bolingcavalry.customizeapplicationevent.CustomizeapplicationeventApplication.main() 10
************************************************************
2018-08-16 18:35:16.099 INFO 7864 --- [ main] b.c.CustomizeapplicationeventApplication : Started CustomizeapplicationeventApplication in 2.098 seconds (JVM running for 3.161)
2018-08-16 18:35:16.101 INFO 7864 --- [ main] c.b.c.util.Utils : onApplicationEvent : org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@77b7ffa4]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.boot.context.event.EventPublishingRunListener.started() 97 <-
org.springframework.boot.SpringApplicationRunListeners.started() 72 <-
org.springframework.boot.SpringApplication.run() 337 <-
org.springframework.boot.SpringApplication.run() 1,258 <-
org.springframework.boot.SpringApplication.run() 1,246 <-
com.bolingcavalry.customizeapplicationevent.CustomizeapplicationeventApplication.main() 10
************************************************************
2018-08-16 18:35:16.103 INFO 7864 --- [ main] c.b.c.util.Utils : onApplicationEvent : org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@77b7ffa4]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.boot.context.event.EventPublishingRunListener.running() 103 <-
org.springframework.boot.SpringApplicationRunListeners.running() 78 <-
org.springframework.boot.SpringApplication.run() 346 <-
org.springframework.boot.SpringApplication.run() 1,258 <-
org.springframework.boot.SpringApplication.run() 1,246 <-
com.bolingcavalry.customizeapplicationevent.CustomizeapplicationeventApplication.main() 10
************************************************************
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
4. 在浏览器输入地址:http://localhost:8080/publish,触发一次CustomizeEvent类型的消息广播,日志显示CustomizeEventListener和AllEventListener都收到了消息,如下:
2018-08-16 18:44:18.879 INFO 7864 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-08-16 18:44:18.880 INFO 7864 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2018-08-16 18:44:18.912 INFO 7864 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 32 ms
2018-08-16 18:44:18.958 INFO 7864 --- [nio-8080-exec-1] c.b.c.util.Utils : onApplicationEvent : com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@37e547da: startup date [Thu Aug 16 18:35:14 CST 2018]; root of context hierarchy]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
com.bolingcavalry.customizeapplicationevent.publish.CustomizePublisher.publishEvent() 39 <-
com.bolingcavalry.customizeapplicationevent.controller.CustomizePublishEventController.publish() 24 <-
sun.reflect.NativeMethodAccessorImpl.invoke0() -2 <-
sun.reflect.NativeMethodAccessorImpl.invoke() 62 <-
sun.reflect.DelegatingMethodAccessorImpl.invoke() 43 <-
java.lang.reflect.Method.invoke() 498 <-
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke() 209 <-
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest() 136 <-
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle() 102 <-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod() 877 <-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal() 783 <-
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle() 87 <-
org.springframework.web.servlet.DispatcherServlet.doDispatch() 991 <-
org.springframework.web.servlet.DispatcherServlet.doService() 925 <-
org.springframework.web.servlet.FrameworkServlet.processRequest() 974 <-
org.springframework.web.servlet.FrameworkServlet.doGet() 866 <-
javax.servlet.http.HttpServlet.service() 635 <-
org.springframework.web.servlet.FrameworkServlet.service() 851 <-
javax.servlet.http.HttpServlet.service() 742 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 231 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.tomcat.websocket.server.WsFilter.doFilter() 52 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.RequestContextFilter.doFilterInternal() 99 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal() 109 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal() 93 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal() 200 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.catalina.core.StandardWrapperValve.invoke() 198 <-
org.apache.catalina.core.StandardContextValve.invoke() 96 <-
org.apache.catalina.authenticator.AuthenticatorBase.invoke() 493 <-
org.apache.catalina.core.StandardHostValve.invoke() 140 <-
org.apache.catalina.valves.ErrorReportValve.invoke() 81 <-
org.apache.catalina.core.StandardEngineValve.invoke() 87 <-
org.apache.catalina.connector.CoyoteAdapter.service() 342 <-
org.apache.coyote.http11.Http11Processor.service() 800 <-
org.apache.coyote.AbstractProcessorLight.process() 66 <-
org.apache.coyote.AbstractProtocol$ConnectionHandler.process() 800 <-
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() 1,471 <-
org.apache.tomcat.util.net.SocketProcessorBase.run() 49 <-
java.util.concurrent.ThreadPoolExecutor.runWorker() 1,142 <-
java.util.concurrent.ThreadPoolExecutor$Worker.run() 617 <-
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() 61 <-
java.lang.Thread.run() 745
************************************************************
2018-08-16 18:44:18.964 INFO 7864 --- [nio-8080-exec-1] c.b.c.util.Utils : onApplicationEvent : com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@37e547da: startup date [Thu Aug 16 18:35:14 CST 2018]; root of context hierarchy]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.CustomizeEventListener.onApplicationEvent() 19 <-
com.bolingcavalry.customizeapplicationevent.listener.CustomizeEventListener.onApplicationEvent() 14 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
com.bolingcavalry.customizeapplicationevent.publish.CustomizePublisher.publishEvent() 39 <-
com.bolingcavalry.customizeapplicationevent.controller.CustomizePublishEventController.publish() 24 <-
sun.reflect.NativeMethodAccessorImpl.invoke0() -2 <-
sun.reflect.NativeMethodAccessorImpl.invoke() 62 <-
sun.reflect.DelegatingMethodAccessorImpl.invoke() 43 <-
java.lang.reflect.Method.invoke() 498 <-
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke() 209 <-
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest() 136 <-
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle() 102 <-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod() 877 <-
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal() 783 <-
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle() 87 <-
org.springframework.web.servlet.DispatcherServlet.doDispatch() 991 <-
org.springframework.web.servlet.DispatcherServlet.doService() 925 <-
org.springframework.web.servlet.FrameworkServlet.processRequest() 974 <-
org.springframework.web.servlet.FrameworkServlet.doGet() 866 <-
javax.servlet.http.HttpServlet.service() 635 <-
org.springframework.web.servlet.FrameworkServlet.service() 851 <-
javax.servlet.http.HttpServlet.service() 742 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 231 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.tomcat.websocket.server.WsFilter.doFilter() 52 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.RequestContextFilter.doFilterInternal() 99 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal() 109 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal() 93 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal() 200 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.catalina.core.StandardWrapperValve.invoke() 198 <-
org.apache.catalina.core.StandardContextValve.invoke() 96 <-
org.apache.catalina.authenticator.AuthenticatorBase.invoke() 493 <-
org.apache.catalina.core.StandardHostValve.invoke() 140 <-
org.apache.catalina.valves.ErrorReportValve.invoke() 81 <-
org.apache.catalina.core.StandardEngineValve.invoke() 87 <-
org.apache.catalina.connector.CoyoteAdapter.service() 342 <-
org.apache.coyote.http11.Http11Processor.service() 800 <-
org.apache.coyote.AbstractProcessorLight.process() 66 <-
org.apache.coyote.AbstractProtocol$ConnectionHandler.process() 800 <-
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() 1,471 <-
org.apache.tomcat.util.net.SocketProcessorBase.run() 49 <-
java.util.concurrent.ThreadPoolExecutor.runWorker() 1,142 <-
java.util.concurrent.ThreadPoolExecutor$Worker.run() 617 <-
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() 61 <-
java.lang.Thread.run() 745
************************************************************
2018-08-16 18:44:18.995 INFO 7864 --- [nio-8080-exec-1] c.b.c.util.Utils : onApplicationEvent : ServletRequestHandledEvent: url=[/publish]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcherServlet]; session=[null]; user=[null]; time=[66ms]; status=[OK]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.web.servlet.FrameworkServlet.publishRequestHandledEvent() 1,074 <-
org.springframework.web.servlet.FrameworkServlet.processRequest() 1,005 <-
org.springframework.web.servlet.FrameworkServlet.doGet() 866 <-
javax.servlet.http.HttpServlet.service() 635 <-
org.springframework.web.servlet.FrameworkServlet.service() 851 <-
javax.servlet.http.HttpServlet.service() 742 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 231 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.tomcat.websocket.server.WsFilter.doFilter() 52 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.RequestContextFilter.doFilterInternal() 99 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal() 109 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal() 93 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal() 200 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.catalina.core.StandardWrapperValve.invoke() 198 <-
org.apache.catalina.core.StandardContextValve.invoke() 96 <-
org.apache.catalina.authenticator.AuthenticatorBase.invoke() 493 <-
org.apache.catalina.core.StandardHostValve.invoke() 140 <-
org.apache.catalina.valves.ErrorReportValve.invoke() 81 <-
org.apache.catalina.core.StandardEngineValve.invoke() 87 <-
org.apache.catalina.connector.CoyoteAdapter.service() 342 <-
org.apache.coyote.http11.Http11Processor.service() 800 <-
org.apache.coyote.AbstractProcessorLight.process() 66 <-
org.apache.coyote.AbstractProtocol$ConnectionHandler.process() 800 <-
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() 1,471 <-
org.apache.tomcat.util.net.SocketProcessorBase.run() 49 <-
java.util.concurrent.ThreadPoolExecutor.runWorker() 1,142 <-
java.util.concurrent.ThreadPoolExecutor$Worker.run() 617 <-
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() 61 <-
java.lang.Thread.run() 745
************************************************************
2018-08-16 18:44:19.392 INFO 7864 --- [nio-8080-exec-2] c.b.c.util.Utils : onApplicationEvent : ServletRequestHandledEvent: url=[/favicon.ico]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcherServlet]; session=[null]; user=[null]; time=[19ms]; status=[OK]
************************************************************
java.lang.Thread.getStackTrace() 1,556 <-
com.bolingcavalry.customizeapplicationevent.util.Utils.printTrack() 25 <-
com.bolingcavalry.customizeapplicationevent.listener.AllEventListener.onApplicationEvent() 18 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener() 172 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener() 165 <-
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent() 139 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 400 <-
org.springframework.context.support.AbstractApplicationContext.publishEvent() 354 <-
org.springframework.web.servlet.FrameworkServlet.publishRequestHandledEvent() 1,074 <-
org.springframework.web.servlet.FrameworkServlet.processRequest() 1,005 <-
org.springframework.web.servlet.FrameworkServlet.doGet() 866 <-
javax.servlet.http.HttpServlet.service() 635 <-
org.springframework.web.servlet.FrameworkServlet.service() 851 <-
javax.servlet.http.HttpServlet.service() 742 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 231 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.tomcat.websocket.server.WsFilter.doFilter() 52 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.RequestContextFilter.doFilterInternal() 99 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal() 109 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal() 93 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal() 200 <-
org.springframework.web.filter.OncePerRequestFilter.doFilter() 107 <-
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() 193 <-
org.apache.catalina.core.ApplicationFilterChain.doFilter() 166 <-
org.apache.catalina.core.StandardWrapperValve.invoke() 198 <-
org.apache.catalina.core.StandardContextValve.invoke() 96 <-
org.apache.catalina.authenticator.AuthenticatorBase.invoke() 493 <-
org.apache.catalina.core.StandardHostValve.invoke() 140 <-
org.apache.catalina.valves.ErrorReportValve.invoke() 81 <-
org.apache.catalina.core.StandardEngineValve.invoke() 87 <-
org.apache.catalina.connector.CoyoteAdapter.service() 342 <-
org.apache.coyote.http11.Http11Processor.service() 800 <-
org.apache.coyote.AbstractProcessorLight.process() 66 <-
org.apache.coyote.AbstractProtocol$ConnectionHandler.process() 800 <-
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() 1,471 <-
org.apache.tomcat.util.net.SocketProcessorBase.run() 49 <-
java.util.concurrent.ThreadPoolExecutor.runWorker() 1,142 <-
java.util.concurrent.ThreadPoolExecutor$Worker.run() 617 <-
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() 61 <-
java.lang.Thread.run() 745
************************************************************
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
实战完成,自定义的广播发送和监听都达到了预期;
实战源码下载
本章实战的源码可以在github下载,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
这个git项目中有多个文件夹,本章源码在文件夹customizeapplicationevent下,如下图红框所示:
至此,spring实战的广播与监听部分,我们通过先看源码再动手实践的方式做了比较深入的了解,希望本次实战能对你在设计进程内的消息服务时有所帮助,借助spring容器和扩展能力,设计出更加简洁高效的服务;
---------------------
作者:博陵精骑
来源:CSDN
原文:https://blog.csdn.net/boling_cavalry/article/details/81697314
版权声明:本文为博主原创文章,转载请附上博文链接!
spring4.1.8扩展实战之三:广播与监听的更多相关文章
- spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)
本章是<spring4.1.8扩展实战>的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再 ...
- spring4.1.8扩展实战之八:Import注解
spring4.1.8扩展实战之八:Import注解 2018年09月10日 12:53:57 博陵精骑 阅读数:441更多 所属专栏: spring4源码分析与实战 版权声明:欢迎转载,请注明 ...
- spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)
本章是<spring4.1.8扩展实战>系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean: 原文地址:https://blog.csdn.net/boli ...
- spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)
本章是<spring4.1.8扩展实战>的第七篇,我们来尝试在容器初始化的时候对bean实例做设置: 原文地址:https://blog.csdn.net/boling_cavalry/a ...
- 26 Flutter仿京东商城项目 购物车之 event_bus事件广播 事件监听
event_bus 介绍 在前面的课程我们给大家讲过状态管理 Provider 的使用. 通俗的讲状态管理就是:当我们想在多个页面(组件/Widget)之间共享状态(数据),或 者一个页面(组件/Wi ...
- spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)
本章我们继续实战spring的扩展能力,通过自定义BeanFactoryPostProcessor接口的实现类,来对bean实例做一些控制: 原文地址:https://blog.csdn.net/bo ...
- Android初级教程使用服务注册广播接收者监听手机解锁屏变化
之前第七章广播与服务理论篇写到: 特殊的广播接收者(一般发广播次数频率很高) 安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的 屏幕锁屏和解锁 电量改变 今天在这里就回顾一下,且用代码方 ...
- ros之tf坐标系广播与监听的编程实现
创建功能包-learning_tf $ cd ~/catkin_ws/src $ catkin_create_pkg learning_tf roscpp rospy tf turtlesim 如何创 ...
- spring4.1.8扩展实战之二:Aware接口揭秘
Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就 ...
随机推荐
- 洛谷P1265 公路修建——prim
给一手链接 https://www.luogu.com.cn/problem/P1265 这道题本质上就是最小生成树,题目描述就是prim的思想 TIP:注意稠密图和稀疏图的区别 #include&l ...
- IDEA 光标显示注释
- redis 持久化 哨兵 主从
Redis搭建步骤 环境: 三台机器 centos7 关闭防火墙 selinux Redis版本 3.0.5 依赖环境 yum install gcc-c++ ruby rubygems –y 把版 ...
- python的小介绍
Python简介 龟叔 优美.清晰.简单 主要应用领域: 云计算 WEB开发 科学技术.人工智能 系统运维 爬虫 金融量化分析 图形GUI 游戏 Python发展史 1989年,Guido开始写Pyt ...
- bzoj1897. tank 坦克游戏(决策单调性分治)
题目描述 有这样一款新的坦克游戏.在游戏中,你将操纵一辆坦克,在一个N×M的区域中完成一项任务.在此的区域中,将会有许多可攻击的目标,而你每摧毁这样的一个目标,就将获得与目标价值相等的分数.只有获得了 ...
- JS中去除字符串空白符
海纳百川,有容乃大 1.通过原型创建字符串的trim() //去除字符串两边的空白 String.prototype.trim=function(){ return this.replace(/(^\ ...
- XML处理指令
“处理指令(PIs)允许文档包含用于应用程序的指令.指令并不是文档字符数据的一部分,但是必须通过应用程序传递”. 处理指令可以用于将信息传递给应用程序.处理指令可以出现在文档任意位置的标记外部.可以出 ...
- presentingViewController、presentedViewController区别
解释两个属性:presentingViewController 和 presentedViewController A----(present)-----B----(present)-----C 1. ...
- ssh-add - 向认证代理添加 RSA 或 DSA 身份数据
总览 (SYNOPSIS) ssh-add [-lLdDx ] [-t life ] [file ... ] ssh-add -s reader ssh-add -e reader 描述 (DESCR ...
- python时间的获取
一.获取当前时间 import datetime # 2019-7-9 print(datetime.datetime.now().year) # 2019 print(datetime.dateti ...