websocket-cometd源码阅读-基于注解初始化(三)

配置例子

 <!-- CometD Servlet -->
  <servlet>
    <servlet-name>cometd</servlet-name>
    <servlet-class>org.cometd.annotation.server.AnnotationCometDServlet</servlet-class>
    <!--liqiang todo 600000-->
    <init-param>
      <param-name>maxProcessing</param-name>
      <param-value>600000</param-value>
    </init-param>
    <init-param>
      <param-name>timeout</param-name>
      <param-value>20000</param-value>
    </init-param>
    <init-param>
      <param-name>interval</param-name>
      <param-value>0</param-value>
    </init-param>
    <init-param>
      <param-name>maxInterval</param-name>
      <param-value>10000</param-value>
    </init-param>
    <init-param>
      <param-name>handshakeReconnect</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>maxLazyTimeout</param-name>
      <param-value>5000</param-value>
    </init-param>
    <init-param>
      <param-name>long-polling.multiSessionInterval</param-name>
      <param-value>2000</param-value>
    </init-param>
    <init-param>
      <param-name>services</param-name>
      <param-value>org.cometd.examples.ChatService</param-value>
    </init-param>
    <init-param>
      <param-name>ws.cometdURLMapping</param-name>
      <param-value>/cometd/*</param-value>
    </init-param>
    <!--容器启动时调用init方法初始化 而不是第一次调用时-->
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
  </servlet>

基于注解的使用例子

@Service("chat") //comted注解
public class ChatService {
    //保存用户信息 不同房间
    private final ConcurrentMap<String, Map<String, String>> _members = new ConcurrentHashMap<>();
    @Inject //comted注解注入
    private BayeuxServer _bayeux;
    @Session //comted注解注入
    private ServerSession _session;

    //初始化 "/chat/**", "/members/**" 渠道
    @Configure({"/chat/**", "/members/**"})
    protected void configureChatStarStar(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(), new BadWordFilter());
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
    }
    //初始化 "/service/members" 渠道
    @Configure("/service/members")
    protected void configureMembers(ConfigurableServerChannel channel) {
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
        channel.setPersistent(true);
    }

    /**
     * 渠道监听器
     * @param client
     * @param message
     */
    @Listener("/service/members")
    public void handleMembership(ServerSession client, ServerMessage message) {
        //获得消息
        Map<String, Object> data = message.getDataAsMap();
        //获得房间信息
        String room = ((String)data.get("room")).substring("/chat/".length());
        //获得房间的用户信息
        Map<String, String> roomMembers = _members.get(room);
        if (roomMembers == null) {
            Map<String, String> new_room = new ConcurrentHashMap<>();
            //加入房间
            roomMembers = _members.putIfAbsent(room, new_room);
            if (roomMembers == null) {
                roomMembers = new_room;
            }
        }
        Map<String, String> members = roomMembers;
        //获得房间的用户信息
        String userName = (String)data.get("user");
        //用户信息与clientId映射
        members.put(userName, client.getId());
        //添加监听器 此客户端断开连接后 告诉当前房间所有人连接词用户下线 以及清除连接信息
        client.addListener((ServerSession.RemovedListener)(s, m, t) -> {
            //清除此房间连接信息
            members.values().remove(s.getId());
            //通知所有用户
            broadcastMembers(room, members.keySet());
        });
        //建立连接后推送订阅者最新的成员名单
        broadcastMembers(room, members.keySet());
    }

    private void broadcastMembers(String room, Set<String> members) {
        // Broadcast the new members list 推送订阅此房间的所有用户告知用户下线
        ClientSessionChannel channel = _session.getLocalSession().getChannel("/members/" + room);
        channel.publish(members);
    }

    @Configure("/service/privatechat")
    protected void configurePrivateChat(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(), new BadWordFilter());
        channel.setPersistent(true);
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
    }

    @Listener("/service/privatechat")
    public void privateChat(ServerSession client, ServerMessage message) {
        Map<String, Object> data = message.getDataAsMap();
        String room = ((String)data.get("room")).substring("/chat/".length());
        Map<String, String> membersMap = _members.get(room);
        if (membersMap == null) {
            Map<String, String> new_room = new ConcurrentHashMap<>();
            membersMap = _members.putIfAbsent(room, new_room);
            if (membersMap == null) {
                membersMap = new_room;
            }
        }
        String[] peerNames = ((String)data.get("peer")).split(",");
        ArrayList<ServerSession> peers = new ArrayList<>(peerNames.length);

        for (String peerName : peerNames) {
            String peerId = membersMap.get(peerName);
            if (peerId != null) {
                ServerSession peer = _bayeux.getSession(peerId);
                if (peer != null) {
                    peers.add(peer);
                }
            }
        }

        if (peers.size() > 0) {
            Map<String, Object> chat = new HashMap<>();
            String text = (String)data.get("chat");
            chat.put("chat", text);
            chat.put("user", data.get("user"));
            chat.put("scope", "private");
            ServerMessage.Mutable forward = _bayeux.newMessage();
            forward.setChannel("/chat/" + room);
            forward.setId(message.getId());
            forward.setData(chat);

            // test for lazy messages
            if (text.lastIndexOf("lazy") > 0) {
                forward.setLazy(true);
            }

            for (ServerSession peer : peers) {
                if (peer != client) {
                    peer.deliver(_session, forward, Promise.noop());
                }
            }
            client.deliver(_session, forward, Promise.noop());
        }
    }

    class BadWordFilter extends JSONDataFilter {
        @Override
        protected Object filterString(ServerSession session, ServerChannel channel, String string) {
            if (string.contains("dang")) {
                throw new DataFilter.AbortException();
            }
            return string;
        }
    }
}

