一起学SF框架系列4.8-spring-context-事件机制(Event)

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果将实现ApplicationListener接口的bean部署到上下文中,则每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。从本质上讲,这是标准的观察者设计模式。从Spring 4.2开始,提供了一个基于注释的事件模型,以及发布任何任意事件(即不一定从ApplicationEvent扩展的对象)的能力。

事件机制

Spring的事件机制是为同一应用程序容器中Spring bean之间的简单通信而设计的。机制工作示意图:
在这里插入图片描述
工作过程如下:
1、定义事件
2、事件发布bean通过发布器发布消息(默认发布器是)ApplicationContext。
3、发布器调用广播机广播方法。
4、广播机选择匹配消息的事件接收Bean,调用onApplication处理消息。
事件接收Bean在初始化过程中自动注册到广播机。

事件发布

发布实现

发布事件的bean通过调用ApplicationEventPublisher.publishEvent(Object event)发布事件,该方法实现在AbstractApplicationContext中,代码如下:

	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 applEvent) {
			applicationEvent = applEvent;
		}
		else {
			//创建为一个PayloadApplicationEvent
			applicationEvent = new PayloadApplicationEvent<>(this, event, eventType);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			//延迟官博
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			//立即广播 注1
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		//如果存在父容器,在父容器中广播
		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {
				abstractApplicationContext.publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

注1:事件广播器实际就是对所有Listener进行注册管理,在广播时逐一按事件匹配广播。事件广播器如果由应用实现,需实现ApplicationEventMulticaster接口;如果应用没有实现,则用spring提供的SimpleApplicationEventMulticaster作为事件广播器。

框架事件

ContextRefreshedEvent:ApplicationContext 初始化或刷新时发布。对应实现AbstractApplicationContext.finishRefresh()
ContextStartedEvent:ApplicationContext 调用start()方法时发布。对应实现AbstractApplicationContext.start()
ContextStoppedEvent:ApplicationContext 调用stop()方法时发布。对应实现AbstractApplicationContext.stop()
ContextClosedEvent:ApplicationContext 调用close()方法时发布,或者JVM关闭时发布。对应实现AbstractApplicationContext.doClose()
RequestHandledEvent:在WebApplicationContext中,当一个request完成时发布。
ServletRequestHandledEvent:RequestHandledEvent 子类,在ServletContext下使用。

自定义事件

定义事件

自定义事件继承ApplicationEvent 。

public class BlockedEmailEvent extends ApplicationEvent {
	//事件类的属性可以当做事件传参
	private final String email;
	private final String content;

	public BlockedEmailEvent(Object source, String email, String content) {
		//ApplicationEvent构建
		super(source);
		this.email= email;
		this.content = content;
	}
}

事件发布

若要发布自定义ApplicationEvent,在ApplicationEventPublisher上调用publishEvent()方法。通常,这是通过创建一个实现ApplicationEventPublisherware的类并将其注册为Springbean来完成的。下面实例就是发送email时,如果是黑名单,则不发送并发送BlockedEmailEvent

@Component
public class EmailService implements ApplicationEventPublisherAware {
	//阻止发送名单
	private List<String> blockedList;
	//发布器
	private ApplicationEventPublisher publisher;

	public void setBlockedList(List<String> blockedList) {
		this.blockedList = blockedList;
	}

	//使用容器的事件发布器(容器会自动调用该方法)
	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
		this.publisher = publisher;
	}

	public void sendEmail(String email, String content) {
		//通知没有发送的email
		if (blockedList.contains(email)) {
			publisher.publishEvent(new BlockedEmailEvent(this, email, content));
			return;
		}
		// 真正发送email...
	}
}

事件接收

事件接收分为同步接收和异步接收。

同步接收

ApplicationListener同步接收事件,表示发送者的publishEvent()方法会阻塞,直到所有侦听器都完成了对事件的处理。同步一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布者的事务上下文中操作。

代码方式

public class BlockedEmailListener implements ApplicationListener<BlockedEmailEvent> {
	public void onApplicationEvent(BlockedEmailEvent event) {
		//接收到对应事件,进行处理
		// notify appropriate parties via notificationAddress...
	}
}

注解方式

public class BlockedEmailListener {
	//注解,参数为事件
	@EventListener
	public void processBlockedEmailEvent(BlockedEmailEvent event) {
		// notify appropriate parties via notificationAddress...
	}
}

异步接收

public class BlockedEmailListener {
	//注解,参数为事件
	@EventListener
	@Async
	public void processBlockedEmailEvent(BlockedEmailEvent event) {
		// notify appropriate parties via notificationAddress...
	}
}

处理顺序

如果一个事件有多个Listener,且有先后顺序要求,用注解@Order,通常同@EventListener配套使用。

public class BlockedEmailListener {
	//注解,参数为事件
	@EventListener
	@Order(5)
	public void processBlockedEmailEvent(BlockedEmailEvent event) {
		// notify appropriate parties via notificationAddress...
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐享技术

每一个打赏,都是对我最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值