是否可以在运行时使用 Spring 配置设置 JMS 侦听器目标

Is it possible to set a JMS listener destination at runtime with Spring configuration

我们在 Spring 应用程序中使用 ActiveMQ,我正在设置使用基于定义发送数据的 class 名称的虚拟主题的设计。所以如果我有 class

public class SomeEvent implements JmsEvent<SomeEvent> {
...
}

其中 JmsEvent 是用于将实现 class 转换为 Message 的辅助接口,我有另一个 class 将使用可配置的覆盖确定目标名称

public class DestinationNameHelper {
    private Map<Class<?>, String> overrides;
    ... 
    public String getDestinationName(Class<?> type) {
        return overrides.getOrDefault(type, type.getSimpleName());
    }
}

这适用于 JmsTemplate,其中为每条发送的消息传递目的地名称。我有一个 class 预置 VirtualTopic. 结果,所以如果你调用 publisher.publish(someEvent) 发送 SomeEvent 的实际目的地将是 VirtualTopic.SomeEvent.

我想做的是为听众使用相同类型的东西,所以如果我有

public class SomeEventListener extends BaseMessageListener<SomeEvent> {
    public void handleMessage(Message message) {}
}

我希望能够自动将其设置为侦听目的地 Consumer.SomeEventListener.VirtualTopic.SomeEvent,现在正在将其手动输入到 XML 配置文件中。 让监听器 class 名称可覆盖会很好,但我至少希望通过 DestinationNameHelper 检索虚拟主题名称(即 SomeEvent),这样监听器配置就不会如果我们覆盖 class' 目的地名称,则不必更改。

例如如果我们将 (SomeEvent.class, "OverrideName") 添加到 DestinationNameHelper 中的覆盖映射,class SomeEvent 的消息应该在

上发布
VirtualTopic.OverrideName

听者应该听

Consumer.SomeEventListener.VirtualTopic.OverrideName

我无法弄清楚在 XML 配置中执行此操作的 SPEL 咒语,并且目标是使用 JmsListenerConfigurer 使目的地名称基于 class 名称可能超出范围。

有没有办法实现我想要的配置?

[编辑] 我的配置看起来像这样:

<bean id="someEventListenerBean" class="com.example.SomeEventListener"/>
<bean id="someEventListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg>
        <bean class="com.example.SomeEventListener"/>
    </constructor-arg>
    <property name="messageConverter">
        <null/>
    </property>
</bean>

<jms:listener-container connection-factory="jmsConnectionFactory" acknowledge="transacted" concurrency="2-5">
    <jms:listener destination="Consumer.SomeEventListener.VirtualTopic.Somevent" ref="testListener"/>
</jms:listener-container>

感谢贾斯汀和加里成为橡皮鸭。澄清并发布我的配置帮助我找到了答案,尽管它可能会被优化。

我修改了 Listener 接口以包含

Class<T> getEventType();

所以所有实施 classes 的人都必须 return 他们正在处理的事件的 class 类型。然后我创建了一个 class like

public class ListenerDestinationHelper {

    private final DestinationHelper destinationHelper;

    public ListenerDestinationHelper(DestinationHelper destinationHelper) {
        this.destinationHelper = destinationHelper;
    }

    public <E extends JmsEvent<E>, M extends Message, T extends JmsEventListener<E, M>> String getDestinationName(T listener) {
        final Class<E> eventClass = listener.getEventType();
        return "Consumer." + listener.getClass().getSimpleName() + ".VirtualTopic." + destinationNameFactory.getDestinationBaseName(eventClass);
    }

}

现在配置包括

<bean id="listenerDestinationHelper" class="com.example.ListenerDestinationHelper"/>

<jms:listener-container connection-factory="jmsConnectionFactory" acknowledge="transacted" concurrency="2-5">
    <jms:listener destination="#{listenerDestinationHelper.getDestinationName(someEventListenerBean)}" ref="testListener"/>
</jms:listener-container>

从这里开始,为侦听器添加覆盖是微不足道的 class 简单名称