源码

<1>

org.cometd.annotation.server.AnnotationCometDServlet#init

   @Override
    public void init() throws ServletException {
        //<2>继承了comet 这里是先对comte进行初始化
        super.init();

        //基于注解方式初始化处理类对象的Processor  new ServerAnnotationProcessor(bayeuxServer);
        processor = newServerAnnotationProcessor(getBayeux());

        //获得servlet initParameter的Service配置
        String servicesParam = getInitParameter("services");
        if (servicesParam != null && servicesParam.length() > 0) {

            for (String serviceClass : servicesParam.split(",")) {
                //<3>创建处理类对象 也就是xml配置的chatServerce
                Object service = processService(processor, serviceClass.trim());
                services.add(service);
                //设置当前Service到requestAttribute
                registerService(service);
            }
        }
    }

    protected ServerAnnotationProcessor newServerAnnotationProcessor(BayeuxServer bayeuxServer) {
        return new ServerAnnotationProcessor(bayeuxServer);
    }

<3>

org.cometd.annotation.server.AnnotationCometDServlet#processService

    protected Object processService(ServerAnnotationProcessor processor, String serviceClassName) throws ServletException {
        try {
            //反射根据类的全名称创建类的对象
            Object service = newService(serviceClassName);
            //<4>初始化
            processor.process(service);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Processed annotated service {}", service);
            }
            return service;
        } catch (Exception x) {
            LOGGER.warn("Failed to create annotated service " + serviceClassName, x);
            throw new ServletException(x);
        }
    }

    protected Object newService(String serviceClassName) throws Exception {
        return Loader.loadClass(getClass(), serviceClassName).newInstance();
    }

<4>

org.cometd.annotation.server.ServerAnnotationProcessor#process

  public boolean process(Object bean) {
        //<5>基于注解依赖注入主要处理 @Inject 和@Session注解
        boolean result = processDependencies(bean);
        //<6>根据@Configure注解的方法初始化channel
        result |= processConfigurations(bean);
        //<7>处理Subscription 订阅渠道
        result |= processCallbacks(bean);
        //<8>调用@PostConstruct 进行初始化操作
        result |= processPostConstruct(bean);
        return result;
    }

<5>

org.cometd.annotation.server.ServerAnnotationProcessor#processDependencies

 public boolean processDependencies(Object bean) {
        if (bean == null) {
            return false;
        }

        Class<?> klass = bean.getClass();
        //首选需要打了comted的@Service注解
        Service serviceAnnotation = klass.getAnnotation(Service.class);
        if (serviceAnnotation == null) {
            return false;
        }

        //容器中bean对象
        List<Object> injectables = new ArrayList<>(Arrays.asList(this.injectables));
        //默认增加是bayeuxServer 所以支持@Inject进行 bayeuxServer注入
        injectables.add(0, bayeuxServer);
        //<9>针对 bean的@Inject注解进行 注入
        boolean result = processInjectables(bean, injectables);
        //<10>针对Service初始化loaclSession 同时初始化一个serverSession与bayeuxServer模拟建立连接
        LocalSession session = findOrCreateLocalSession(bean, serviceAnnotation.value());
        //<11>session注入到bean 后续我们可以通过操作session发送消息
        result |= processSession(bean, session);
        return result;
    }

<9>

