SpringEvent使用
开发中常会遇到需要解耦的业务,比较典型的场景是电商系统订单结算完成通知商品服务减库存。结算的成功与否也不依赖库存扣减结果,扣减库存不需要实时完成(秒杀除外)。分布式场景可以使用消息队列方案,单机应用可以起一个线程调用扣减接口,但是线程调用较难维护,因为新增一个下游节点就要在代码中加一个调用接口,不优雅。比较起来SpringEvent消息通知机制可以很好的处理单机场景。
SpringEvent的使用十分简单,核心内容为创建继承了ApplicationEvent的事件、调用ApplicationEventPublisher发送事件、接收者实现ApplicationListener方法接收事件。使用分为以下三个步骤:
声明事件(继承ApplicationEvent类)
public class UserRegisterEvent extends ApplicationEvent {
private static final long serialVersionUID = -4829855648590354032L;
public UserRegisterEvent(User user) {
super(user);
}
public User getUser() {
return (User) source;
}
}
发送事件(调用ApplicationEventPublisher的publishEvent方法发送事件)
@Autowired
private ApplicationEventPublisher publisher;
@Override
public void register(User user) throws Exception {
publisher.publishEvent(new UserRegisterEvent(user));
}
监听器接收事件(新建监听类,实现ApplicationListener接口,实现onApplicationEvent方法)
public class CouponListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent event) {
System.out.println(event.getUser());
}
}
SpringEvent源码分析
读源码是一个枯燥的过程,源码解析的图书、教程也很难理解,不管读者的基础如何,开头就介绍十几个命名相似、方法名贼拉长的类,然后A调B,B调C,C调E,E再调A,A又调C.....,巴拉巴拉一大堆调用,我往往翻了两三页就忘了把前面的调用关系都忘了,也没搞懂为什么要这么调。
其实学习框架源码时可以换个思路,我们可以把源码当成是一段没汉语注解的、写的比较优秀的代码,和我们日常写的业务代码从本质上没有区别。
既然框架源码和普通代码没区别,为什么那么难懂呢?
回想一下,我们新加入一个公司熟悉项目的时候会把class文件反编译出来挨个阅读吗?不会,那我们是怎么做的呢?
先熟悉整个项目,了解项目中用到什么技术、登陆系统操作一下功能。再深入各个模块,了解各自模块实现了什么业务,对接了哪些接口等。最后深入阅读代码。按着这样的套路一遍走下来就能轻松熟悉整个项目。
所以在分析框架源码前我们也可以先对使用场景、使用的目的、调用方法整体进行整体把握。思考源码底层要实现什么业务,可能会用什么技术(比如反射、切面、线程等)去实现,最后思考假如不用框架自己会怎么实现。
所以学习SpringEvent时我们可以套一下这个方法。我们使用SpringEvent的目的就是为了触发方发出消息,下游方法方法接受消息。具体的实现是调用了applicationEventPublisher的publishEvent方法,传入了UserRegisterEvent这个事件,触发onApplicationEvent。
读源码前可以大胆提几个猜测:
- UserRegisterEvent的作用是为了辨识推送者和接受者,因为在调用前并不知道接收方是谁
- 在运行中动态调用接受者可能是使用了反射机制。
带着这些猜测来验证吧!
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, 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<Object>(this, event);
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 {
//调用广播方法,发送事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
}
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//获取事件的监听者,事件是以广播形式传播,可能有多个监听者,所以循环广播
//
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
//如果是异步事件,起一个线程去反射调用监听者
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
//非异步,反射调用监听者
invokeListener(listener, event);
}
}
}
//通过事件类型和事件体获取说有监听者
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//调用监听者的onApplicationEvent方法,就是我们写的CouponListener类的方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
SpringEvent的实现方法中用到了AbstractApplicationContext、SimpleApplicationEventMulticaster、AbstractApplicationEventMulticaste等底层公用类的方法,本文不深究方法中每个判断的用途,只是验证SpringEvent的底层实现机理。
参考资料: