package com.github.atemerev.pms.listeners.dispatch;
import com.github.atemerev.pms.Asynchronous;
import com.github.atemerev.pms.Listener;
import com.github.atemerev.pms.listeners.MessageListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Dispatch messages to their appropriate handler messages and (stay with us,
* because here things are coming little crazy) execute it within
* gracefully provided executor (default is current thread, but who stays with
* defaults these times?) Armed with this highly magical class, you can easily
* write asynchronous events processing with arbitrary complexity. Isn't it
* a little exciting?
* <p/>
*
* @author Alexander Kuklev
* @author Alexander Temerev
* @version $Id:$
*/
public class DispatchListener implements MessageListener {
private static final String LOCAL = "local"; // Magic workaround
private boolean async = false;
protected Executor executor;
protected Object listener;
// Yay, a meta-Java!
// 事件 --> 事件处理
// 类 --> 方法
protected Map<Class, Method> dispatchTable = new HashMap<Class, Method>();
/**
* Create new dispatch listener and route message handling to its own
* methods. Obviously, you will not want to use this constructor directly.
* Instead, extend DispatchListener with your own class and write handler
* methods right there.
*/
public DispatchListener() {
this(LOCAL);
}
/**
* Create new dispatch listener with default (this-thread synchronous)
* execution. Populate the method dispatch table.
*
* @param listener The actual listener instance, the object of a class
* with @Listener-annotated methods.
*/
public DispatchListener(Object listener) {
if (listener == LOCAL) {
listener = this;
}
this.listener = listener;
for (Class aClass = listener.getClass(); aClass != null; aClass = aClass.getSuperclass()) {
//一个监听器Handler可以定义多个方法. 每种方法可以处理不同类型的事件
for (Method method : aClass.getDeclaredMethods()) {
//监听器的方法注解是@Listener
Listener listenerAnnotation = method.getAnnotation(Listener.class);
//方法的参数类型: 即自定义的事件类型
Class[] parameterTypes = method.getParameterTypes();
if (listenerAnnotation != null && parameterTypes.length >= 1) {
//只处理第一个参数
Class messageType = parameterTypes[0];
Method handler = dispatchTable.get(messageType);
if (handler == null) {
//事件类型(类)-->事件处理(方法)
//Map限制了一种事件类型只能对应一个事件处理逻辑
dispatchTable.put(messageType, method);
}
}
}
}
}
/**
* Create new async dispatcher and initialize it with specified Executor.
*
* @param executor java.util.concurrent.Executor instance.
* @param async If true, all listeners will be executed through the supplied executor, regardless of
* their @Asynchronous annotation.
*/
public DispatchListener(Executor executor, boolean async) {
this(executor);
this.async = async;
}
/**
* Set your own executor for async message handling, so you won't have to
* wait until processing unblocks.
*
* @param executor Executor to handle messages.
*/
public void setExecutor(Executor executor) {
this.executor = executor;
}
/**
* Create new async dispatcher and initialize it with specified Executor.
*
* @param executor java.util.concurrent.Executor instance.
*/
public DispatchListener(Executor executor) {
this();
setExecutor(executor);
}
/**
* Create new async dispatcher with specified actual listener and
* executor for message handling.
*
* @param listener Object with @Listener-annotated methods to handle
* incoming messages.
* @param executor Executor to run these methods in.
*/
public DispatchListener(Object listener, Executor executor) {
this(listener);
setExecutor(executor);
}
/**
* Dispatch message to appropriate message handler and execute it with your
* very own highly kosher executor.
*
* @param message Message to process.
*/
@Override
public void processMessage(final Object message) {
//参数是类的实例,可以获取出对应的类(事件), 再从dispatchTable中取出Method:事件的处理逻辑
final Method method = findCorrespondingMethod(message);
if (method != null) {
//使用异步模式,在方法上要有注解:@Asynchronous
if (executor == null || (method.getAnnotation(Asynchronous.class) == null && !async)) {
//使用反射机制进行动态调用
invoke(listener, method, message);
} else {
//如果是多线程模式,则启动一个新的线程来执行事件的处理逻辑
executor.execute(new Runnable() {
public void run() {
invoke(listener, method, message);
}
});
}
}
}
/**
* Process message and dispatch it to one of your custom-written
* tailor-made Swiss-precise message handlers. And
* yes, some magic happens here, with reflections and stuff.
*
* @param message Message to dispatch to appropriate listener.
* @return Target method, or null if none found.
*/
protected Method findCorrespondingMethod(Object message) {
for (Class aClass = message.getClass(); aClass != null; aClass = aClass.getSuperclass()) {
Method method = dispatchTable.get(aClass);
if (method != null) {
return method;
}
//类名没找到,使用接口名
for (Class anInterface : aClass.getInterfaces()) {
method = dispatchTable.get(anInterface);
if (method != null) {
return method;
}
}
}
return null;
}
/**
* Invoke the found method and rethrow the exceptions as runtime.
*
* @param listener Listener with the method.
* @param method Method to invoke.
* @param message Message to pass to this method.
*/
protected final void invoke(Object listener, Method method, Object message) {
if (listener == null || method == null) return;
try {
method.setAccessible(true);
method.invoke(listener, message);
} catch (IllegalAccessException e1) {
// can never occur due to setAccessible(true);
} catch (InvocationTargetException e1) {
throw new RuntimeException(e1);
}
}
}