读源码练内功(一):guava之eventbus

现在如今眼目下,开源程序库越来越多,程序员们很多时候都不需要自己造轮子,就可以找到称心如意的开源库进行使用。虽然我们在使用各种各样的开源代码时,并不需要知道这些代码是如何实现的。但是了解它们的实现方法,不仅可以提升我们自己本身的编程能力和编程技巧,同时也为我们学习某一特定的技术点提供了可以模仿的例子。


Guava简介

如同boost之于c++,guava也几乎成为了java编程中不可或缺的一部分。guava中涵盖了很多有意思的东西,比如在java中使用函数式编程;新的数据结构,如bimap等等。总之,guava让写java程序成为一件更美好的事情。guava中还有很多很多有意思的东西,可以去guava的官方网站上探个究竟。guava

本文试图通过分析guava的eventbus的源代码,学习如何在java中使用annotation编程。

EventBus简介

Publisher-Subscriber这种设计模式在GoF中早就详细的解释。也是一种最常用不过的设计模式。而EventBus则是对于Publisher和Subscriber的一种实现,如果你还在使用JDK中的Observer,则不妨看看TW大大的一片博客《你应该更新的java知识之observer》,使用EventBus替代Observer似乎成了一种必须。

EventBus使用方法

MessageScreen.java

public class MessageScreen {
    @Subscribe
    public void printMessage(String message) {
        System.out.println(message);
    }
}

调用代码:

        EventBus eventBus = new EventBus();
        eventBus.register(new MessageScreen());
        eventBus.post("Hello Screen");

显示结果:

     Hello Screen


代码解释:

调用eventBus中得register时,会向eventBus中注册一个Listener。这里的listener就是MessageScreen。

listener中被@Subscribe标示的方法为EventHandler,当eventBus使用post发布Event时,这个方法就会被调用。对应前面的例子,EventHandler是printMessage这个方法。Event是“Hello Screen”。

EventHandler中的参数类型为EventType,这里是String。当eventBus使用post时,会更具event的Type不同发送到相应的handler进行处理。

于是,一切看似神奇的事情都是发生在@Subscribe这个标记上。那Annotation究竟是干什么用得呢?怎么的一个标明了@Subscribe的方法就可以变成了一个subscriber了呢?


Annotation编程

Annotation是一种元数据,它提供了关于程序的信息,但是这个信息并不属于被Annotation标注的程序的本身的一部分。它不会影响被标注的程序的执行。

以上面的MessageListener为例,displayMessage这个方法被标注了@Subscribe,这个标注信息是在EventBus中的register中用到的,而不是在MessageListener中用到的。

Annotation编程可以分为大致两个部分,第一个部分是annotation定义,第二个部分是获取被annotation标记的代码,然后针对这部分代码进行一些特定的操作。下面就使用EventBus讲解这两个部分的工作是如何完成的。

定义Annotation:@Subscribe

Subscribe的定义

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {
}


@interface表示定义了一个annotation。@Retention和@Target是predefined annotation。

@Retention表示Subscribe这个Annotation是怎么被保存的。@Retention(RententionPolicy.RUNTIME)说明Subscribe这个annotation在运行时可以被使用。

@Target表示的是这个Subscribe这个annotation能被标注到哪里。@Target(ElementType.METHOD)说明Subscribe是用于标记方法的。对应一开始EventBus的例子,就是printMessage这个方法。


获取被Annotation标记的代码:EventBus中如何知道@Subscribe的方法的呢?

EventBus的代码:

    public void register(Object object) {
        Multimap<Class<?>, EventHandler> methodsInListener =
                finder.findAllHandlers(object);
        handlersByType.putAll(methodsInListener);
    }

在EventBus进行register时,会通过一个finder找到register的object中被标注了@Subscribe的方法。并且按照EventType进行分类,放在handlersByType里。这样当EventBus的post新的Event时,就可以根据EventType调用相应的EventHandler。

AnnotatedHandlerFinder的代码

     public Multimap<Class<?>, EventHandler> findAllHandlers(Object listener) {
         Multimap<Class<?>, EventHandler> methodsInListener = HashMultimap.create();
         Class<?> clazz = listener.getClass();
         for (Method method : getAnnotatedMethods(clazz)) {
             Class<?>[] parameterTypes = method.getParameterTypes();
             Class<?> eventType = parameterTypes[0];
             EventHandler handler = makeHandler(listener, method);
             methodsInListener.put(eventType, handler);
         }
         return methodsInListener;
     }

在findAllHandlers这个函数中,将首先调用getAnnotatedMethods得到listener中所有被标记了@Subscribe的方法。然后将listener和被标记了@Subscribe的方法本身放在一个叫EventHandler的数据结构中,同时记录了method的第一个也是唯一一个参数类型作为EventType作为post时按类型分发消息使用。接着再看是如何得到被@Subscribe标记的方法的:

    private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {

            for (Method method : clazz.getMethods()) {
                if (method.isAnnotationPresent(Subscribe.class)) {
                    ... ...// add to list
                }
            }
        }

getAnnotatedMethods首先获得了listener的所有方法。然后再遍历查询是否有方法是否被@Subscribe标示了:method.isAnnotationPresent(Subscribe.class)。如果被@Subscribe标示了,则添加到返回的list中。这其实也告诉了我们,这种方法的另一个好处,一个类中不再只能有一个update作为Observer的方法。而是可以有多个被@Subscribe标示的EventHandler,他们在同一个类时,就通过方法名和EventType进行区分。


当EventBus中的post被调用时,中间会有一系列的入队列,出队列的操作。最后调用,Eventhandler的handleEvent方法。

EventHandler.java

public void handleEvent(Object event) {
        method.invoke(target,new Object[]{ event});
        }

handleEvent中method是在register中被标记了@Subscribe的方法,第一个参数target就是在register中加入的listener,通过调用method的invoke方法。这样就调用了被@Subscribe的方法。


总结

1.Annotation编程在很多地方都有用到,比如大家都再熟悉不过的Junit4中的@Test,Spring中的@Component等等。所以,学习Annotation,说不定可以让我们写出这些使用方便的程序。

2.在EventBus的完整实现中,其实还包含了其他很多的技术,像多线程、cache以及Guava其他的一些功能。比如getAnnotatedMethods这个方法并不是真正的去查找Annotated Methods,真正的查找是getAnnotatedMethodsInternal这个方法,期间就使用了cache。这里想专注于Annotation本身的使用,就不展开讨论了。

3.在判断是否含有@Subscribe时,AnnotatedHandlerFinder实际上是检查了所有父类的方法。因为父类本身的Annotation是不会被子类继承的,如果子类重写了父类的方法。那么调用子类的isAnnotationPresent则会返回false。

4. 如何想要了解更多的关于annotation的细节知识,可以访问下oracle的官方文档:oracle annotation


  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值