Spring事件机制

Spring事件机制介绍

Spring的事件通知机制是一项很有用的功能,使用事件机制我们可以将相互耦合的代码解耦,从而方便功能的修改与添加;

spring事件分为3步:

  • 事件定义 (事件定义:需要继承:ApplicationEvent)
  • 事件监听 (事件监听:需要实现:ApplicationListener)
  • 事件发布

发布一个自定义的事件,会触发该事件监听器的onApplicationEvent方法

Spring为我们提供了一些已经定义好了的事件:

  • ApplicationContextEvent
  • ContextClosedEvent 当容器关闭时候会触发该事件的监听器
  • ContextRefreshedEvent 当容器refresh完成后会触发该事件的监听器
  • ContextStartedEvent 当容器启动会触发该事件的监听器
  • ContextStoppedEvent 当容器关闭的时候会触发该事件的监听器
  • ServletRequestHandledEvent Spring MVC 请求完成之后推送会触发该事件监听器

Spring自定义事件通知

  • 1.自定义事件:
public class DemoEvent extends ApplicationEvent {

    private String message;

    public DemoEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage(){
        return this.message;
    }
    
    public void setMessage(String message) {
        this.message = message;
    }
}
  • 2.定义事件监听器:

监听器1:

@Component
@Order(1)
public class DemoListener implements ApplicationListener<DemoEvent> {

    @Override
    public void onApplicationEvent(DemoEvent event) {
        System.out.println("当前线程 " + Thread.currentThread().getName() + ": receiver : " + event.getMessage());
        event.setMessage(event.getMessage() + "@1");
    }

}

监听器2:

@Component
@Order(5)
public class Demo2Listener implements ApplicationListener<DemoEvent> {

    @Override
    public void onApplicationEvent(DemoEvent event) {
        System.out.println("当前线程 " + Thread.currentThread().getName() + ": receiver 2: " + event.getMessage());
    }

}

@Order 注解作用:当监听器被触发时,监听器的执行顺序,order值越小越先执行;

  • 3.事件发布:
@Component
public class DemoPublish {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publish(String message) {
        //执行相关的业务逻辑
        System.out.println("当前线程 " + Thread.currentThread().getName() + ": ");
        DemoEvent demoEvent = new DemoEvent(this, message);
        applicationEventPublisher.publishEvent(demoEvent);
    }
}
  • 4.单元测试:
@SpringBootTest
class DemoApplicationTests {

    @Autowired
    private DemoPublish demoPublish;

    @Test
    public void testPublish(){
        demoPublish.publish("hello world");
    }
}
  • 5.运行结果:
当前线程 main: 
当前线程 main: receiver : hello world
当前线程 main: receiver 2: hello world@1

如果我们需要在执行相关业务逻辑后处理一些其他相关的操作,就可以再增加一个该事件的监听器;

通过运行结果:我们不难发现,该事件都是同步操作的;如果在一些业务场景中,我们希望个别监听器的业务逻辑,不要阻塞主流程的执行,我们可以采用异步的方式:如下。

异步事件监听

在上部分的基础上:

  • 1.添加一个配置类:
@Configuration
public class DemoConfiguration {

    /**
     * 容器中注册一个 线程池
     * @return
     */
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor(){
        return new SimpleAsyncTaskExecutor();
    }

    /**
     * 自定义一个 ApplicationEventMulticaster 在容器中
     * @param simpleAsyncTaskExecutor
     * @return
     */
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster(SimpleAsyncTaskExecutor simpleAsyncTaskExecutor){
        SimpleApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();
        applicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor);
        return applicationEventMulticaster;
    }
}
  • 2.运行单元测试,输出的结果:
当前线程 main: 
当前线程 SimpleAsyncTaskExecutor-14: receiver : hello world
当前线程 SimpleAsyncTaskExecutor-15: receiver 2: hello world@1

由于跑的是单元测试,可能会涉及到主线程结束,所有子线程都挂掉的情况;

通过运行结果可以发现:DemoListener、Demo2Listener 都在不同的线程中执行;

如果就简单这样处理,会出现一个问题:当DemoConfiguration被加载到容器后,所有的监听器都变成异步的了;也就说所有的事件都被异步化了; 解决此类问题方式如下:


  • 3.新增加一个监听器:
@Component
@Order(3)
public class Demo3Listener implements ApplicationListener<DemoEvent> {

    @Async
    @Override
    public void onApplicationEvent(DemoEvent event) {
        System.out.println("当前线程 " + Thread.currentThread().getName() + ": receiver 3: " + event.getMessage());
    }

}

可以看出,在onApplicationEvent方法上添加了一个@Async的注解,加上这个注解的方法在另外的线程中执行, 另外的线程就是从配置类中注入的线程池中获取;

  • 4.修改配置类DemoConfiguration:
@Configuration
@EnableAsync
public class DemoConfiguration {

    /**
     * 容器中注册一个 线程池
     * @return
     */
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor(){
        return new SimpleAsyncTaskExecutor();
    }

}

可以看出,在配置类上添加了一个@EnableAsync注解,该注解就是开启异步执行;并且删除了自定义ApplicationEventMulticaster

  • 5.运行单元测试,查看输出结果:
当前线程 main: 
当前线程 main: receiver : hello world
当前线程 main: receiver 2: hello world@1
当前线程 SimpleAsyncTaskExecutor-1: receiver 3: hello world@1

由于跑的是单元测试,可能会涉及到主线程结束,所有子线程都挂掉的情况

Spring 事件机制通知原理分析

  • 1.事件发布中调用applicationEventPublisher.publishEvent(demoEvent);publishEvent方法源码如下:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		// earlyApplicationEvents 为null
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
		// 获取ApplicationEventMulticaster,调用`multicastEvent`方法广播事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}
  • 2.multicastEvent方法:
@Override
	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) {
			//如果线程池不为null,就采用异步方式触发事件监听器
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
			//否则就采用主线程触发事件监听器
				invokeListener(listener, event);
			}
		}
	}

注意:在异步监听器事件通知中,采用自定义ApplicationEventMulticaster ,并给该它设置一个线程池;因此在该配置类被加载后,就会导致所有的事件都被异步化了; 最后通过启用异步操作:@EnableSync 注解 + @Sync注解的方式实现个别事件监听器进行异步操作,该方式的原理就是对标记了有@Sync注解的方法进行代理,采用线程池的方式执行标记了@Sync方法。

参考: https://blog.wangqi.love/articles/Java/Spring%20Event%E4%BA%8B%E4%BB%B6%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值