下面主要讲解跟 @OnEvent 相关代码的注释
一:SocketIoServer创建
启动服务端,首先要创建SocketIoServer,创建socketIoServer实例时做了如下几件事:
- 创建namespacesHub = new NamespacesHub(读取到的配置#{com.corundumstudio.socketio}),存放创建的所有的Namespace
- 创建并添加默认mainNamespace<“”,mainNamespace>到namespacesHub.namespaces中,mainNamespace默认名为空“”
- 创建mainNamespace时创建了ScannerEngine engine = new ScannerEngine() ,主要负责找到注解对应的AnnotationScanner
- 创建engine时创建了3个AnnotationScanner并放到了全局变量 annotations 中,用于定义扫描哪些注解,以及扫描到这些注解后要添加哪些监听,及调用到被这些注解标注的方法时要做的事情
private static final List<? extends AnnotationScanner> annotations=
Arrays.asList(new OnConnectScanner(), new OnDisconnectScanner(), new OnEventScanner());
二:SpringAnnotationScanner 注解扫描器
其实SpringAnnotationScanner实现了BeanPostProcessor,定义了三个要处理的注解
private final List<Class<? extends Annotation>> annotations =
Arrays.asList(OnConnect.class, OnDisconnect.class, OnEvent.class);
- 前置处理器: 判断要创建的bean中的方法是否有annotations 中定义的注解,有则赋值此类给originalBeanClass
- 后置处理器: 判断originalBeanClass是否为null,不为null,说明此类在前置处理器中判断此类中有方法有annotations中的注解 ,则
a. 向上面创建的socketIoServer实例中添加监听:socketIOServer.addListeners(bean, originalBeanClass);
b. 将originalBeanClass置为null
三:SocketIoServer
-
socketIdServer.addListeners(Object listeners, Class<?> listenersClass) 添加注解对应的监听
a. 调用mainNamespace.addListeners(listeners, listenersClass),啥也没做,直接调用scannerEngine.scan
b. 调用scannerEngine.scan(Namespace namespace, Object object, Class<?> clazz)
I. 获取此类中声明的方法,若这些方法标有annotations中对应的注解,则调用该注解对应的注解扫描器AnnotationScanner的下面两个方法,
II. annotationScanner.validate:校验方法入参进行校验,如OnEventScanner.validate
III. annotationScanner.addListener:向对应的注解扫面器annotationScanner添加监听,如OnEventScanner.addListener,当该注解标注的方法被调用时,就会回调添加监听者的onData方法进行对应的逻辑处理
四:OnEventScanner
- validate(Method method, Class<?> clazz):参数个数校验
a. 获取方法参数列表参数个数paramsCount
b. 获取方法中SocketIOClient和AckRequest类型的参数在参数列表中的个数socketIOClientIndex
c. 获取方法中AckRequest类型的参数在参数列表中的个数ackRequestIndex
d. 获取方法中除SocketIOClient和AckRequest类型所有真正业务用来传输数据的所有参数在参数列表中个数dataIndexes.size()
e. 判断paramsCount是否等于socketIOClientIndex+ackRequestIndex+dataIndexes.size() - addListener(Namespace namespace, final Object object, final Method method, Annotation annot)
a. 调用namesapce.addEventListener(eventName, eventClass, listener)
五:Namespace
private final ScannerEngine engine = new ScannerEngine();
private final ConcurrentMap<String, EventEntry<?>> eventListeners = PlatformDependent.newConcurrentHashMap();
- addListeners(Object listeners, Class<?> listenersClass)
a. 只调用了下engine.scan(this, listeners, listenersClass);
- addMultiTypeEventListener(String eventName, MultiTypeEventListener listener,
Class<?>… eventClass)真正给事件添加了监听者
a. 创建EventEntry<“#{方法@OnEvent对应的value},entry”>并存入eventListeners
b. 向entry添加监听者DataListener类型的OnEventScanner,实现onData方法,方法作用:
I. 获取方法所有参数存入args
II. 通过反射真正执行被注解标注的方法method.invoke(object, args)
六: EventEntry
private final Queue<DataListener<T>> listeners = new ConcurrentLinkedQueue<DataListener<T>>();;
- addListener(DataListener listener):将DataListener监听保存到listeners
以上都是启动Bean出事后完成的工作,下面就是建立连接后通过channel传输数据时的逻辑了
当前端发送数据到channel时,最终会走到pipeline中的InPacketHandler节点,从而走到channelRead0,从而调用 PacketListener.onPacket(Packet packet, NamespaceClient client, Transport transport)
,若type=Message;subType=PacketType.ACK,接着调用Namespace.onEvent(NamespaceClient client, String eventName, List<Object> args, AckRequest ackRequest)
,下面着重看一下Namespace.onEvent做了哪些事情:
七: Namespace.onEvent(NamespaceClient client, String eventName, List args, AckRequest ackRequest)
- 首先根据eventName获取eventListeners中对应的EntryEntry从而获取对应的所有监听DataListener
- 回调所有监听的onData,执行真正的监听逻辑,在onData里面通过反射调用了真正的方法
整体流程
大致逻辑: 服务端启动初始化bean时,利用SpringAnnotationScanner 的前后置处理器,找到@OnEvent注解,并将其key为@OnEvent设置的value,value为监听包装成的EventEntry保存到Namespace的eventListeners中 private final ConcurrentMap<String, EventEntry<?>> eventListeners = PlatformDependent.newConcurrentHashMap()
中,当有数据写入cannel中,服务端在读取channel中的数据时,获取对应namespace的所有监听eventListeners,并找到key为指定事件名的所有监听,并调用监听的onData方法,onData作为对应的逻辑处理后通过反射调用注解锁标注的方法