Guava學習筆記:EventBus


  EventBus是Guava的事件處理機制,是設計模式中的觀察者模式(生產/消費者編程模型)的優雅實現。對於事件監聽和發布訂閱模式,EventBus是一個非常優雅和簡單解決方案,我們不用創建復雜的類和接口層次結構。

  Observer模式是比較常用的設計模式之一,雖然有時候在具體代碼里,它不一定叫這個名字,比如改頭換面叫個Listener,但模式就是這個模式。手工實現一個Observer也不是多復雜的一件事,只是因為這個設計模式實在太常用了,Java就把它放到了JDK里面:Observable和Observer,從JDK 1.0里,它們就一直在那里。從某種程度上說,它簡化了Observer模式的開發,至少我們不用再手工維護自己的Observer列表了。不過,如前所述,JDK里的Observer從1.0就在那里了,直到Java 7,它都沒有什么改變,就連通知的參數還是Object類型。要知道,Java 5就已經泛型了。Java 5是一次大規模的語法調整,許多程序庫從那開始重新設計了API,使其更簡潔易用。當然,那些不做應對的程序庫,多半也就過時了。這也就是這里要討論知識更新的原因所在。今天,對於普通的應用,如果要使用Observer模式該如何做呢?答案是Guava的EventBus。

  EventBus基本用法:

  使用Guava之后, 如果要訂閱消息, 就不用再繼承指定的接口, 只需要在指定的方法上加上@Subscribe注解即可。代碼如下:

  消息封裝類:

public class TestEvent {
    private final int message;
    public TestEvent(int message) {        
        this.message = message;
        System.out.println("event message:"+message);
    }
    public int getMessage() {
        return message;
    }
}

  消息接受類:

public class EventListener {
    public int lastMessage = 0;

    @Subscribe
    public void listen(TestEvent event) {
        lastMessage = event.getMessage();
        System.out.println("Message:"+lastMessage);
    }

    public int getLastMessage() {      
        return lastMessage;
    }
}

  測試類及輸出結果:

public class TestEventBus {
    @Test
    public void testReceiveEvent() throws Exception {

        EventBus eventBus = new EventBus("test");
        EventListener listener = new EventListener();

        eventBus.register(listener);

        eventBus.post(new TestEvent(200));
        eventBus.post(new TestEvent(300));
        eventBus.post(new TestEvent(400));

        System.out.println("LastMessage:"+listener.getLastMessage());
        ;
    }
}

//輸出信息
event message:200
Message:200
event message:300
Message:300
event message:400
Message:400
LastMessage:400

   MultiListener的使用:

  只需要在要訂閱消息的方法上加上@Subscribe注解即可實現對多個消息的訂閱,代碼如下:

public class MultipleListener {
    public Integer lastInteger;  
    public Long lastLong;  
   
    @Subscribe  
    public void listenInteger(Integer event) {  
        lastInteger = event; 
        System.out.println("event Integer:"+lastInteger);
    }  
   
    @Subscribe  
    public void listenLong(Long event) {  
        lastLong = event; 
        System.out.println("event Long:"+lastLong);
    }  
   
    public Integer getLastInteger() {  
        return lastInteger;  
    }  
   
    public Long getLastLong() {  
        return lastLong;  
    }  
}

  測試類:

public class TestMultipleEvents {
    @Test  
    public void testMultipleEvents() throws Exception {  
       
        EventBus eventBus = new EventBus("test");  
        MultipleListener multiListener = new MultipleListener();  
       
        eventBus.register(multiListener);  
       
        eventBus.post(new Integer(100));
        eventBus.post(new Integer(200));  
        eventBus.post(new Integer(300));  
        eventBus.post(new Long(800)); 
        eventBus.post(new Long(800990));  
        eventBus.post(new Long(800882934));  
       
        System.out.println("LastInteger:"+multiListener.getLastInteger());
        System.out.println("LastLong:"+multiListener.getLastLong());
    }   
}

//輸出信息
event Integer:100
event Integer:200
event Integer:300
event Long:800
event Long:800990
event Long:800882934
LastInteger:300
LastLong:800882934

  Dead Event:

  如果EventBus發送的消息都不是訂閱者關心的稱之為Dead Event。實例如下:

