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配置不变,正常注入拦截器即可。
问题根源说明
出现循环依赖的原因是:
WebSocketConfig初始化时需要注入TopicSubscriptionInterceptorTopicSubscriptionInterceptor初始化时需要注入SimpUserRegistrySimpUserRegistry由DelegatingWebSocketMessageBrokerConfiguration创建,而该类又依赖WebSocketConfig的配置(因为@EnableWebSocketMessageBroker导入了它)
两种方案都是通过延迟依赖获取/初始化的方式,打破了初始化阶段的循环依赖链条,确保Bean能正常创建。
内容的提问来源于stack exchange,提问作者NoobSailboat
相关产品推荐
相关产品推荐

