# EVENT事件监听
场景:开发需要做一个第三方组织机构同步集成功能,当系统内出现人员、部门、单位新增、修改、删除的时候,开发需要将这些信息同步给第三方,让第三方同步新增、修改、删除对应组织机构数据。
传统的做法是找到新增、删除、修改的源代码,在后面追加同步的代码,这种方式会侵入源码,耦合性极高。
CTP平台基于此问题,提供了一套事件分发和监听机制:组织机构开发在新增、修改、删除的代码位置封装推送一个Event对象,第三方集成只需监听这个Event对象,去实现数据同步即可。这种思路能降低代码耦合,提升可维护性。
# 实现原理
采用类似Observable+Observer模式,使用java.util.EventObject技术,并进行了简化开发。方案特点:注解驱动、无需注册监听、无需定义事件类型、异步分发、易扩展。
# 定义Event
下面以人员新增事件为例做Event事件开发演示。
标准产品侧,要触发事件则要先定义Event事件实例对象,实例如下,“人员新增”事件中存放了新增的人员对象V3xOrgMember:
public class AddMemberEvent extends Event {
private static final long serialVersionUID = -5719181273255663176L;
private V3xOrgMember member;
private boolean isBatch = false;;
public V3xOrgMember getMember() {
return member;
}
public void setMember(V3xOrgMember member) {
this.member = member;
}
public AddMemberEvent(Object source) {
super(source);
}
public boolean isBatch() {
return isBatch;
}
public void setBatch(boolean isBatch) {
this.isBatch = isBatch;
}
}
# 发布Event
标准产品侧,当执行人员新增逻辑的时候,就需要封装AddMemberEvent对象并通过fireEvent将事件发布出去:
public void addMembers(List<V3xOrgMember> members) throws BusinessException {
for (V3xOrgMember v3xOrgMember : members) {
try {
// 实例化Event对象
AddMemberEvent event = new AddMemberEvent(this);
// 传入必要的参数
event.setMember(v3xOrgMember);
// 分发Event事件,此步动作发送之后,外部即可监听
EventDispatcher.fireEvent(event);
} catch (Exception e) {
log.error("AddMemberEvent error:"+v3xOrgMember.getName(),e);
}
}
}
发布时机&发布类型:
# 立即触发事件,出现异常也不抛出,只记录日志
EventDispatcher#fireEvent
# 立即触发事件,出现异常时会抛出异常,供发出者捕获
EventDispatcher#fireEventWithException
# 事务提交成功后才触发,事务回滚则不触发
EventDispatcher#fireEventAfterCommit
# 监听Event
“定义Event”和“发布Event”是标准产品侧实现的代码,只要完成了事件的发布,标准产品侧就不需要再做别的事情。
下面就是需求方的任务,需求方需要在自己的业务类中(或者单独新增一个Listener)来监听Event事件,示例如下:
public class WeixinOrganizationListener {
private DingdingManager dingdingManager;
private FeishuManager feishuManager;
public void setDingdingManager(DingdingManager dingdingManager) {
this.dingdingManager = dingdingManager;
}
public void setFeishuManager(FeishuManager feishuManager) {
this.feishuManager = feishuManager;
}
@ListenEvent(event = AddMemberEvent.class, async = true)
public void addMember(AddMemberEvent event) {
V3xOrgMember member = event.getMember();
WeixinUtil.syncMember(member, "add");
dingdingManager.syncMemberDing(member, "add");
feishuManager.syncMember(member, "add");
}
@ListenEvent(event = UpdateMemberEvent.class, async = true)
public void updateMember(UpdateMemberEvent event) {
V3xOrgMember member = event.getMember();
WeixinUtil.syncMember(member, "update");
if (member.isValid()) {
dingdingManager.syncMemberDing(member, "update");
feishuManager.syncMember(member, "update");
} else {
dingdingManager.syncMemberDing(member, "del");
feishuManager.syncMember(member, "del");
}
}
}
用于事件监听的类需要通过插件化开发的方式注册到Spring容器中:
<bean id="weixinOrganizationListener" class="com.seeyon.apps.weixin.listener.WeixinOrganizationListener" />
关于注解@ListenEvent:其定义信息如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD/*, ElementType.TYPE */})
@Inherited
public @interface ListenEvent {
/**
* 监听的事件类型。
*
* @return 监听的事件类型。
*/
Class event();
/**
* 执行模式,同步或异步。为true时异步(asynchronous)执行,为false时同步(synchronization)执行。缺省为false。
* @return 执行模式。
*/
boolean async() default false;
/**
* 事件触发模式,如不指定,则立刻触发。
* <color>不推荐使用:如果需要立即执行,请将async=false</color>
* @return 事件触发模式。
*/
@Deprecated
EventTriggerMode mode() default EventTriggerMode.immediately;
/**
* <description>顺序(越小越在前面)</description>
*
* @return
* @author: ouyp
* @since: Seeyon V7.1
* @date: 2019年1月10日 下午4:15:25
*/
int order() default 0;
}
如无特殊需要,我们一概建议异步执行监听:设置async参数为true!
# 产品标准Event接口
详见SeeyonAPI Event事件部分:https://open.seeyoncloud.com/seeyonapi/8.2/event/
# 常见问题
1)Listener获取不到AppContext.currentUser()
用户掉线或离线状态、或者你当前是异步线程执行。
