Listener,直译为侦听器或监听器,在面向对象的开发中经常需要用到。如不美观你需要启动或者遏制基于Java的 Web系统平分歧部门的代码,那么你可以使用一个简单的ServletContentListener来监听容器(container)琅缦沔的启动事务和遏制事务。这个监听器可以使用java.util.ServiceLoader去寻找这些被侦听事务所对应的已注册类。 这个体例不错,可是如不美观添加一个编译时刻注释措置器会不会更好呢?如不美观你用 @Lifecycle(LifecycleEvent.STARTUP)注释一个静态体例,它将会在开机的时辰被挪用(在关机的时辰被关失踪)。措置器会发生类,并为了ServiceLoader而注册它们。你也可以把同样的机制用在任何事务总线(event-bus)模子上:在编译的时辰注册 listener,而且带有注释,当事务被触发的时辰总线会自动挪用他们。年夜素质上讲,你可以经由过程注释实现用ServiceLoader在运行时刻自动发现代码。
$ ]! Z5 x6 Y) }) p: ]7 a& g 现实过程中,其概念如下:
D; H6 P) p" E6 D; c4 z 1. 你用@EventListener对体例进行注释(可能包含一些元信息(meta-info))。; v; m7 a7 U- z( r+ }: O, s+ c
2. 注释措置器为每个@EventListener体例生成一个EventDispatcher,搜罗注释中元信息需要的过滤器。
- {5 r9 @4 q6 @ 3. 事务总线操作java.util.ServiceLoader找到EventDispatcher的实现。
9 p2 h, V+ n# g) H b+ \ 当EventBus.dispatch被挪用时,任何有乐趣的、已经用@EventListener注释的体例城市被挪用。: m5 z; F5 r6 x+ p w2 `
本文将对事务总线建树的几个需要轨范进行剖析,年夜而说明章矣门念。事务总线不需要任何手动注册就可根柢用已注释了的listener体例。我们将年夜Eventbus起头谈判,然后是注释措置器,最后是一个用法实例。
( h& y% M8 G9 @3 b6 I4 o) f3 H' T# V/ m 组织你的代码
; ?: K) a u2 c2 F 这个例子的代码包含两个零丁的IDE工程) [1 i; F* K# Q& ^4 l. D) h' @/ M: l& `- D
◆EventBus ——包含了事务总线以及注释措置器5 |! t! g* t& e8 H/ S
◆EventBusExample ——包含了一个使用事务总线的例子
F5 ?% K1 K6 _1 J 当操作注释措置器的时辰,你应该在IDE选项中封锁"Compile on Save"(或者其他等同的选项)。这些选项可能会删除注释措置器所生成的类,让你摸不着脑子。 ]8 z0 Z% T3 X1 X b7 i; g
以下内容将会诠释这些工程中的代码是若何工作的,而且为了便于声名还供给了一些轨范片段。5 a, n E# V# l( G/ _, T/ |- y K* M# `; j* Y
注释和事务
( C3 f1 H p1 C5 G; K& t+ i 你需要的第一个工具是一个@EventListener注释,用来标识那些侦听事务的体例。下面是一个EventListener注释的例子,它只能用来注释体例。在代码编译后它将被丢弃,因为所有的措置都是对源代码进行的。1 ^2 E) K+ R# X; _ C
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface EventListener { String name() default ".*"; Class source() default Object.class; } 因为这个例子是一个事务总线模子,那么listener体例最好只接管它们独一感乐趣的事务。为了便于执行这个轨则,在BusEventObject类中包含了你想过滤的名字(以@EventListener注释琅缦沔的名字为基本)。为了让过滤事务加倍简单,这个通俗的EventObject类中还有一个附加的名字域。BusEventObject也作为一个标识,可以标识出经由过程EventBus分拨的事务。
4 _7 `- c+ E' W8 Z7 w4 n public abstract class BusEventObject extends EventObject { private final String name; public BusEventObject( final Object source, final String name) { super(source); if(name == null || name.isEmpty()) { throw new IllegalArgumentException("empty or null name"); } this.name = name; } public String getName() { return name; } } 注释措置器$ l( {) I6 i' ~- n; X
为了起头写注释措置器,你首先应该熟悉javax.annotation.processing 和 javax.lang.model的包组。一般来说,你可以直接擦过执行措置孀居口,进入抽象类 javax.annotation.processing.AbstractProcessor。AbstractProcessor需要一些关于实现的信息,这些信息用来供给注释。例子中的EventListenerAnnotationProcessor代码声明如下所示:7 X( c2 U) l5 h
- L5 L! y# }& B$ K- p( @8 R
@SupportedSourceVersion(SourceVersion.RELEASE_5) @SupportedAnnotationTypes(EventListenerAnnotationProcessor.ANNOTATION_TYPE) public class EventListenerAnnotationProcessor extends AbstractProcessor { @SupportedSourceVersion告诉AbstractProcessor你只想要用java5或者更高版本写的源文件;而 @SupportedAnnotationTypes告诉AbstractProcessor哪个注释是你感乐趣的(EventListener.class.getName()不会作为一个注释值起浸染,因为编译器不能计较这种表达式的值)。 |