動手作一個EventBus(二):實現一個簡單的EventBus

#EventBus(二)   上一章講解了Guava的EventBus的使用,這一章會開始模擬EventBus的源碼來實現一個簡單EventBus。首先要了解事件總線的幾個功能模塊。demo在github上java

##1.Subscriber   Subscriber模塊要作的事情就是根據接收到的事件,來執行相應的操做。這裏經過Java的反射來實現,經過調用Method的invoke方法來執行一個類中的某個方法。   
###2.1 訂閱者 Subscriber.javagit

//訂閱者
public class Subscriber {

	//訂閱事件的類
    private final Object target;
	
	//訂閱事件的方法
    private final Method method;

    public Subscriber(Object target, Method method) {
        if (target == null || method == null) {
            throw new IllegalArgumentException("Target object and method must not be null");
        }
        this.target = target;
        this.method = method;
        //值爲 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值爲 false 則指示反射的對象應該實施 Java 語言訪問檢查,能夠提升性能
        this.method.setAccessible(true);
    }

    public void invoke(Object parameter) throws Exception {
        method.invoke(target, parameter);
    }
}

###2.2 訂閱者的註解 Subscribe.java   這裏要模擬EventBus中的Subscribe註解,使用註解的好處是被訂閱的消息不須要特定地遵照一些約定,只要標註上這個註解,那麼就表明訂閱這個類型的消息。github

/**
 * 標識訂閱者
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public [@interface](https://my.oschina.net/u/996807) Subscribe {

}

##2.EventBus   EventBus模塊要作的就是框架

  * 維護一個根據事件找到對應的訂閱者的路由ide

  * 維護一個存儲消息的隊列post

###2.3 事件總線 EventBus.java性能

//事件總線
public class EventBus {

    //維護key是事件的class,value是這個事件全部的訂閱者的映射關係
    private Map<Class<?>, List<Subscriber>> register;
	
	//存放事件的阻塞隊列
    private BlockingQueue<Object> queue;

    public EventBus() {
        this.register = new HashMap<>();
        queue = new LinkedBlockingDeque<>();
        new Thread(new Runnable() {
            [@Override](https://my.oschina.net/u/1162528)
            public void run() {
            	//事件的消費者
                Consumer consumer = new Consumer(queue, register);
                consumer.start();
            }
        }).start();
    }

    /**
     * 把類的信息及其subscriber註冊到map中去
     */
    public void register(Object listener) {
        if (listener == null) {
            return;
        }
        Class<?> clazz = listener.getClass();
        //找到當前類的全部帶有Subscribe註解的方法
        for (Method method : getAnnotatedMethod(clazz)) {
            pushToResisterMap(listener, method);
        }
    }

    private void pushToResisterMap(Object listener, Method method) {
        if (listener == null || method == null) {
            return;
        }
        //獲取方法的第一個參數
        Class<?> eventParamter = method.getParameterTypes()[0];
        List<Subscriber> subscriberList;
        if (register.containsKey(eventParamter)) {
            subscriberList = register.get(eventParamter);
            subscriberList.add(new Subscriber(listener, method));
        } else {
            subscriberList = new ArrayList<>();
            subscriberList.add(new Subscriber(listener, method));
            register.put(eventParamter, subscriberList);
        }
    }

    /**
     * 獲取類的全部帶有Subscribe註解的方法
     */
    private Set<Method> getAnnotatedMethod(Class<?> clazz) {
        Set<Method> annotatedMethods = new HashSet<>();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Subscribe annotation = method.getAnnotation(Subscribe.class);
            if (annotation != null && !method.isBridge()) {
                annotatedMethods.add(method);
            }
        }
        return annotatedMethods;
    }

    /**
     * 向阻塞隊列裏發送事件
     */
    public void post(Object event) {
        if (event == null) {
            return;
        }
        try {
            queue.put(event);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

##3.Consumer   消費者要作的就是不斷地從隊列中獲取新的事件,從EventBus獲取這個事件的全部訂閱者,而後讓訂閱者執行相應的方法便可    ###2.4 消費者 Consumer.java //消費者 public class Consumer {測試

private BlockingQueue<Object> queue;

    private Map<Class<?>, List<Subscriber>> register;

    public Consumer(BlockingQueue<Object> queue, Map<Class<?>, List<Subscriber>> register) {
        this.queue = queue;
        this.register = register;
    }

    public void start() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (true) {
            Object event;
            try {
                //從隊列裏取事件,若是沒有事件就阻塞住
                event = queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
                continue;
            }
            Class<?> clazz = event.getClass();
            if (!register.containsKey(clazz)) {
                System.out.println("Cannot found event's subscriber");
                continue;
            }
            //找到事件的subscriber,而後執行對應的事件
            List<Subscriber> subscriberList = register.get(clazz);
            for (Subscriber subscriber : subscriberList) {
                try {
                    subscriber.invoke(event);
                } catch (Exception e) {
                    System.out.println("Eventbus execute event failed , " + event + "/" + subscriber);
                }
            }
        }
    }
}

###2.5 訂閱事件的服務 EventService.javaui

public class EventService {

    public EventService(EventBus eventBus) {
        eventBus.register(this);
    }

    @Subscribe
    public void handleEvent(HelloEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("handleEvent received: " + event);
    }

    @Subscribe
    public void doHelloEvent(HelloEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("doHelloEvent received: " + event);
    }

    @Subscribe
    public void doGoodByEvent(GoodByEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("doGoodByEvent received: " + event.toString());
    }

    @Subscribe
    public void receiveGoodByEvent(GoodByEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("receiveGoodByEvent received: " + event.toString());
    }
}

class HelloEvent {

    private String greeting;

    private Date date;

    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "HelloEvent{" +
                "greeting='" + greeting + '\'' +
                ", date=" + date +
                '}';
    }
}

class GoodByEvent {

    private String saying;

    private Date date;

    public void setSaying(String saying) {
        this.saying = saying;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "GoodByEvent{" +
                "saying='" + saying + '\'' +
                ", date=" + date +
                '}';
    }
}

###2.6 測試一下this

public class UnitTest {

    private EventBus eventBus;

    private EventService eventService;

    @Before
    public void init() {
        eventBus = new EventBus();
        eventService = new EventService(eventBus);
    }

    private void postSeveralEvent() {
        HelloEvent helloEvent = new HelloEvent();
        helloEvent.setGreeting("你好啊");
        helloEvent.setDate(new Date());

        GoodByEvent goodByEvent = new GoodByEvent();
        goodByEvent.setSaying("再見啦");
        goodByEvent.setDate(new Date());

        eventBus.post(helloEvent);
        eventBus.post(goodByEvent);
    }

    @Test
    public void run() throws Exception {
        postSeveralEvent();
        while (Thread.activeCount() > 2) {

        }
    }
}

運行以上測試用例能夠看到,事件被成功的接收到並執行了

doHelloEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
handleEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
doGoodByEvent received: GoodByEvent{saying='再見啦', date=Sun May 07 21:13:11 CST 2017}
receiveGoodByEvent received: GoodByEvent{saying='再見啦', date=Sun May 07 21:13:11 CST 2017}

  這兩篇博客是以前在記錄在印象筆記上的筆記,最近有空整理了一下~等有空了還想寫一下關於Guice和Jersey的一些博客,以前的公司用了這兩個框架。(:зゝ∠)

相關文章
相關標籤/搜索