Spring JMS - 使用注解的声明式事务管理

Spring JMS - Declarative Transaction Management Using Annotations

我有一些旧代码需要重构,其中包含 JMS 的程序化事务管理。

有一项计划服务可以同步读取所有消息(使用 JMS)并一次处理一条消息。我正在使用 JmsTransactionManager 进行交易。我可以使用注释来使用声明式事务管理来管理每条消息的事务,而不是像这样以编程方式管理它们吗:

//code from scheduled service's run method
private void run()
{
    javax.jms.Message jmsMessage = null;
    do
    {
        TransactionStatus status = null;
        try
        {
            status = jmsTransactionManager.getTransaction(new DefaultTransactionDefinition());
            jmsMessage = jmsTemplate.receive(heldTransmissionDestination);
            if(jmsMessage != null)
            {

                process(jmsMessage);
                jmsMessage.acknowledge(); //session is still open within the transaction
            }
            jmsTransactionManager.commit(status);
        }
        catch(Exception e)
        {
            logger.error("Exception: ", e);
            if(status != null)
            {
                jmsTransactionManager.rollback(status);
                logger.info("JMSTransaction rollback successful");
            }
            //since an exception occured, break out of the do-while
            break;
        }
    }
    while(jmsMessage != null);
}

请注意,此代码有效。 jmsTemplate 的配置将 sessionTrasacted 设置为 true,如下所示:

// from config
@Bean
public JmsTemplate jmsTemplate() {
    JmsTemplate jmsTemplate = new JmsTemplate();
    jmsTemplate.setSessionTransacted(true);
    // ... other stuff ommited for brevity
    return jmsTemplate;
}

我试过了,它确实适用于注释。但是,有几点需要注意:

首先,是在您的配置中使用 @EnableTransactionManagement(如果您使用的是纯 java DSL),如下所示:

@Configuration
@EnableTransactionManagement
public class JmsConfig {

    @Bean
    public JmsTemplate jmsTemplate() {
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setSessionTransacted(true);
        // ... other stuff ommited for brevity
        return jmsTemplate;
    }

    @Bean
    public JmsTransactionManager jmsTransactionManager() {
        JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
        jmsTransactionManager.setConnectionFactory(jmsConnectionFactory());
        return jmsTransactionManager;
    }

    //other bean definitions omitted for brevity
}

或者,您可以在 XML 配置中执行类似的操作,而不是上面的操作:

<tx:annotation-driven transaction-manager="jmsTransactionManager"/>
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
    <property name="connectionFactory">
        <ref bean="jmsConnectionFactory" />
    </property>
</bean>

其次,交易方式proxied(AOP)。因此,需要在一个事务中的逻辑需要移动到另一个 class 以便它可以被代理。请注意,spring 确实会自动检测您的事务管理器,如果您只将其命名为 transactionManager。但就我而言,我有多个这样的 bean,所以我必须明确地使用这个;并在我的注释的值属性中引用它,如下所示:

public class ServiceToSchedule
{

    @Autowired
    private JmsMessageProcessor jmsMessageProcessor;

    public void run()
    {
        Message jmsMessage = null;

        do
        {
            try {
                jmsMessage = jmsMessageProcessor.readAndProcessMessage();
            } catch (JMSException e) {
                logger.debug("JMSException processing heldTransmission: ", e);
                break;
            } catch (Exception e) {
                logger.debug("Exception processing heldTransmission: ", e);
                break;
            }

        }
        while(jmsMessage != null);
    }
}

public class JmsMessageProcessor
{

    @Transactional(value="jmsTransactionManager", propagation = Propagation.REQUIRES_NEW)
    public Message readAndProcessMessage() throws JMSException
    {
        Message jmsMessage = jmsTemplate.receive(heldTransmissionDestination);
        if(jmsMessage != null)
        {
            process(jmsMessage);
        }
        //have to return to break out of the while in the caller
        return jmsMessage;
    }

    @Transactional(value="jmsTransactionManager", propagation = Propagation.NESTED)
    protected void process(Message jmsMessage)
    {
        //code to process the jmsMessage, can potentially throw 
        //an exception that requires rolling back the jms transaction
    }   
}