事件和直接方法调用适合于不同的情况。使用方法调用需要知道某件事A发生并调用某个方法B处理它, 即知道发生什么事情然后做某件事的场景; 对于事件来说我们知道发生了一个事件A,那些模块会被通知处理并不是我们关系的问题,即知道发生什么事情但不关心要做某事的场景。比如,当我们想要将某些业务处理传递给另一个线程时候,使用事件更好一些
Spring 事件/监听机制属于事件/监听器设计模式, 可以视为观察者模式(Observer Pattern)的扩展, 观察者模式的Observable发布的对象是Object,因此观察者模式可以发布任何对象, 而事件/监听器模式发布的内容是有类型限制的,在 Java中它必须是EventObject对象, 因此Spring 事件抽象类ApplicationEvent继承了EventObject
Spring事件机制的几个重要组件,
事件(event)可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标,一般是ApplicationEvent的子类
事件发布者(publisher)事件发生的触发者。
事件广播器(Multicaster)作为实际发布事件的委托,将所有事件多播给注册的监听器,实现让监听器忽略它们不感兴趣的事件
监听器(listener)具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。
创建事件类MessageEvent 继承ApplicationEvent即可
public class MessageEvent extends ApplicationEvent {
private String message;
public MessageEvent(Object source, String message) {
super(source);
this.message = message;
}
//省略getter和setter方法
}
创建消息发布对象MessagePublish
package com.boot.basic.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MessagePublish {
@Autowired
AnnotationConfigApplicationContext applicationContext;
public void publish(String msg) {
applicationContext.publishEvent(new MessageEvent(this,msg));
}
}
在Spring Boot中应用上下文对象publishEvent后, 事件将交给SimpleApplicationEventMulticaster处理, 它是ApplicationEventMulticaster接口的实现类, 该接口主要承担两种职责,一是关联ApplicationListener,二是广播ApplicationEvent
创建启动类将EventConfig通过@Configuration声明为配置类,启动后发布条内容为This is a event msg的消息,用于测试上面的场景
package com.boot.basic.event;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.boot.basic.event")
public class EventConfig {
public static void main(String[] args) throws InterruptedException {
System.out.println("==========start context=============");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
//容器启动后发布1条消息
MessagePublish publish = context.getBean(MessagePublish.class);
publish.publish("This is a event msg");
System.out.println("==========close context=============");
}
}
此方式实现ApplicationListener接口, 创建MessageListener监听
package com.boot.basic.event;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MessageListener implements ApplicationListener {
@Override
public void onApplicationEvent(MessageEvent messageEvent) {
String msg = messageEvent.getMessage();
System.out.println();
System.out.println("Message Tesing######## "+this.getClass().getName() + ":" + msg);
}
}
监听器实现方式
1. 启动容器时从注册的Bean中找到所有ApplicationListener类型的Bean,将这些监听注册到事件广播对象中
// AbstractApplicationContext#registerListeners部分代码
protected void registerListeners() {
//略掉部分代码
//1. 通过ApplicationListener获取到实现其接口的所有BEAN
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
//2.将ApplicationListener监听Beans 都添加到事件广播器中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
//略掉部分代码
}
2.,事件发布者将时间提交到广播器中, 广播器循环注册的监听如果符合条件就让监听器执行事件处理
使用注解 @EventListener实现Spring事件监听,简化编写监听类的步骤,不需要再继承ApplicationListener接口去实现onApplicationEvent
@EventListener 注解,指定 classes,即需要处理的事件类型,一般就是 ApplicationEven 及其子类,例如@EventListener(MessageEvent.class)
package com.boot.basic.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 基于注解驱动 使用注解 @EventListener实现Spring事件监听
*/
@Component
class EventsListener {
@EventListener(MessageEvent.class)
public void onEvent(MessageEvent event) {
String msg = event.getMessage();
System.out.println();
System.out.println("Message Tesing######## "+this.getClass().getName() + ":" + msg);
}
}
监听器实现方式
1. Spring容器初始化 时, AbstractApplicationContext#finishBeanFactoryInitialization阶段将注解类生成一个事件ApplicationListener接口的ApplicationListenerMethodAdapter对象
2. 事件发布者将时间提交到广播器中, 广播器循环注册的监听如果符合条件就让监听器执行事件处理
上述方式为同步处理,当多个监听器接收事件并处理时,如果先处理的监听器阻塞,那么之后的监听器都需要等其处理完后再处理
Message Tesing######## com.boot.basic.event.EventsListener:This is a event msg
Message Tesing######## com.boot.basic.event.MessageListener:This is a event msg
==========close context=============
SimpleApplicationEventMulticaster允许时间广播的时候ApplicaitonListener异步处理监听时间, 其中ExecutortaskExecutor通过,源码参考如下
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
//注入线程池
public void setTaskExecutor(@Nullable Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener> listener : getApplicationListeners(event, type)) {
//这里判断如果对象注入了taskExcutor,那么就创建线程异步处理
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
//略
}
可以使用 @EnableAsync开启Spring异步处理流程,默认启动流程为搜索线程池定义中的 TaskExcute实例,或一个名为 taskExcute的java.util.concurrent.Excecutor实例,如果以上都没找到,则会使用SimpleAsyncTaskExceuteor 处理异步方法调用。
参考代码
修改 EventConfig 类使用 @EnableAsync开启异步处理,并设置一个线程池
package com.boot.basic.event;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@EnableAsync
@Configuration
@ComponentScan("com.boot.basic.event")
public class EventConfig {
public static void main(String[] args) throws InterruptedException {
System.out.println("==========start context=============");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
MessagePublish publish = context.getBean(MessagePublish.class);
publish.publish("This is a event msg");
System.out.println("==========close context=============");
//context.close();
}
private static final int corePoolSize = 10; // 核心线程数(默认线程数)
private static final int maxPoolSize = 100; // 最大线程数
private static final int keepAliveTime = 10; // 允许线程空闲时间(单位:默认为秒)
private static final int queueCapacity = 200; // 缓冲队列数
private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀
@Bean("taskExecutor")
public ThreadPoolTaskExecutor getAsyncExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
2. 修改监听器,增加注解@Async使监听器处理方法异步实现
package com.boot.basic.event;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* 面向接口编程,实现ApplicationListener接口
*/
@Component
public class MessageListener implements ApplicationListener {
@Async
@Override
public void onApplicationEvent(MessageEvent messageEvent) {
try {
Thread.sleep(3000L);
String msg = messageEvent.getMessage();
System.out.println();
System.out.println("Message Tesing######## "+this.getClass().getName() + ":" + msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
重新运行EventConfig,会发现打印close contex后才执行MsssageListener#onApplicationListener 方法, 云因是方法执行异步,且处理时候线程sleep等待了几秒
Message Tesing######## com.boot.basic.event.EventsListener:This is a event msg
==========close context=============
Message Tesing######## com.boot.basic.event.MessageListener:This is a event msg
1. 创建时间监听
package com.boot.basic.event;
import org.springframework.context.ApplicationListener;
public class TestEventListener implements ApplicationListener {
public void onApplicationEvent(MessageEvent messageEvent) {
System.out.println("Message Tesing######## "+this.getClass().getName() + ":" + messageEvent.getMessage());
}
}
2. 启动类添加创建的监听
@RestController
@SpringBootApplication
public class EventDemoApplication {
@Autowired
private MessagePublish messagePublish;
public static void main(String[] args) {
SpringApplication springApplication =
new SpringApplication(EventDemoApplication.class);
springApplication.addListeners(new TestEventListener());
springApplication.run(args);
}
@GetMapping("/send")
public String hello(@RequestParam(value = "msg", defaultValue = "Test message") String message) {
messagePublish.publish(message);
return "success";
}
}
测试访问 http://127.0.0.1:8080/send?msg=show.me.the.money
查看控制台会发现启动添加的监听TestEventListener:show和通过组件添加的监听器都正常运行
Message Tesing######## com.boot.basic.event.EventsListener:show.me.the.money
Message Tesing######## com.boot.basic.event.TestEventListener:show.me.the.money
Message Tesing######## com.boot.basic.event.MessageListener:show.me.the.money
上面的所有代码都可以从github 找到 : 链接:GITHUB - 事件DEMO
前一篇: SpringBoot 02 配置文件