文章目录
- 1.测试案例
- 2.DEBUG源码分析
- 3. 异步监听
- 4.ApplicationListener子接口
- 5. 注解支持
- 6. 基于观察者模式高仿spring事件监听
- 6.1 先定义自定义一个事件
- 6.2 定义两个监听器
- 6.3 定义一个持有所有监听器的对象,类似spring的`SimpleApplicationEventMulticaster`
- 6.4 事件发布测试
1.测试案例
定义一个事件
package com.example.demo.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
定义两个listener
package com.example.demo.event;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class Listener1 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
String source = (String) event.getSource();
System.out.println(this.getClass().getName() + ":" + source);
}
}
package com.example.demo.event;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class Listener2 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
String source = (String) event.getSource();
System.out.println(this.getClass().getName() + ":" + source);
}
}
注入spring容器里的ApplicationEventPublisher对象,发布事件
package com.example.demo;
import com.example.demo.event.MyEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class EventPublisherTest {
@Resource
private ApplicationEventPublisher eventPublisher;
@Test
public void test() {
eventPublisher.publishEvent(new MyEvent("xxx"));
}
}
2.DEBUG源码分析
从eventPublisher.publishEvent(new MyEvent("xxx"));
进去很容易就能找到,可以发现SimpleApplicationEventMulticaster这个事件发布对象持有所有listenter对象及MyEvent对象,
事件发布过程其实就是遍历拿到每个listener对象并调用它自己的onApplicationEvent()
方法
SimpleApplicationEventMulticaster类的主要方法:
- addApplicationListener(ApplicationListener<?> listener) :
- addApplicationListenerBean(String listenerBeanName):
- removeApplicationListener(ApplicationListener<?> listener):
- removeApplicationListenerBean(String listenerBeanName):
- multicastEvent(ApplicationEvent event):广播事件;
- multicastEvent(ApplicationEvent event, @Nullable
ResolvableType eventType):广播事件,指定事件的source类型。
3. 异步监听
从上图断点可以看到ApplicationEventMulticaster对象持有有taskExecutor字段为null,导致没有异步执行所有监听器。这里需要想办法这个字段设置线程池即可:
springboot默认会配置一个ThreadPoolTaskExecutor对象在容器里,这里把它拿出来设置给ApplicationEventMulticaster对象即可
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* @author Administrator
*/
@SpringBootApplication
public class DemoApplication {
@Resource
private SimpleApplicationEventMulticaster eventMulticaster;
@Resource
private ThreadPoolTaskExecutor executor;
@PostConstruct
public void setEventExecutor() {
eventMulticaster.setTaskExecutor(executor);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
4.ApplicationListener子接口
还可以实现子类的getOrder方法可以实现多个监听器排序;实现supportsEventType,supportsSourceType可以实现按过滤Class过来或按事件源Source的Class过滤
5. 注解支持
这里不通过实现接口来实现监听,顺序排序,按事件类型过滤监听,采用更方便的注解实现,且一个类中就可以实现多个监听
package com.example.demo.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
public class Listener3 {
@Order(Integer.MIN_VALUE) // 优先级最高
@EventListener(MyEvent.class) // 只监听指定类型的事件
public void onApplicationEvent(ApplicationEvent event) {
MyEvent myEvent = (MyEvent) event;
System.out.println(this.getClass().getName() + ":" + myEvent.getSource());
}
}
6. 基于观察者模式高仿spring事件监听
6.1 先定义自定义一个事件
Java中已定义观察者模式事件及监听器顶级接口
package com.example.demo.javanativeevent;
import java.util.EventObject;
public class MyJavaNativeEvent extends EventObject {
public MyJavaNativeEvent(Object source) {
super(source);
}
}
6.2 定义两个监听器
因为Java提供的EventListener无具体监听方法,且无合适的子接口,故这里自定义一个类似spirng的子接口NativeEventListener
。这样后续事件发布就是遍历这种类型接口并调用onApplicationEvent()方法
package com.example.demo.javanativeevent;
import java.util.EventListener;
import java.util.EventObject;
public interface NativeEventListener extends EventListener {
void onApplicationEvent(EventObject event);
}
package com.example.demo.javanativeevent;
import java.util.EventObject;
public class JavaNativeListener1 implements NativeEventListener {
@Override
public void onApplicationEvent(EventObject event){
String source = (String)event.getSource();
System.out.println(this.getClass().getName()+":"+source);
}
}
6.3 定义一个持有所有监听器的对象,类似spring的SimpleApplicationEventMulticaster
package com.example.demo.javanativeevent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.List;
/**
* TODO
*
* @author majun
* @version 1.0
* @since 2023-08-13 18:50
*/
public class SimpleJavaNativeEventMulticaster {
public List<NativeEventListener> eventListeners= Collections.synchronizedList(new ArrayList<>(8));
public void addListener(NativeEventListener listener){
this.eventListeners.add(listener);
}
public void multicastEvent(EventObject eventObject){
eventListeners.stream().parallel().forEach(listener -> {
listener.onApplicationEvent(eventObject);
});
}
}
6.4 事件发布测试
package com.example.demo.javanativeevent;
/**
* TODO
*
* @author majun
* @version 1.0
* @since 2023-08-13 19:01
*/
public class EventPublishTest {
public static void main(String[] args) {
// 注册两个listener,这是spirng把这个过程隐藏在spring容器初始化过程中了
SimpleJavaNativeEventMulticaster eventMulticaster = new SimpleJavaNativeEventMulticaster();
eventMulticaster.addListener(new JavaNativeListener1());
eventMulticaster.addListener(new JavaNativeListener2());
// 发布事件
eventMulticaster.multicastEvent(new MyJavaNativeEvent("yyyy"));
}
}