动手做一个EventBus(二):实现一个简单的EventBus

#EventBus(二)   上一章讲解了Guava的EventBus的使用,这一章会开始模拟EventBus的源码来实现一个简单EventBus。首先要了解事件总线的几个功能模块。demo在github上

##1.Subscriber   Subscriber模块要做的事情就是根据接收到的事件,来执行相应的操作。这里通过Java的反射来实现,通过调用Method的invoke方法来执行一个类中的某个方法。   
###2.1 订阅者 Subscriber.java

//订阅者
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注解,使用注解的好处是被订阅的消息不需要特定地遵守一些约定,只要标注上这个注解,那么就代表订阅这个类型的消息。

/**
 * 标识订阅者
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public [@interface](https://my.oschina.net/u/996807) Subscribe {

}

##2.EventBus   EventBus模块要做的就是

  * 维护一个根据事件找到对应的订阅者的路由

  * 维护一个存储消息的队列

###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.java

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 测试一下

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的一些博客,之前的公司用了这两个框架。(:зゝ∠)

转载于:https://my.oschina.net/u/3421586/blog/894460

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值