Spring RabbitListener注释上的动态路由键

Spring RabbitListener注释上的动态路由键,spring,spring-amqp,spring-el,Spring,Spring Amqp,Spring El,我需要为每个登录到应用程序的用户创建一个链接到Direct Exchange的队列。底层路由将为“用户\用户ID” 也就是说,每次我通过用户管理队列接收到一条用户登录的消息时。实例化一个范围为“prototype”的bean,该bean包含一个用RabbitListener注释的方法来声明其队列。我将userId传递给这个bean,以便能够配置队列和routingKey的名称。但由于循环引用错误,我无法访问Spel表达式中的此实例变量 在这里,我放置了声明队列的bean: @Component(

我需要为每个登录到应用程序的用户创建一个链接到Direct Exchange的队列。底层路由将为“用户\用户ID”

也就是说,每次我通过用户管理队列接收到一条用户登录的消息时。实例化一个范围为“prototype”的bean,该bean包含一个用RabbitListener注释的方法来声明其队列。我将userId传递给这个bean,以便能够配置队列和routingKey的名称。但由于循环引用错误,我无法访问Spel表达式中的此实例变量

在这里,我放置了声明队列的bean:

@Component("usersHandler")
@Scope(value = "prototype")
public class UsersHandler {

    private static Logger logger = LoggerFactory.getLogger(UsersHandler.class);

    private Long userId;

    public UsersHandler(Long userId) {
        this.userId = userId;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    @RabbitListener(bindings
            = @QueueBinding(
                    value = @Queue(
                            value = "#{'queue_'.concat(usersHandler.userId)}",
                            durable = "false",
                            autoDelete = "true",
                            arguments = {
                                @Argument(
                                        name = "x-message-ttl",
                                        value = "#{rabbitCustomProperties.directExchange.queueArguments['x-message-ttl']}",
                                        type = "java.lang.Integer"
                                )
                                ,
                                @Argument(
                                        name = "x-expires",
                                        value = "#{rabbitCustomProperties.directExchange.queueArguments['x-expires']}",
                                        type = "java.lang.Integer"
                                )
                                ,
                                @Argument(
                                        name = "x-dead-letter-exchange",
                                        value = "#{rabbitCustomProperties.directExchange.queueArguments['x-dead-letter-exchange']}",
                                        type = "java.lang.String"
                                )
                            }
                    ),
                    exchange = @Exchange(
                            value = "#{rabbitCustomProperties.directExchange.name}",
                            type = ExchangeTypes.DIRECT,
                            durable = "#{rabbitCustomProperties.directExchange.durable}",
                            autoDelete = "#{rabbitCustomProperties.directExchange.autoDelete}",
                            arguments = {
                                @Argument(
                                        name = "alternate-exchange",
                                        value = "#{rabbitCustomProperties.directExchange.arguments['alternate-exchange']}",
                                        type = "java.lang.String"
                                )
                            }
                    ),
                    key = "#{'user_'.concat(usersHandler.userId)}")
    )
    public void handleMessage(@Payload Notification notification) {
        logger.info("Notification Received : " + notification);
    }

}
这是另一个bean,负责创建与用户登录数量相同的UserHandler:

@Component("adminHandler")
public class AdminHandler implements UsersManadgementVisitor {

    @Autowired
    private ApplicationContext appCtx;

    private Map<Long, UsersHandler> handlers = new HashMap<Long, UsersHandler>();

    private static Logger logger = LoggerFactory.getLogger(AdminHandler.class);

    @RabbitListener(queues="#{rabbitCustomProperties.adminExchange.queues['users'].name}")
    public void handleMessage(@Payload UsersManadgementMessage message) {
        logger.info("Message -> " + message);
        message.getType().accept(this, message.getId());
    }

    @Override
    public void visitUserConnected(Long idUser) {
        logger.info("Declare new queue for user: " + idUser );
        UsersHandler userHandler = appCtx.getBean(UsersHandler.class, idUser);
        handlers.put(idUser, userHandler);
    }

    @Override
    public void visitUserDisconnected(Long idUser) {
        logger.info("Remove queue for user: " + idUser );
        handlers.remove(idUser);
    }
}
@组件(“adminHandler”)
公共类AdminHandler实现UsersManadgementVisitor{
@自动连线
私有应用上下文appCtx;
私有映射处理程序=new HashMap();
私有静态记录器Logger=LoggerFactory.getLogger(AdminHandler.class);
@RabbitListener(queues=“#{rabbitCustomProperties.adminExchange.queues['users'].name}”)
public void handleMessage(@Payload UsersManadgementMessage){
logger.info(“消息->”+消息);
message.getType().accept(这是message.getId());
}
@凌驾
公共无效访问已连接(长idUser){
info(“为用户声明新队列:”+idUser);
UsersHandler userHandler=appCtx.getBean(UsersHandler.class,idUser);
put(idUser,userHandler);
}
@凌驾
公共无效访问已断开连接(长idUser){
info(“删除用户的队列:”+idUser);
删除(idUser);
}
}
我的问题是:


如何使变量userId在SpEL表达式的计算上下文中可用?

您可以使用
ThreadLocal
T
运算符

@SpringBootApplication
public class So43717710Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So43717710Application.class, args);
        UserHolder.setUser("someUser");
        context.getBean(Listener.class);
        UserHolder.clearUser();
        context.getBean(RabbitTemplate.class).convertAndSend("foo", "user_someUser", "bar");
        Thread.sleep(5000);
        context.close();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Listener listener() {
        return new Listener();
    }


    public static class Listener {

        @RabbitListener(bindings = @QueueBinding(value = @Queue("#{'queue_' + T(com.example.UserHolder).getUser()}"),
                exchange = @Exchange(value = "foo"),
                key = "#{'user_' + T(com.example.UserHolder).getUser()}"))
        public void listen(String in) {
            System.out.println(in);
        }

    }

}

public class UserHolder {

    private static final ThreadLocal<String> user = new ThreadLocal<String>();

    public static void setUser(String userId) {
        user.set(userId);
    }

    public static String getUser() {
        return user.get();
    }

    public static void clearUser() {
        user.remove();
    }

 }

呜呜!!魔术谢谢你的帮助,我觉得这是一个优雅的解决方案。
@SpringBootApplication
public class So43717710Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So43717710Application.class, args);
        UserHolder.setUser("someUser");
        context.getBean(Listener.class);
        UserHolder.clearUser();
        context.getBean(RabbitTemplate.class).convertAndSend("foo", "user_someUser", "bar");
        Thread.sleep(5000);
        context.close();
    }

    @Bean
    public UserHolder holder() {
        return new UserHolder();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Listener listener() {
        return new Listener();
    }


    public static class Listener {

        @RabbitListener(bindings = @QueueBinding(value = @Queue("#{'queue_' + @holder.user}"),
                exchange = @Exchange(value = "foo"),
                key = "#{'user_' + @holder.user}"))
        public void listen(String in) {
            System.out.println(in);
        }

    }

}