Guava之EventBus使用總結
來自: http://www.cnblogs.com/jack2013/p/5162838.html
1.將EventBus封裝為單例模式使用
1 package priv.jack.demo.listener;
2
3 import java.util.Map;
4
5 import com.google.common.collect.Maps;
6 import com.google.common.eventbus.EventBus; 8
9 /**
10 * 事件總線工廠
11 * 將EventBus封裝為單例模式使用
12 * @author Jack
13 *
14 */
15 public class EventBusFactory {
16
17 private volatile static EventBusFactory INSTANCE ;
18
19 /**
20 * 保存已經注冊的監聽器,防止監聽器重復注冊
21 */
22 private Map<String, Class<? extends EventListener>> registerListenerContainers = Maps.newConcurrentMap() ;
23
24 private EventBusFactory() {}
25
26 public static EventBusFactory build() {
27 if(INSTANCE == null) {
28 synchronized (EventBusFactory.class) {
29 if(INSTANCE == null) {
30 INSTANCE = new EventBusFactory() ;
31 }
32 }
33 }
34 return INSTANCE ;
35 }
36
37 private final EventBus EVENTBUS = new EventBus() ;
38
39 /**
40 * 事件轉發
41 * @param event
42 */
43 public void postsEvent(SystemEvent event) {
44 EVENTBUS.post(event) ;
45 }
46
47 /**
48 * 監聽器注冊
49 * @param clazz
50 */
51 public void register(Class<? extends EventListener> clazz) {
52 String clazzName = clazz.getSimpleName() ;
53 if(registerListenerContainers.containsKey(clazzName)) {
54 return ;
55 }
56 try {
57 registerListenerContainers.put(clazzName, clazz) ;
58 Object obj = registerListenerContainers.get(clazzName).newInstance();
59 EVENTBUS.register(obj) ;
60 } catch (Exception e) {
61 e.printStackTrace();
62 }
63 }
64 }
封裝之后,將EventBus的post和register也進行封裝
2.封裝SystemEvent作為事件的頂層父類,為了使EventBusFactory的postEvent更通用
package priv.jack.demo.listener.event;/**
- 系統事件父類
@author Jack / public class SystemEvent {
public static final String MEMBER_DELETE_EVENT = "memberDelEvt" ; public static final String TEST_EVENT = "testEvt" ;
private String eventName;
public SystemEvent() {
}
public SystemEvent(String eventName) {
this.eventName = eventName;}
@Override public String toString() {
return "SystemEvent [eventName=" + eventName + "]";}
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
}</pre>
3.編寫事件子類
package priv.jack.demo.listener.event;public class TestEvent extends SystemEvent {
private String testField; public String getTestField() { return testField; } public void setTestField(String testField) { this.testField = testField; } public TestEvent() {} public TestEvent(String eventName, String testField) { super(eventName) ; this.testField = testField ; }}
package priv.jack.demo.listener.event;
import java.util.List;
/**
- 成員刪除事件
- 該事件觸發畫面合成修改操作
@author Jack / public class MemberDeletedEvent extends SystemEvent{
private String confCreatorToken;
private List<String> e164ListToOper ;
private int cmd;
public MemberDeletedEvent() { }
public MemberDeletedEvent(String eventName) {
super(eventName);}
public MemberDeletedEvent(String eventName, String confCreatorToken, List<String> e164ListToOper, int cmd) {
this(eventName) ; this.confCreatorToken = confCreatorToken ; this.e164ListToOper = e164ListToOper ; this.cmd = cmd ;}
@Override public String toString() {
return "MemberDeletedEvent [confCreatorToken=" + confCreatorToken + ", e164ListToOper=" + e164ListToOper + ", cmd=" + cmd + "]";}
public String getConfCreatorToken() {
return confCreatorToken;}
public void setConfCreatorToken(String confCreatorToken) {
this.confCreatorToken = confCreatorToken;}
public List<String> getE164ListToOper() {
return e164ListToOper;}
public void setE164ListToOper(List<String> e164ListToOper) {
this.e164ListToOper = e164ListToOper;}
public int getCmd() {
return cmd;}
public void setCmd(int cmd) {
this.cmd = cmd;}
}</pre>
4.編寫Listener接口類
public interface EventListener {
public void action(SystemEvent event) ;
}</pre>
5.編寫Listener實現類
/**
- 測試事件監聽類
@author Jack / public class TestEventListener implements EventListener{
private Logger logger = LoggerFactory.getLogger("birdie") ;
@Override @Subscribe @AllowConcurrentEvents public void action(SystemEvent event) {
logger.info(String.format("[TestEventListener ] action, listener=%s event=%s", this.toString(), event.toString())); TestEvent subEvent = (TestEvent) event ; invoke(subEvent) ;}
public void invoke(TestEvent testEvent) {
logger.info(String.format("[TestEventListener ] action testEvent=%s", testEvent.toString()));}
}
/**
- 成員刪除事件監聽器, 支持并發操作
- @see MemberDeletedEvent
- @see birdie-web下的EventBusTest
@author Jack / public class MemberDeletedEventListener implements EventListener{
private Logger logger = LoggerFactory.getLogger("birdie") ;
@Override @Subscribe @AllowConcurrentEvents public void action(SystemEvent event) {
logger.info(String.format("[MemberDeletedEventListener ] action event=%s", event.toString())); MemberDeletedEvent subEvent = (MemberDeletedEvent) event ; invoke(subEvent) ;}
public void invoke(MemberDeletedEvent memberDeletedEvent) {
if(MtsOper.MT_DELETE.getOperCode() == memberDeletedEvent.getCmd()) { //...業務代碼 }}
}</pre>
6.單元測試
public class EventBusTest {
//測試重復注冊監聽器
@Test
public void testRepeatRegister() {
final EventBus bus = new EventBus() ;
bus.register(new TestEventListener());
bus.register(new TestEventListener());
TestEvent event = new TestEvent(SystemEvent.TEST_EVENT, "123") ;
bus.post(event);
}
//測試并發操作
//@Test
public void testConcurrcy() {
List<String> list = Lists.newArrayList("001#01") ;
MemberDeletedEvent event = new MemberDeletedEvent(SystemEvent.MEMBER_DELETE_EVENT, "123", list , 1) ;
for(int i=0 ; i < 100 ; i++) {
final int cnt = i ;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第"+ cnt+ "次");
EventBusFactory.build().register(MemberDeletedEventListener.class) ;
}
}) ;
t.start();
}
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
EventBusFactory.build().postsEvent(event);
}
//測試繼承結構下的事件分發
@Test
public void testInherit() {
TestEvent event = new TestEvent(SystemEvent.TEST_EVENT, "123") ;
EventBusFactory.build().register(TestEventListener.class) ;
EventBusFactory.build().postsEvent(event);
}
}</pre>
總結
1.支持類的繼承結構下的事件分發,但子類和父類都能收到事件。
2.@AllowConcurrentEvents 進行并發設置,經過簡單測試是OK的
相關問題
1.如何能夠很優雅的進行對監聽器進行集中且動態的注冊,讓其可擴展性更好
目前上面demo的代碼是在post之前進行register,并在register中進行重復注冊的屏蔽, 如果新增監聽器的話需要修改代碼,可擴展性差
2.請大牛對代碼進行評價
</div>