You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebSocket:在ChannelInterceptor中使用SimpUserRegistry避免依赖循环

解决SimpUserRegistry与拦截器的循环依赖问题

要实现通过SimpUserRegistry限制主题订阅数,同时解决循环依赖,可采用以下几种有效方案:

方案一:通过ApplicationContextAware延迟获取SimpUserRegistry

让拦截器实现ApplicationContextAware接口,在初始化时不直接注入SimpUserRegistry,而是在需要使用时从Spring上下文获取,避免初始化阶段的循环依赖:

@Component
public class TopicSubscriptionInterceptor implements ChannelInterceptor, ApplicationContextAware {

    private SimpUserRegistry userRegistry;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 延迟获取SimpUserRegistry,绕过初始化时的循环依赖
        this.userRegistry = applicationContext.getBean(SimpUserRegistry.class);
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) {
            String destination = accessor.getDestination();
            // 统计当前主题的订阅用户数
            int subscriberCount = userRegistry.getUsers().stream()
                    .flatMap(user -> user.getSessions().stream())
                    .flatMap(session -> session.getSubscriptions().stream())
                    .filter(sub -> destination.equals(sub.getDestination()))
                    .toList()
                    .size();
            
            // 示例:限制最多10个订阅者
            if (subscriberCount >= 10) {
                accessor.setError(new MessageDeliveryException("主题订阅人数已达上限"));
                return message;
            }
        }
        return message;
    }
}

WebSocketConfig保持正常注入拦截器即可:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Autowired
    private TopicSubscriptionInterceptor topicSubscriptionInterceptor;

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(topicSubscriptionInterceptor);
    }

    // 其他WebSocket配置方法...
}

方案二:使用@Lazy注解延迟初始化依赖

在拦截器对SimpUserRegistry的注入上添加@Lazy,让Spring在第一次使用该依赖时才完成初始化,打破循环依赖链条:

@Component
public class TopicSubscriptionInterceptor implements ChannelInterceptor {

    @Autowired
    @Lazy
    private SimpUserRegistry userRegistry;

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        // 同方案一的业务逻辑
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) {
            String destination = accessor.getDestination();
            int subscriberCount = userRegistry.getUsers().stream()
                    .flatMap(user -> user.getSessions().stream())
                    .flatMap(session -> session.getSubscriptions().stream())
                    .filter(sub -> destination.equals(sub.getDestination()))
                    .toList()
                    .size();
            
            if (subscriberCount >= 10) {
                accessor.setError(new MessageDeliveryException("主题订阅人数已达上限"));
                return message;
            }
        }
        return message;
    }
}

WebSocketConfig配置不变,正常注入拦截器即可。

问题根源说明

出现循环依赖的原因是:

  1. WebSocketConfig初始化时需要注入TopicSubscriptionInterceptor
  2. TopicSubscriptionInterceptor初始化时需要注入SimpUserRegistry
  3. SimpUserRegistry由DelegatingWebSocketMessageBrokerConfiguration创建,而该类又依赖WebSocketConfig的配置(因为@EnableWebSocketMessageBroker导入了它)

两种方案都是通过延迟依赖获取/初始化的方式,打破了初始化阶段的循环依赖链条,确保Bean能正常创建。

内容的提问来源于stack exchange,提问作者NoobSailboat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 02:56:52