Spring的事件机制

Spring的事件包含三部分

  • 事件(ApplicationEvent):继承自 jdk 的 EventObject,所有的事件都需要继承 ApplicationEvent,并且通过构造器参数 source 得到事件源.
  • 应用事件监听器(ApplicationListener):继承自 jdk 的 EventListener,所有的监听器都要实现这个接口
  • 事件发布者(ApplicationEventPublisher,ApplicationContext 实现了 ApplicationEventPublisher 接口):具体实现类是 SimpleApplicationEventMulticaster,在 Spring 中维护监听者。ApplicationEventMulticaster#multicastEvent方法将事件传给监听器

Spring默认事件类型

  • ContextRefreshedEvent:在初始化或刷新时 ApplicationContext 发布
  • ContextStartedEvent:通过在 ConfigurableApplicationContext 接口上使用 start() 方法启动 ApplicationContext 时发布,通常,此信号用于在显式停止后重新启动 bean
  • ContextStoppedEvent:使用 ConfigurableApplicationContext 接口上的 stop()方法停止 ApplicationContext 时发布,停止的上下文可以通过 start() 调用重新启动
  • ContextClosedEvent:通过使用 ConfigurableApplicationContext 接口上的 close() 方法或通过 JVM shutdown hook 钩子函数在 ApplicationContext 被关闭时发布。在这里,“关闭”意味着所有单例 bean 都将被销毁
  • RequestHandledEvent:一个特定于 web 的事件,它告诉所有 bean 一个 HTTP 请求已经得到了服务。此事件在请求完成后发布。此事件仅适用于使用 Spring 的 DispatcherServlet 的 Web 应用程序

自定义事件

注解方式

新建一个用户注册事件,继承ApplicationEvent

public class UserRegisterEvent extends ApplicationEvent {

    public UserRegisterEvent(User user) {
        super(user);
    }

}

创建两个事件监听器,@EventListener注解用在方法上

@Slf4j
@Service
public class UserRegisterListener {
    @EventListener
    public void sendMsg(UserRegisterEvent userRegisterEvent) {
        User user = (User) userRegisterEvent.getSource();
        log.info("发送短信,手机号{}", user.getPhone());
    }

    @EventListener
    public void sendEmail(UserRegisterEvent userRegisterEvent) {
        User user = (User) userRegisterEvent.getSource();
        log.info("发送邮箱,邮箱{}", user.getEmail());
    }
}

在用户注册逻辑完成后,发布用户注册事件

@Service
public class UserServiceImpl {

    public final ApplicationEventPublisher applicationEventPublisher;

    public UserServiceImpl(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void register() {
        log.info("用户注册");
        // 发送用户注册事件
        applicationEventPublisher.publishEvent(new UserRegisterEvent(User.builder().name("lisi").email("abc@xxx.com").phone("8008208820").build()));
    }
}

运行结果:

可以看到事件已经发布,并且监听成功。

但是从上图可以看到,主逻辑和监听器的线程是同一个,也就是在同一个线程中执行,如果想多线程做异步处理,该怎么做?

事件的发布是在事件广播器的ApplicationEventMulticaster#multicastEvent方法,具体的实现是SimpleApplicationEventMulticaster#multicastEvent

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 获取线程池
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 判断线程池是否为空
	if (executor != null) {
            // 线程池不为空,监听器交给线程池处理
	    executor.execute(() -> invokeListener(listener, event));
	}
	else {
            // 线程池为空,执行监听器
	    invokeListener(listener, event);
	}
    }
}

在 Spring 启动时,会初始化事件广播器AbstractApplicationContext#initApplicationEventMulticaster

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 判断容器是否包含了 ApplicationEventMulticaster
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
	this.applicationEventMulticaster =
			beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
	if (logger.isTraceEnabled()) {
		logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
	}
    }
    else {
        // 没有就创建一个,并注册到容器
	this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
	beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
	if (logger.isTraceEnabled()) {
		logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
				"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
	}
    }
}

如果容器不包含ApplicationEventMulticaster,就会创建一个SimpleApplicationEventMulticaster,这时候executor就会为空。

如果我们自己写一个SimpleApplicationEventMulticaster的Bean实例,并且给实例设置线程池的属性,那监听器就会交给线程池处理,实现异步处理

@Configuration
public class MyConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster () {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(createTaskExecutor());
        return simpleApplicationEventMulticaster;
    }

    private ExecutorService createTaskExecutor() {
        CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("applicationEventMulticaster-");
        return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), threadFactory);
    }
}

运行结果:

由上图可以发现,监听器已经由线程池处理,做到了主逻辑和监听器的异步

posted @   咖啡因的取悦  阅读(249)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示