JAVA注解式编程进阶

1,370 阅读15分钟

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:

提供信息给编译器: 编译器可以利用注解来探测错误和警告信息

编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。

运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

值得注意的是,注解不是代码本身的一部分。

一、Annotation架构

Java Annotation是JDK5.0引入的一种注释机制,先看看Annotation的架构图: 从中,我们可以看出:

(01) 1个Annotation 和 1个RetentionPolicy关联。 可以理解为:每1个Annotation对象,都会有唯一的RetentionPolicy属性。

(02) 1个Annotation 和 1~n个ElementType关联。 可以理解为:对于每1个Annotation对象,可以有若干个ElementType属性。

(03) Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override等等。 Annotation 的每一个实现类,都“和1个RetentionPolicy关联”并且“和1~n个ElementType关联”。

二、注解的使用

1、注解的定义

注解通过 @interface 关键字进行定义。

public @interface TestAnnotation {
}

它的形式跟接口很类似,不过前面多了一个 @ 符号。上面的代码就创建了一个名字为 TestAnnotaion 的注解。

2、注解的应用

@TestAnnotation
public class Test {
}

创建一个类 Test,然后在类定义的地方加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了, 可以简单理解为将 TestAnnotation 这张标签贴到 Test 这个类上面。不过,要想注解能够正常工作,还需要介绍一下一个新的概念那就是元注解。

3、元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。 如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

@Retention

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。它的取值如下:

RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

我们可以这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。

@Documented

顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Target

Target 是目标的意思,@Target 指定了注解运用的地方。

你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。

类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值

ElementType.ANNOTATION_TYPE 可以给一个注解进行注解

ElementType.CONSTRUCTOR 可以给构造方法进行注解

ElementType.FIELD 可以给属性进行注解

ElementType.LOCAL_VARIABLE 可以给局部变量进行注解

ElementType.METHOD 可以给方法进行注解

ElementType.PACKAGE 可以给一个包进行注解

ElementType.PARAMETER 可以给一个方法内的参数进行注解

ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。 说的比较抽象。代码来解释。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

4、注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    int id();
    String msg();
}

上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。

赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。

@TestAnnotation(id=3,msg="hello annotation")
public class Test {
}

需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。 注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    public int id() default -1;
    public String msg() default "Hi";
}

TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。 它可以这样应用。

@TestAnnotation()
public class Test {}

因为有默认值,所以无需要再在 @TestAnnotation 后面的括号里面进行赋值了,这一步可以省略。 另外,还有一种情况。如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。

public @interface Check {
    String value();
}

上面代码中,Check 这个注解只有 value 这个属性。所以可以这样应用。

@Check("hi")
int a;

这和下面的效果是一样的

@Check(value="hi")
int a;

最后,还需要注意的一种情况是一个注解没有任何属性。比如

public @interface Perform {}

那么在应用这个注解的时候,括号都可以省略。

@Perform
public void testMethod(){}

5、注解的提取

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如


@TestAnnotation()
public class Test {
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}

程序的运行结果是:

id:-1
msg:

这个正是 TestAnnotation 中 id 和 msg 的默认值。

上面的例子中,只是检阅出了注解在类上的注解,其实属性、方法上的注解照样是可以的。同样还是要假手于反射。

@TestAnnotation(msg="hello")
public class Test {
    @Check(value="hi")
    int a;
    @Perform
    public void testMethod(){}
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

它们的结果如下:

id:-1
msg:hello
check value:hi
method testMethod annotation:Perform

需要注意的是,如果一个注解要在运行时被成功提取,那么 @Retention(RetentionPolicy.RUNTIME) 是必须的。注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。

三、 JAVA自带的注解

@Deprecated -- @Deprecated 所标注内容,不再被建议使用。

@Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。

@Documented -- @Documented 所标注内容,可以出现在javadoc中。

@Inherited -- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。

@Retention -- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。

@Target -- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。

@SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。

四、注解的组成部分

java注解的组成中,有3个非常重要的主干类。它们分别是:

(01) Annotation.java

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

(02) ElementType.java


package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}

(03) RetentionPolicy.java


package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

说明:

(01) Annotation 就是个接口

  “每1个Annotation” 都与 “1个RetentionPolicy”关联,并且与 “1~n个ElementType”关联。
  可以通俗的理解为:每1个Annotation对象,都会有唯一的RetentionPolicy属性;
  至于ElementType属性,则有1~n个。