org.cometd.annotation.AnnotationProcessor#processInjectables

 protected boolean processInjectables(Object bean, List<Object> injectables) {
        boolean result = false;
        //遍历容器
        for (Object injectable : injectables) {
            result |= processInjectable(bean, injectable);
        }
        return result;
    }

    protected boolean processInjectable(Object bean, Object injectable) {
        boolean result = false;
        for (Class<?> c = bean.getClass(); c != Object.class; c = c.getSuperclass()) {
            //反射获得fields 获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                //如果打了@Inject注解
                if (field.getAnnotation(Inject.class) != null) {
                    /**
                     * 如果是A.isAssignableFrom(B) 确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是实现了另外一个接口(B),
                     * 或者两个类相同。主要,这里比较的维度不是实例对象,而是类本身,因为这个方法本身就是Class类的方法,判断的肯定是和类信息相关的。
                     */
                    if (field.getType().isAssignableFrom(injectable.getClass())) {
                        Object value = getField(bean, field);
                        if (value != null) {
                            if (LOGGER.isDebugEnabled()) {
                                LOGGER.debug("Avoid injection of field {} on bean {}, it's already injected with {}", field, bean, value);
                            }
                            continue;
                        }
                        //完成注入
                        setField(bean, field, injectable);
                        result = true;
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Injected {} to field {} on bean {}", injectable, field, bean);
                        }
                    }
                }
            }
        }

        //针对打在set方法上 的注入
        List<Method> methods = findAnnotatedMethods(bean, Inject.class);
        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                if (parameterTypes[0].isAssignableFrom(injectable.getClass())) {
                    invokePrivate(bean, method, injectable);
                    result = true;
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Injected {} to method {} on bean {}", injectable, method, bean);
                    }
                }
            }
        }
        return result;
    }

<10>

org.cometd.annotation.server.ServerAnnotationProcessor#findOrCreateLocalSession

private LocalSession findOrCreateLocalSession(Object bean, String name) {
        LocalSession session = sessions.get(bean);
        if (session == null) {
            //<12>创建一个Local session
            session = bayeuxServer.newLocalSession(name);
            //bean的名字作为key维护放入sessions
            LocalSession existing = sessions.putIfAbsent(bean, session);
            if (existing != null) {
                session = existing;
            } else {
                //<13>默认握手加入bayeuxServer
                session.handshake();
            }
        }
        return session;
    }

<12>

org.cometd.server.BayeuxServerImpl#newLocalSession

@Override
    public LocalSession newLocalSession(String idHint) {
        return new LocalSessionImpl(this, idHint);
    }

<13>

org.cometd.server.LocalSessionImpl#handshake

 @Override
    public void handshake(Map<String, Object> template, ClientSession.MessageListener callback) {
        if (_session != null) {
            throw new IllegalStateException("Method handshake() invoke multiple times for local session " + this);
        }

        ServerSessionImpl session = new ServerSessionImpl(_bayeux, this, _idHint);

        ServerMessage.Mutable hsMessage = newMessage();
        if (template != null) {
            hsMessage.putAll(template);
        }
        String messageId = newMessageId();
        hsMessage.setId(messageId);
        hsMessage.setChannel(Channel.META_HANDSHAKE);
        registerCallback(messageId, callback);

        doSend(session, hsMessage, Promise.from(hsReply -> {
            if (hsReply != null && hsReply.isSuccessful()) {
                _session = session;
                ServerMessage.Mutable cnMessage = newMessage();
                cnMessage.setId(newMessageId());
                cnMessage.setChannel(Channel.META_CONNECT);
                cnMessage.getAdvice(true).put(Message.INTERVAL_FIELD, -1L);
                cnMessage.setClientId(session.getId());
                doSend(session, cnMessage, Promise.from(cnReply -> {
                    // Nothing more to do.
                }, failure -> messageFailure(cnMessage, failure)));
            }
        }, failure -> messageFailure(hsMessage, failure)));
    }

<11>

org.cometd.annotation.server.ServerAnnotationProcessor#processSession

 

private boolean processSession(Object bean, LocalSession localSession) {
        ServerSession serverSession = localSession.getServerSession();

        boolean result = false;
        for (Class<?> c = bean.getClass(); c != Object.class; c = c.getSuperclass()) {
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                if (field.getAnnotation(Session.class) != null) {
                    Object value = null;
                    if (field.getType().isAssignableFrom(localSession.getClass())) {
                        value = localSession;
                    } else if (field.getType().isAssignableFrom(serverSession.getClass())) {
                        value = serverSession;
                    }

                    if (value != null) {
                        setField(bean, field, value);
                        result = true;
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Injected {} to field {} on bean {}", value, field, bean);
                        }
                    }
                }
            }
        }

        List<Method> methods = findAnnotatedMethods(bean, Session.class);
        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                Object value = null;
                if (parameterTypes[0].isAssignableFrom(localSession.getClass())) {
                    value = localSession;
                } else if (parameterTypes[0].isAssignableFrom(serverSession.getClass())) {
                    value = serverSession;
                }

                if (value != null) {
                    invokePrivate(bean, method, value);
                    result = true;
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Injected {} to method {} on bean {}", value, method, bean);
                    }
                }
            }
        }
        return result;
    }

