使用spring的抽象类作为MessageListener的Jms?

时间:2013-11-28 10:36:15

标签: java spring jms activemq abstract-class

我需要你的帮助来确认我的设计没问题。 使抽象类实现MessageListener是一个错误,因为我找不到任何资源这样做。

此解决方案仅在我以编程方式设置消息侦听器时才起作用,否则Spring会抱怨如果我尝试仅进行弹簧配置,则该类是抽象的。

我想要做的是提供一个API,用于使用每个应用程序必须使用的特定队列,并实现自己的OnMessage方法。

API基本上是:

  • 连接到JMS
  • 使用一条消息
  • 使用非抽象方法
  • 将json转换为抽象类中的对象
  • 调用抽象方法

应用程序必须实现抽象方法,然后使用对象执行他们想要的操作。

我的解决方案:

public abstract class MyReceiver implements MessageListener {
    public void startReceiving() throws JMSException {
        context = new ClassPathXmlApplicationContext("jms-context-client.xml");
        AbstractMessageListenerContainer container = context.getBean("jmsContainer", AbstractMessageListenerContainer.class);
        container.setMessageSelector("JMSCorrelationID = '" + this.clientID + "'");
        container.setMessageListener(this); // <-- good practice ?
        container.start();
    }

    @Override
    public void onMessage(Message message) {
        // transform json data to object
        // call abstract method that the client MUST implement
        onMessageAbstract(response);
    }

    public abstract void onMessageAbstract(MyObject response);
}

弹簧配置:

<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" init-method="start"
    destroy-method="stop">
    <property name="maxConnections" value="20" />
    <property name="connectionFactory" ref="connectionFactory" />
</bean>
<bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">
    <property name="brokerURL"><value><![CDATA[failover:(tcp://127.0.0.1:61616)?randomize=false&initialReconnectDelay=1000&useExponentialBackOff=false]]></value</property>
</bean>
<bean id="queueCommonMessage" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="foo.bar" />
</bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="queueCommonMessage" />
    <property name="autoStartup" value="false" />
    <property name="sessionTransacted" value="false"/>
    <property name="concurrentConsumers" value="1" />
</bean>

使用API​​的应用示例

public static void main(String[] args) throws JMSException {
        MyReceiver receiver = new MyReceiver("clientID") {
            @Override
            public void onMessageAbstract(MyObject response) {
                logger.debug("OK");
            }
        };
        receiver.startReceiving();    
    }

谢谢!

1 个答案:

答案 0 :(得分:2)

只要只有一个消费者或更精确的消费者,只要只有一个会话正在创建您的消费者(其中包含您的消息助手),就不会有任何问题。

只要你的MyReceiver类是无状态的,就把它放在一行中没有问题,并且对于从同一队列中消耗的不同应用程序可以正常工作。

当您的MyReceiver类具有某种状态时,会出现问题。例如,假设您有一个计数变量,每当您收到一条消息来跟踪收到的消息总数时,该变量就会递增。现在,如果你说

container.setMessageListener(this);

对于从单个连接的同一会话创建的两个接收器,它们将共享导致竞争条件的MyReceiver类的实例。

一般来说,我在JMS代码中看到的是

container.setMessageListener(new customMessageListener());

甚至是一个内在的匿名类。

但是根据您的要求,您的设计看起来不错。无需创建额外的对象。

仅仅为了记录,虽然两个或更多接收器同时从单个队列接收消息可以到达任何接收器。

当你说消费一条消息时,我也无法得到。

如果您使用异步接收。在关闭连接之前,您不能保证只接收一条消息。如果您的应用程序希望一次只接收一条消息,请使用receiver.receive()(同步/阻止接收)。