(02) ElementType 是Enum枚举类型,它用来指定Annotation的类型。

      “每1个Annotation” 都与 “1~n个ElementType”关联。
      当Annotation与某个ElementType关联时,就意味着:Annotation有了某种用途。
      例如,若一个Annotation对象是METHOD类型,则该Annotation只能用来修饰方法。

(03) RetentionPolicy 是Enum枚举类型,它用来指定Annotation的策略。

通俗点说,就是不同RetentionPolicy类型的Annotation的作用域不同。
      “每1个Annotation” 都与 “1个RetentionPolicy”关联。
      a) 若Annotation的类型为 SOURCE,则意味着:Annotation仅存在于编译器处理期间,
      编译器处理完之后,该Annotation就没用了。
          例如,“ @Override ”标志就是一个Annotation。当它修饰一个方法的时候,
          就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,
          “@Override”就没有任何作用了。
      b) 若Annotation的类型为 CLASS,则意味着:编译器将Annotation存储于类对应的.class文件中,
     		它是Annotation的默认行为。
      c) 若Annotation的类型为 RUNTIME,则意味着:编译器将Annotation存储于class文件中,
      		并且可由JVM读入。

五、注解的用途

Annotation 是一个辅助类,它在EventBus、Retrofit等业界流行的开源框架中被广泛使用。

1 编译检查

Annotation具有“让编译器进行编译检查的作用”。

例如,@SuppressWarnings, @Deprecated和@Override都具有编译检查作用。 若某个方法被 @Override的 标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被@Override标示,但父类中却没有“被@Override标注”的同名方法,则编译器会报错。示例如下:

ublic class OverrideTest {

    /**
     * toString() 在java.lang.Object中定义;
     * 因此,这里用 @Override 标注是对的。
     */
    @Override
    public String toString(){
        return "Override toString";
    }

    /**
     * getString() 没有在OverrideTest的任何父类中定义;
     * 但是,这里却用 @Override 标注,因此会产生编译错误!
     */
    @Override
    public String getString(){
        return "get toString";
    }
   
    public static void main(String[] args) {
    }
}

2 在反射中使用 Annotation

在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。

这也意味着,我们可以在反射中解析并使用 Annotation。

六、EventBus中的注解

EventBus 是一款在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开,简化了组件之间的通信,使用简单、效率高、体积小!下边是官方的 EventBus 原理图:

EventBus 的用法可以参考官网,这里不做过多的说明。本文主要是从 EventBus 使用的方式入手,来分析 EventBus 背后注解的实现原理,以下内容基于eventbus:3.1.1版本,以Subscribe注解为例:

一、Subscribe注解

EventBus3.0 开始用Subscribe注解配置事件订阅方法,不再使用方法名了,例如:

@Subscribe
public void handleEvent(String event) {
    // do something
}

其中事件类型可以是 Java 中已有的类型或者我们自定义的类型。 具体看下Subscribe注解的实现:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // 指定事件订阅方法的线程模式,即在那个线程执行事件订阅方法处理事件,默认为POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
    // 是否支持粘性事件,默认为false
    boolean sticky() default false;
    // 指定事件订阅方法的优先级,默认为0,如果多个事件订阅方法可以接收相同事件的,则优先级高的先接收到事件
    int priority() default 0;
}

所以在使用Subscribe注解时可以根据需求指定threadMode、sticky、priority三个属性。

其中threadMode属性有如下几个可选值:

ThreadMode.POSTING,默认的线程模式,在那个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。

ThreadMode.MAIN,如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。

ThreadMode.MAIN_ORDERED,无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。

ThreadMode.BACKGROUND,如果在主线程发送事件,则先将事件入队列,然后通过线程池依次处理事件;如果在子线程发送事件,则直接在发送事件的线程处理事件。

ThreadMode.ASYNC,无论在那个线程发送事件,都将事件入队列,然后通过线程池处理。

二、注册事件订阅方法

注册事件的方式如下:

EventBus.getDefault().register(this);
其中getDefault()是一个单例方法,保证当前只有一个EventBus实例:

public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

继续看new EventBus()做了些什么:

public EventBus() {
        this(DEFAULT_BUILDER);
    }

在这里又调用了EventBus的另一个构造函数来完成它相关属性的初始化:

EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
}

DEFAULT_BUILDER就是一个默认的EventBusBuilder:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); 如果有需要的话,我们也可以通过配置EventBusBuilder来更改EventBus的属性,例如用如下方式注册事件:

EventBus.builder()
        .eventInheritance(false)
        .logSubscriberExceptions(false)
        .build()
        .register(this);