public class DeadEventListener {
    boolean notDelivered = false;  
       
    @Subscribe  
    public void listen(DeadEvent event) {  
        
        notDelivered = true;  
    }  
   
    public boolean isNotDelivered() {  
        return notDelivered;  
    }  
}

  測試類:

public class TestDeadEventListeners {
    @Test  
    public void testDeadEventListeners() throws Exception {  
       
        EventBus eventBus = new EventBus("test");               
        DeadEventListener deadEventListener = new DeadEventListener();  
        eventBus.register(deadEventListener);  

        eventBus.post(new TestEvent(200));         
        eventBus.post(new TestEvent(300));        
       
        System.out.println("deadEvent:"+deadEventListener.isNotDelivered());

    }  
}

//輸出信息
event message:200
event message:300
deadEvent:true

  說明:如果沒有消息訂閱者監聽消息, EventBus將發送DeadEvent消息,這時我們可以通過log的方式來記錄這種狀態。

  Event的繼承:

  如果Listener A監聽Event A, 而Event A有一個子類Event B, 此時Listener A將同時接收Event A和B消息,實例如下:

  Listener 類:

public class NumberListener {  
       
    private Number lastMessage;  
   
    @Subscribe  
    public void listen(Number integer) {  
        lastMessage = integer; 
        System.out.println("Message:"+lastMessage);
    }  
   
    public Number getLastMessage() {  
        return lastMessage;  
    }  
}  

public class IntegerListener {  
       
    private Integer lastMessage;  
   
    @Subscribe  
    public void listen(Integer integer) {  
        lastMessage = integer; 
        System.out.println("Message:"+lastMessage);
    }  
   
    public Integer getLastMessage() {  
        return lastMessage;  
    }  
}  

  測試類:

public class TestEventsFromSubclass {
    @Test  
    public void testEventsFromSubclass() throws Exception {  
       
        EventBus eventBus = new EventBus("test");  
        IntegerListener integerListener = new IntegerListener();  
        NumberListener numberListener = new NumberListener();  
        eventBus.register(integerListener);  
        eventBus.register(numberListener);  
       
        eventBus.post(new Integer(100));  
       
        System.out.println("integerListener message:"+integerListener.getLastMessage());
        System.out.println("numberListener message:"+numberListener.getLastMessage());
              
        eventBus.post(new Long(200L));  
       
        System.out.println("integerListener message:"+integerListener.getLastMessage());
        System.out.println("numberListener message:"+numberListener.getLastMessage());        
    }  
}

//輸出類
Message:100
Message:100
integerListener message:100
numberListener message:100
Message:200
integerListener message:100
numberListener message:200

  說明:在這個方法中,我們看到第一個事件(新的整數(100))是收到兩個聽眾,但第二個(新長(200 l))只能到達NumberListener作為整數一不是創建這種類型的事件。可以使用此功能來創建更通用的監聽器監聽一個廣泛的事件和更詳細的具體的特殊的事件。

   一個綜合實例:

public class UserThread extends Thread {
    private Socket connection;
    private EventBus channel;
    private BufferedReader in;
    private PrintWriter out;

    public UserThread(Socket connection, EventBus channel) {
        this.connection = connection;
        this.channel = channel;
        try {
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            out = new PrintWriter(connection.getOutputStream(), true);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    @Subscribe
    public void recieveMessage(String message) {
        if (out != null) {
            out.println(message);
            System.out.println("recieveMessage:"+message);
        }
    }

    @Override
    public void run() {
        try {
            String input;
            while ((input = in.readLine()) != null) {
                channel.post(input);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //reached eof
        channel.unregister(this);
        try {
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        in = null;
        out = null;
    }
}
mport java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.google.common.eventbus.EventBus;

public class EventBusChat {
    public static void main(String[] args) {
        EventBus channel = new EventBus();
        ServerSocket socket;
        try {
            socket = new ServerSocket(4444);
            while (true) {
                Socket connection = socket.accept();
                UserThread newUser = new UserThread(connection, channel);
                channel.register(newUser);
                newUser.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  說明:用telnet命令登錄:telnet 127.0.0.1 4444 ,如果你連接多個實例你會看到任何消息發送被傳送到其他實例。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM