利用事件框架可以增加代码的灵活性和降低耦合性。看一个示例:
System.out.println("do some business.");
System.out.println("send email.");
System.out.println("send sms.");
需要在完成一段业务逻辑后发送电子邮件和短信,现在业务代码和发送短信及电邮是耦合在一起的。
创建一个表示业务完成的事件类:
static class AfterDoSomethingEvent extends ApplicationEvent {
public AfterDoSomethingEvent(Object source) {
super(source);
}
}
注入事件发布器:
private ApplicationEventPublisher eventPublisher;
就像在说的,这里的
ApplicationEventPublisher
是一个特殊 bean,实际上也是ApplicationContext
容器。
重构代码,在业务代码完成后发送事件:
System.out.println("do some business.");
eventPublisher.publishEvent(new AfterDoSomethingEvent(this));
创建用于发送电子邮件的监听器 Bean:
static class SendEmailEventListener implements ApplicationListener<AfterDoSomethingEvent> {
public void onApplicationEvent(AfterDoSomethingEvent event) {
System.out.println("send email.");
}
}
创建发送短信的监听器 Bean:
static class SendSmsEventListener implements ApplicationListener<AfterDoSomethingEvent>{
public void onApplicationEvent(AfterDoSomethingEvent event) {
System.out.println("send sms.");
}
}
现在将业务代码与发送电邮和短信进行了解耦。如果要调整业务代码完成后事件相关的业务,只要添加或删除相应的用于事件监听的 Bean 即可。
比如:
SendEmailEventListener.class})
({static class Config{
}
这样就只会发电邮,不会发短信。
@EventListener
除了通过实现ApplicationListener
接口的方式创建事件监听,还可以使用@EventListener
注解:
static class SmsService{
public void handleEvent(AfterDoSomethingEvent event) {
log.info("send sms.");
}
}
static class EmailService{
public void handleEvent(AfterDoSomethingEvent event) {
log.info("send email.");
}
}
效果是相同的。
多线程
默认的 Spring 事件框架使用的是单线程,如果要改为多线程,需要提供一个实现了ApplicationEventMulticaster
接口的 Bean:
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(){
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
return simpleApplicationEventMulticaster;
}
需要注意的是,这个 Bean 的名称是固定的applicationEventMulticaster
,Spring 通过名称进行匹配,如果是其它名称 Spring 是不会使用的。
setTaskExecutor
方法是设置ApplicationEventMulticaster
使用的线程池,所以还需要添加一个线程池:
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(10);
executor.setCorePoolSize(5);
executor.setQueueCapacity(100);
return executor;
}
再执行测试就能看到事件监听器是使用额外线程进行处理:
@EventListener 注解原理
下面用代码模拟@EventListener
注解的实现原理。
添加一个自定义注解:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective
@interface MyListener {
}
使用自定义注解替换@EventListener
:
@Slf4j
@Component
static class SmsService {
@MyListener
public void handleEvent(AfterDoSomethingEvent event) {
log.info("send sms.");
}
}
遍历容器中的 Bean,查找使用自定义注解标记的方法,查找到符合的方法后,添加一个对应的 ApplicationListener
:
@Bean
public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext applicationContext) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
// 所有单例初始化后调用
// 获取所有的 bean
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
Object bean = applicationContext.getBean(beanDefinitionName);
// 检查 bean 是否有包含了一个参数的 @MyListener 方法
Method[] methods = bean.getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyListener.class)) {
Class<?>[] parameterTypes = method.getParameterTypes();
// 检查是不是只有一个形参
if (parameterTypes.length != 1) {
continue;
}
Class<?> eventParameterType = parameterTypes[0];
// 检查参数类型是不是 ApplicationEvent
if (!ApplicationEvent.class.isAssignableFrom(eventParameterType)) {
continue;
}
// 为每个方法创建一个 ApplicationListener
applicationContext.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@SneakyThrows
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 判断产生的事件是否与事件处理方法的类型匹配
if (eventParameterType.isAssignableFrom(event.getClass())) {
// 反射调用方法
method.invoke(bean, event);
}
}
});
}
}
}
}
};
}
这里利用了SmartInitializingSingleton
接口,实现了这个接口的 Bean 会在所有单例被初始化后进行调用。
实现事件发布
在本文的前面部分已经提到过,实际上ApplicationEventPublisher
使用ApplicationEventMulticaster
进行事件发布,因此下面通过实现ApplicationEventMulticaster
的方式用代码模拟事件发布功能的实现原理。
用一个自定义抽象类实现ApplicationEventPublisher
接口:
abstract static class AbsMyApplicationEventMulticaster implements ApplicationEventMulticaster {
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
}
@Override
abstract public void addApplicationListenerBean(String listenerBeanName);
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
}
@Override
public void removeApplicationListenerBean(String listenerBeanName) {
}
@Override
public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {
}
@Override
public void removeApplicationListenerBeans(Predicate<String> predicate) {
}
@Override
public void removeAllListeners() {
}
@Override
public void multicastEvent(ApplicationEvent event) {
}
@Override
abstract public void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}
用这个抽象类实现ApplicationEventMulticaster
Bean:
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ApplicationContext applicationContext) {
return new AbsMyApplicationEventMulticaster() {
private final List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
@Override
public void addApplicationListenerBean(String listenerBeanName) {
ApplicationListener<ApplicationEvent> listener = applicationContext.getBean(listenerBeanName, ApplicationListener.class);
listeners.add(listener);
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
for (ApplicationListener<ApplicationEvent> listener : listeners) {
listener.onApplicationEvent(event);
}
}
};
}
实际运行会报错:
Caused by: java.lang.ClassCastException: class org.springframework.context.event.ContextRefreshedEvent cannot be cast to class com.example.demo.EventTests6$AfterDoSomethingEvent (org.springframework.context.event.ContextRefreshedEvent and com.example.demo.EventTests6$AfterDoSomethingEvent are in unnamed module of loader 'app')
原因是容器的 refresh
事件产生后,也会由ApplicationEventMulticaster
调用自定义监听器,但实际上这些监听器只能用于处理自定义事件,因此产生的类型转换失败导致了运行错误。
实现对监听器支持的事件类型的处理:
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ApplicationContext applicationContext) {
return new AbsMyApplicationEventMulticaster() {
private final List<GenericApplicationListener> listeners = new ArrayList<>();
@Override
public void addApplicationListenerBean(String listenerBeanName) {
ApplicationListener<ApplicationEvent> listener = applicationContext.getBean(listenerBeanName, ApplicationListener.class);
GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
listener.onApplicationEvent(event);
}
@Override
public boolean supportsEventType(ResolvableType eventType) {
Class<? extends ApplicationListener> clazz = listener.getClass();
ResolvableType[] interfaces = ResolvableType.forClass(clazz).getInterfaces();
if (interfaces.length == 0){
return false;
}
ResolvableType generic = interfaces[0].getGeneric();
return generic.isAssignableFrom(eventType);
}
};
listeners.add(genericApplicationListener);
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
for (GenericApplicationListener listener : listeners) {
if (listener.supportsEventType(eventType)) {
listener.onApplicationEvent(event);
}
}
}
};
}
本文的完整示例代码可以从获取。
The End.
文章评论