<6>

org.cometd.annotation.server.ServerAnnotationProcessor#processConfigurations

public boolean processConfigurations(Object bean) {
        if (bean == null) {
            return false;
        }

        Class<?> klass = bean.getClass();
        //首选需要打了@Service注解
        Service serviceAnnotation = klass.getAnnotation(Service.class);
        if (serviceAnnotation == null) {
            return false;
        }

        //首先需要打了打了@Configure的方法
        List<Method> methods = findAnnotatedMethods(bean, Configure.class);
        if (methods.isEmpty()) {
            return false;
        }

        for (Method method : methods) {
            Configure configure = method.getAnnotation(Configure.class);
            //@Configure配置的value为channelName
            String[] channels = configure.value();
            for (String channelName : channels) {
                //定义一个Initializer接口的匿名方法 内部会调用调用@Configure注解方法
                Initializer init = channel -> {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Configure channel {} with method {} on bean {}", channel, method, bean);
                    }
                    invokePrivate(bean, method, channel);
                };

                //<14>针对@Configure创建Channel
                MarkedReference<ServerChannel> initializedChannel = bayeuxServer.createChannelIfAbsent(channelName, init);

                //内部初始化成功会将marked设置为true
                if (!initializedChannel.isMarked()) {
                    //是否配置了configureIfExists 为true 默认false 如果为true则会调用打了@Configure的方法
                    if (configure.configureIfExists()) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Configure again channel {} with method {} on bean {}", channelName, method, bean);
                        }
                        init.configureChannel(initializedChannel.getReference());
                    } else if (configure.errorIfExists()) {
                        throw new IllegalStateException("Channel already configured: " + channelName);
                    } else {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Channel {} already initialized. Not called method {} on bean {}", channelName, method, bean);
                        }
                    }
                }
            }
        }
        return true;
    }

<14>

org.cometd.server.BayeuxServerImpl#createChannelIfAbsent

  @Override
    public MarkedReference<ServerChannel> createChannelIfAbsent(String channelName, Initializer... initializers) {
        ChannelId channelId;
        boolean initialized = false;
        //尝试根据channelName获取 判断是否存在
        ServerChannelImpl channel = _channels.get(channelName);
        if (channel == null) {
            // Creating the ChannelId will also normalize the channelName.
            //尝试通过处理过的channelId获取
            channelId = new ChannelId(channelName);
            String id = channelId.getId();
            if (!id.equals(channelName)) {
                channelName = id;
                channel = _channels.get(channelName);
            }
        } else {
            channelId = channel.getChannelId();
        }

        //表示没有被初始化
        if (channel == null) {
            //新建一个channel
            ServerChannelImpl candidate = new ServerChannelImpl(this, channelId);
            //放入_channels
            channel = _channels.putIfAbsent(channelName, candidate);
            if (channel == null) {
                // My candidate channel was added to the map, so I'd better initialize it

                channel = candidate;
                if (_logger.isDebugEnabled()) {
                    _logger.debug("Added channel {}", channel);
                }

                try {
                    //通知 Initializer实现 可以对ServerChannelImpl做自定义配置
                    for (Initializer initializer : initializers) {
                        notifyConfigureChannel(initializer, channel);
                    }

                    //调用listeners中ChannelListener的configureChannel方法可以对channel进行自定义配置
                    for (BayeuxServer.BayeuxServerListener listener : _listeners) {
                        if (listener instanceof ServerChannel.Initializer) {
                            notifyConfigureChannel((Initializer)listener, channel);
                        }
                    }
                } finally {
                    channel.initialized();
                }
                //调用listeners中ChannelListener的channelAdded表示已经被初始化
                for (BayeuxServer.BayeuxServerListener listener : _listeners) {
                    if (listener instanceof BayeuxServer.ChannelListener) {
                        notifyChannelAdded((ChannelListener)listener, channel);
                    }
                }

                initialized = true;
            }
        } else {
            channel.resetSweeperPasses();
            // Double check if the sweeper removed this channel between the check at the top and here.
            // This is not 100% fool proof (e.g. this thread is preempted long enough for the sweeper
            // to remove the channel, but the alternative is to have a global lock)
            _channels.putIfAbsent(channelName, channel);
        }
        // Another thread may add this channel concurrently, so wait until it is initialized
        channel.waitForInitialized();
        return new MarkedReference<>(channel, initialized);
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章