红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Spring Boot 学习笔记
  4. 正文

Spring 源码学习 19:事件框架

2025年7月16日 11点热度 0人点赞 0条评论

利用事件进行解耦

利用事件框架可以增加代码的灵活性和降低耦合性。看一个示例:

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);
    }
}

注入事件发布器:

@Autowired
private ApplicationEventPublisher eventPublisher;

就像在上篇说的,这里的ApplicationEventPublisher是一个特殊 bean,实际上也是 ApplicationContext 容器。

重构代码,在业务代码完成后发送事件:

System.out.println("do some business.");
eventPublisher.publishEvent(new AfterDoSomethingEvent(this));

创建用于发送电子邮件的监听器 Bean:

@Component
static class SendEmailEventListener implements ApplicationListener<AfterDoSomethingEvent> {
​
    @Override
    public void onApplicationEvent(AfterDoSomethingEvent event) {
        System.out.println("send email.");
    }
}

创建发送短信的监听器 Bean:

@Component
static class SendSmsEventListener implements ApplicationListener<AfterDoSomethingEvent>{
​
    @Override
    public void onApplicationEvent(AfterDoSomethingEvent event) {
        System.out.println("send sms.");
    }
}

现在将业务代码与发送电邮和短信进行了解耦。如果要调整业务代码完成后事件相关的业务,只要添加或删除相应的用于事件监听的 Bean 即可。

比如:

@Configuration
@Import({SendEmailEventListener.class})
static class Config{
}

这样就只会发电邮,不会发短信。

@EventListener

除了通过实现ApplicationListener接口的方式创建事件监听,还可以使用@EventListener注解:

@Slf4j
@Component
static class SmsService{
    @EventListener
    public void handleEvent(AfterDoSomethingEvent event) {
        log.info("send sms.");
    }
}
​
@Slf4j
@Component
static class EmailService{
    @EventListener
    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;
}

再执行测试就能看到事件监听器是使用额外线程进行处理:

image-20250716141907441

@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.

参考资料

  • 黑马程序员Spring视频教程,深度讲解spring5底层原理

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: event publisher 事件
最后更新:2025年7月16日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号