Github:https://github.com/yihonglei/daisy-framework
Java很多web框架源码都能看到很多事件监听的存在,主要解决的问题就是类似观察者模式的行为,
当一件事情发生的时候,其他的相关事情需要知道并处理。
事件监听三要素: 事件源,事件对象,事件监听器。
1、监听器需要注册,即注册到某个数据结构,map或list等等存储起来;
2、一件事情的发生源头,这件事情需要触发监听器,让监听器能感受到,监听器才能去处理业务;
3、调用每一个监听器,执行相关处理;
要理解监听器,需要理解几个问题,监听器存储在什么数据结构?监听器如何被触发?监听器如何处理业务?
上一个代码吧,要不然太蒙了!
demo基于spring boot环境。
Java自定义事件实现EventObject对象。
package com.jpeony.common.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.EventObject;
/**
* 自定义事件
*
* @author yihonglei
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class DaisyEvent extends EventObject {
DaisyEventEnum event;
Object[] args;
/**
* 事件对象
*
* @param daisyEventEnum 事件类型
* @param source 触发事件初始化对象
* @param args 其他参数
* @author yihonglei
*/
public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source, Object... args) {
super(source);
this.event = daisyEventEnum;
this.args = args;
}
/**
* 事件对象
*
* @param daisyEventEnum 事件
* @param source 触发事件初始化对象
* @author yihonglei
*/
public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source) {
this(daisyEventEnum, source, null);
}
}
DaisyEventEnum事件类型,args事件参数。
定义事件类型主要是对可以针对事件类型进行发布。
package com.jpeony.common.event;
/**
* 事件枚举
*
* @author yihonglei
*/
public enum DaisyEventEnum {
/**
* 初始化
*/
INIT_EVENT,
/**
* 测试事件
*/
TEST_EVENT;
}
事件监听器继承EventListener,定义事件发布的标准方法。
package com.jpeony.common.event;
import java.util.EventListener;
/**
* 自定义事件监听,Spring的ApplicationListener也是这么干的,自定义方便自己对事件进行管理。
* 注意,这里的事件是同步的,如果有需要,可以将事件提交到线程池异步化执行或者使用Spring的异步事件方式,
* 它也是提交到线程池实现异步化。
*
* @author yihonglei
*/
public interface DaisyEventListener extends EventListener {
/**
* 触发事件
*
* @param e 相关事件
*/
void fire(DaisyEvent e);
}
event1
package com.jpeony.core.service.event;
import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventListener;
import com.jpeony.common.event.DaisyEventManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 实现daisyEventListener接口,在构造方法添加你的事件即可!
*
* @author yihonglei
*/
@Slf4j
@Component
public class Test1EventListener implements DaisyEventListener {
public Test1EventListener() {
DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
}
@Override
public void fire(DaisyEvent e) {
try {
String userName = (String) e.getSource();
log.info("【Test1】对发布事件,事件触发,事件参数测试!userName={}", userName);
} catch (Exception ex) {
log.error("出异常了!", ex);
}
}
}
event2
package com.jpeony.core.service.event;
import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventListener;
import com.jpeony.common.event.DaisyEventManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 实现daisyEventListener接口,在构造方法添加你的事件即可!
*
* @author yihonglei
*/
@Slf4j
@Component
public class Test2EventListener implements DaisyEventListener {
public Test2EventListener() {
DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
}
@Override
public void fire(DaisyEvent e) {
try {
String userName = (String) e.getSource();
log.info("【Test2】对发布事件,事件触发,事件参数测试!userName={}", userName);
} catch (Exception ex) {
log.error("出异常了!", ex);
}
}
}
spring boot启动时注册到时间管理器DaisyEventManager(下面有源码)。
负责事件的注册,事件触发。
package com.jpeony.common.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.EnumMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 事件管理器
*
* @author yihonglei
*/
public class DaisyEventManager {
private static Logger logger = LoggerFactory.getLogger(DaisyEventManager.class);
private static EnumMap> eventListeners = new EnumMap<>(DaisyEventEnum.class);
public static void addListener(DaisyEventEnum event, DaisyEventListener el) {
CopyOnWriteArrayList listeners = eventListeners.get(event);
if (listeners == null) {
listeners = new CopyOnWriteArrayList<>();
eventListeners.put(event, listeners);
} else {
for (DaisyEventListener listener : listeners) {
if (listener.getClass() == el.getClass()) {
logger.info("more than one instances added, this class is {}", listener.getClass());
return;
}
}
}
listeners.add(el);
}
/**
* 移出指定事件的指定监听器
*
* @author yihonglei
*/
public static synchronized void removeListener(DaisyEventEnum event, DaisyEventListener el) {
CopyOnWriteArrayList listeners = eventListeners.get(event);
if (listeners != null) {
listeners.remove(el);
}
}
/**
* 清除指定事件的所有监听器
*
* @author yihonglei
*/
public static void purgeListeners(DaisyEventEnum event) {
CopyOnWriteArrayList listeners = eventListeners.get(event);
if (listeners != null) {
listeners.clear();
// 移除该事件
eventListeners.remove(event);
}
}
/**
* 触发事件
*
* @author yihonglei
*/
public static void fireEvent(DaisyEvent obj) {
CopyOnWriteArrayList listeners = eventListeners.get(obj.getEvent());
if (listeners == null) {
return;
}
for (DaisyEventListener listener : listeners) {
listener.fire(obj);
}
}
}
spring boot启动的时候,Test1EventListener和Test2EventListener通过构造器的事件管理器注册到EnumMap。
DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
EnumMap是一个事件类型对应多个listener的数据结构,Map的key是事件类型,value是监听的list集合。
package com.jpeony.test.service.event;
import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventManager;
import com.jpeony.test.BaseServletTest;
import org.junit.Test;
/**
* @author yihonglei
*/
public class EventTest extends BaseServletTest {
@Test
public void test() {
// 事件发布,支持任意参数,可以是一个值,也可以传入对象
String userName = "Tom";
DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));
}
}
发布TEST_EVENT事件,event1和event2监听并执行。
DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));
传入TEST_EVENT,事件管理器根据key为TEST_EVENT取出对应的监听器列表并循环执行。
1)监听事件继承EventObject;
2)监听器继承于EventLisneter,也可以不继承,这个接口只是一个标记接口,然后定义统一的事件发布方法;
3)弄一个事件管理器,不弄也行,我这里是为了方便,你可以在某个类里面搞一个Map或list存这些监听器,
数据结构选取可以根据自己的业务来,总之,要把这些监听器对象存起来;
4)事件执行就比较简单,就是根据事件类型取出相应的监听器,循环执行,完事!
最近无聊看了下tomcat源码,单独简单聊下tomcat里的事件监听怎么实现的。
你会发现,tomcat生命周期管理的自定义事件也只是继承了EventObject,data事件监听器处理需要的参数,
type就是事件类型。
刚才我自己实现监听器,标识接口用的Java的EventListener,这个是只是个标记接口,
表明跟我这个EventListener相关的都是要实现监听器,你别的不相关的别瞎搞,只是一个规范而已。
tomcat里面就只是自己定义了事件发布标准,监听器都走LifecycleListener接口标识。
数据结构
最后都是调用add方法,把监听都放在这个list里面。
这个地方也是通过type取到监听list,然后循环触发执行。
1、事件自定义,继承EventObject;
2、监听器定义,要统一标识接口和发布方法;
3、监听器注册,搞一个合适的数据结构把你的监听分类存起来;
4、事件触发,根据事件类型,从数据结构取出相应监听器,循环执行;