这些都是需要满足的要求
有问题的链是以下
<int:poller id="myInputChannelPoller">
<int:transactional transaction-manager="myTransactionManager">
</int:poller>
<int:chain id="myChain" input-channel="myInputChannel">
<int:poller ref="myInputChannelPoller" />
<int:service-activtor id="someMessageManipulation" />
<int:service-activtor id="aDatabaseOperation" />
<int:service-activtor id="sendJmsMessage" />
<int:service-activtor id="anotherMessageManipulation" />
<int:service-activtor id="anotherDatabaseOperation" />
</int:chain>
为了在事务中包装链,我将<int:transactional/>
标记添加到轮询器。
在数据库事务之前提交了所有JMS事务,我使用了ChainedTransactionManager
@Bean
public PlatformTransactionManager myTransactionManager(JpaTransactionManager jpaTransactionManager, JmsTransactionmanager jmsTransactionManager) {
// jpa before jms, since the commits will be done in reverse order
return new ChainedTransactionManager(jpaTransactionManager, jmsTransactionmanager)
}
当JMS提交失败时,这也确保没有数据库提交。
我不确定如何正确满足的唯一要求是后数据库提交操作。我想到的第一个想法是
中的transaction-synchronization-factory
机制
<int:transaction-synchronization-factory id="mySyncFactory">
<int:after-commit channel="myOutputChannel" />
</int:transaction-synchronization-factory>
并将其链接到现有轮询器的事务
<int:poller id="myInputChannelPoller">
<int:transactional transaction-manager="myTransactionManager" synchronization-factory="mySyncFactory">
</int:poller>
但是这种方法似乎发送了最初从myInputChannel收到的消息,而没有考虑消息操作。
另一个想法是在链的末尾添加另一个service-activator
MyJpaTransactionManagerInjector
,它将消息和输出队列作为有效负载添加到JpaTransactionManager MyJpaTransactionManager
的子类。
接收消息和目标输出通道作为输入。创建ForwardMessage
并将其注入MyJpaTransactionManager
。
public class MyJpaTransactionManagerInjector {
@Inject
private MyJpaTransactionManager myJpaTransactionManager;
@ServiceActivator
public void injectMessage(Message<?> message, @Header(value = "outputChannel") MessageChannel outputChannel) {
myJpaTransactionManager.inject(new ForwardMessage(message, outputChannel));
}
}
在执行提交之前注册ForwardMessage
。
public class MyJpaTransactionManager extends JpaTransactionManager {
private final ThreadLocal<List<TransactionSynchronizationAdapter>> synchronizations =
new NamedThreadLocal<>("Transactional resources");
public void inject(TransactionSynchronizationAdapter synchronization) {
if (synchronizations.get() == null) {
synchronizations.set(new ArrayList<>());
}
synchronizations.get().add(synchronization);
}
@Override
protected void prepareForCommit(DefaultTransactionStatus status) {
super.prepareForCommit(status);
if (synchronizations.get() != null) {
synchronizations.get().forEach(TransactionSynchronizationManager::registerSynchronization);
}
synchronizations.remove();
}
}
将afterCommit()
上的消息转发至目标渠道
public class ForwardMessage extends TransactionSynchronizationAdapter {
private Message<?> message;
private MessageChannel outputChannel;
public ForwardMessage(Message<?> message, MessageChannel outputChannel) {
this.message = message;
this.outputChannel = outputChannel;
}
@Override
public void afterCommit() {
outputChannel.send(message);
}
}
只要有可能,我会避免继承TransactionManager
,因为它很容易打破一些效果非常好的东西。有没有一个解决方案应该优于这个?
答案 0 :(得分:1)
我仍然坚持使用<int:transaction-synchronization-factory>
。
PollingConsumer
注册了这个:
TransactionSynchronizationManager.bindResource(resource,
integrationSynchronization.getResourceHolder());
进入交易。
其中resource
是消费者的MessageChannel
。在你的情况下,它是myInputChannel
bean。
您可以使用以下内容:
IntegrationResourceHolder integrationResourceHolder = (IntegrationResourceHolder) TransactionSynchronizationManager.getResource(myInputChannel);
并在处理后调用其addAttribute()
来存储消息:
integrationResourceHolder.addAttribute("resultMessage", resultMessage);
之后您仍然可以使用<int:after-commit channel="myOutputChannel" />
并通过适当的SpEL变量访问该属性:
<int:after-commit channel="myOutputChannel" expression="#resultMessage"/>