有了EventBus的实例就可以进行注册了:

public void register(Object subscriber) {
        // 得到当前要注册类的Class对象
        Class<?> subscriberClass = subscriber.getClass();
        // 根据Class查找当前类中订阅了事件的方法集合,即使用了Subscribe注解、有public修饰符、一个参数的方法
        // SubscriberMethod类主要封装了符合条件方法的相关信息:
        // Method对象、线程模式、事件类型、优先级、是否是粘性事等
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            // 循环遍历订阅了事件的方法集合,以完成注册
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可以看到register()方法主要分为查找和注册两部分,首先来看查找的过程,从findSubscriberMethods()开始:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // METHOD_CACHE是一个ConcurrentHashMap,直接保存了subscriberClass和对应SubscriberMethod的集合,以提高注册效率,赋值重复查找。
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // 由于使用了默认的EventBusBuilder,则ignoreGeneratedIndex属性默认为false,即是否忽略注解生成器
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        // 如果对应类中没有符合条件的方法,则抛出异常
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            // 保存查找到的订阅事件的方法
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

findSubscriberMethods()流程很清晰,即先从缓存中查找,如果找到则直接返回,否则去做下一步的查找过程,然后缓存查找到的集合,根据上边的注释可知findUsingInfo()方法会被调用:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        // 初始状态下findState.clazz就是subscriberClass
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            // 条件不成立
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                // 通过反射查找订阅事件的方法
                findUsingReflectionInSingleClass(findState);
            }
            // 修改findState.clazz为subscriberClass的父类Class,即需要遍历父类
            findState.moveToSuperclass();
        }
        // 查找到的方法保存在了FindState实例的subscriberMethods集合中。
        // 使用subscriberMethods构建一个新的List<SubscriberMethod>
        // 释放掉findState
        return getMethodsAndRelease(findState);
    }

findUsingInfo()方法会在当前要注册的类以及其父类中查找订阅事件的方法,这里出现了一个FindState类,它是SubscriberMethodFinder的内部类,用来辅助查找订阅事件的方法,具体的查找过程在findUsingReflectionInSingleClass()方法,它主要通过反射查找订阅事件的方法:

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        // 循环遍历当前类的方法,筛选出符合条件的
        for (Method method : methods) {
            // 获得方法的修饰符
            int modifiers = method.getModifiers();
            // 如果是public类型,但非abstract、static等
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 获得当前方法所有参数的类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 如果当前方法只有一个参数
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // 如果当前方法使用了Subscribe注解
                    if (subscribeAnnotation != null) {
                        // 得到该参数的类型
                        Class<?> eventType = parameterTypes[0];
                        // checkAdd()方法用来判断FindState的anyMethodByEventType map是否已经添加过以当前eventType为key的键值对,没添加过则返回true
                        if (findState.checkAdd(method, eventType)) {
                             // 得到Subscribe注解的threadMode属性值,即线程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 创建一个SubscriberMethod对象,并添加到subscriberMethods集合
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

到此register()方法中findSubscriberMethods()流程就分析完了,我们已经找到了当前注册类及其父类中订阅事件的方法的集合。接下来分析具体的注册流程,即register()中的subscribe()方法:


private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 得到当前订阅了事件的方法的参数类型
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription类保存了要注册的类对象以及当前的subscriberMethod
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // subscriptionsByEventType是一个HashMap,保存了以eventType为key,Subscription对象集合为value的键值对
        // 先查找subscriptionsByEventType是否存在以当前eventType为key的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        // 如果不存在,则创建一个subscriptions,并保存到subscriptionsByEventType
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        // 添加上边创建的newSubscription对象到subscriptions中
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        // typesBySubscribere也是一个HashMap,保存了以当前要注册类的对象为key,注册类中订阅事件的方法的参数类型的集合为value的键值对
        // 查找是否存在对应的参数类型集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        // 不存在则创建一个subscribedEvents,并保存到typesBySubscriber
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        // 保存当前订阅了事件的方法的参数类型
        subscribedEvents.add(eventType);
        // 粘性事件相关的,后边具体分析
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

这就是注册的核心流程,所以subscribe()方法主要是得到了subscriptionsByEventType、typesBySubscriber两个 HashMap。我们在发送事件的时候要用到subscriptionsByEventType,完成事件的处理。当取消 EventBus 注册的时候要用到typesBySubscriber、subscriptionsByEventType,完成相关资源的释放。在findUsingReflectionInSingleClass()方法中,它主要是通过反射来查找订阅事件的方法。