注释处理器
为了开始写注释处理器,你首先应该熟悉javax.annotation.processing 和 javax.lang.model的包组。一般来说,你可以直接掠过执行处理器接口,进入抽象类javax.annotation.processing.AbstractProcessor。AbstractProcessor需要一些关于实现的信息,这些信息用来提供注释。例子中的EventListenerAnnotationProcessor代码声明如下所示:
24. @SupportedSourceVersion(SourceVersion.RELEASE_5)
25. @SupportedAnnotationTypes(EventListenerAnnotationProcessor.ANNOTATION_TYPE)
26. public class EventListenerAnnotationProcessor extends AbstractProcessor {
@SupportedSourceVersion告诉AbstractProcessor你只想要用java5或者更高版本写的源文件;而@SupportedAnnotationTypes告诉AbstractProcessor哪个注释是你感兴趣的(EventListener.class.getName()不会作为一个注释值起作用,因为编译器不能计算这种表达式的值)。
28. public static final String ANNOTATION_TYPE = "eventbus.EventListener";
为了简单起见,注释处理器被分成两个主要的类(EventListenerAnnotationProcessor 和EventDispatcherGenerator)以及一个通用工具类(ServiceRegistration)。为了便于编译器注释工具执行EventListenerAnnotationProcessor,你需要用一个服务文件来注册它(编译器也使用ServiceLoader)。
eventbus.processor.EventListenerAnnotationProcessor
服务注册文件(META-INF/services/javax.annotation.processing.Processor)是根据ServiceLoader一定能找到的接口来命名的。
EventListenerAnnotationProcessor.process()方法的第一个行动就是找到这轮编译中所有的@EventListener方法。
30. final Elements elements = processingEnv.getElementUtils();
31. final TypeElement annotation = elements.getTypeElement(ANNOTATION_TYPE);
32. final Set extends Element> methods =
33. roundEnv.getElementsAnnotatedWith(annotation);
Element对象很像编译器以及注释处理器的反射对象(reflection objects)。TypeElement就像是类,而ExecutableElement跟构造器或者方法类似。RoundEnvironment(代表本轮注释处理)将会返回到被@EventListener 注释的Element。
EventDispatcherGenerator
EventDispatcherGenerator是一个非常简单的代码生成器。你可能更喜欢用模板(比如FreeMarker 或者Velocity)来生成你的源代码,但是这个例子中的代码是用PrintWriter写的。每个代表@EventListener注释方法的ExecutableElementEvent被传递到DispatcherGenerator.generate,它可以给EventDispatcher写出源代码。
35. for(final Element m : methods) {
36. // ensure that the element is a method
37. if(m.getKind() == ElementKind.METHOD) {
38. final ExecutableElement method = (ExecutableElement)m;
39. results.add(generator.generate(method));
40. }
41. }
该EventDispatcherGenerator需要为每个方法产生一个Java源文件。一个注释处理器用ProcessingEnvironment提供的过滤目标来创建用于编写代码的源文件。
43. final JavaFileObject file = processingEnvironment.getFiler().createSourceFile(
44. className, // ie: com.mydomain.example.OnMessageDispatcher
45. method); // ie: com.mydomain.example.Listener.onMessage(MessageEvent)
在这个例子中,给定的过滤器ExecutableElement代表了已经注释的方法(createSourceFile中的第二个观点)。这会告诉环境你正在生成跟那个方法相关的源代码,虽然不是必须的,但是比较有用。然后代码用JavaFileObject来打开一个书写器,并开始生成源代码。
47. final Writer writer = file.openWriter();
48. final PrintWriter pw = new PrintWriter(writer);
49. pw.append("package ").append(packageName).println(';');
在@EventListener注释中为方法指定值,从而在调用注释方法之前产生一个if 语句,这个if语句可以过滤BusEventObjects。EventDispatcherGenerator把if 语句写进源代码,从而决定是否把事件对象分派到@EventListener方法中去。
51. public final class EventBus {
52. private static final EventDispatcher[] DISPATCHERS;
53. static {
54. final ServiceLoader loader =
55. ServiceLoader.load(EventDispatcher.class);
56. final List list = new ArrayList();
57. for(final EventDispatcher dispatcher : loader) {
58. list.add(dispatcher);
59. }
60. DISPATCHERS = list.toArray(new EventDispatcher[list.size()]);
61. }
62. private EventBus() {
63. }
64. public static void dispatch(final BusEventObject object) {
65. if(object == null) {
66. throw new IllegalArgumentException("null event object");
67. }
68. for(final EventDispatcher dispatcher : DISPATCHERS) {
69. dispatcher.dispatch(object);
70. }
71. }
72. public static interface EventDispatcher {
73.
74. void dispatch(BusEventObject object);
75. }
76. }
32/3<123>