2011-03-18 5 views
10

私は現在進行中のトランザクションにトランザクションリスナーを追加したいspringframeworkアプリケーションを持っています。その動機は、下流のシステムに通知するポストコミットアクションを引き起こすことです。私はいくつかのサービスメソッドの周りにトランザクションをラップするために@Transactionalを使用しています。これはポストトランザクションリスナーを作成/登録する場所です。私は次のような "何か"をしたい。トランザクションリスナを春に動的に登録しますか?

public class MyService { 
@Transaction 
public void doIt() { 
    modifyObjects(); 

    // something like this 
    getTransactionManager().registerPostCommitAction(new 
    TransactionSynchronizationAdapter() { 
    public void afterCommit() { 
     notifyDownstream(); 
    } 
    }); 
} 
} 

Springには、私が欲しいと思うTransactionSynchronizationインターフェイスとアダプタクラスがあります。現行のトランザクションまたはトランザクション・マネージャのどちらに動的に登録するかはすぐには分かりません。私が避けることができれば、JtaTransactionManagerをサブクラス化しないでください。

Q:誰もこれまでにこれをしましたか?

Q:アダプターを登録する最も簡単な方法は何ですか?

答えて

3

あなたはこれを達成するために、あなたのサービスでaspect to matchトランザクションメソッドの側面を使用することができます。

@Aspect 
public class AfterReturningExample { 

    @AfterReturning("execution(* com.mypackage.MyService.*(..))") 
    public void afterReturning() { 
    // ... 
    } 

} 
+0

ただし、これはどのトランザクションでも実行されません。私は幸運な完成を望むだけです。また、これらのAOPアノテーションをJDKプロキシだけのデフォルトのスプリング(コード製織を使用していない)で使用できますか? – Justin

+0

メソッドが例外をスローするときにリターンアドバイスは実行されないので、メソッドがresultを返すときに@Transactionalをコミットし、例外をスローするときにロールバックします。アスペクトはバネの動的プロキシを介して呼び出されるため、コードを作成することはありません(バネからプロキシへのインタフェースを提供しない限り、実行時にcglibを使用してコードを組み立てることになります)。 – krock

+1

+1興味深いですが、私はまだテストなしでは確信していません。少なくとも2つの@AfterReturningが関わっています:@Transactionalと私のカスタムAspect。 @Transactionalが実行されている場合、すべてのMineが正常に実行されます。しかし、他の順序では、私はトランザクションコミット状態とは独立して前に実行します。 – Justin

26

実際にはハード、私は思ってませんでした。 springはスレッドコンテキストに '正しい'ものを入れる静的ヘルパークラスを持っています。ここで

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() { 
     @Override 
     public void afterCommit() { 
      s_logger.info("TRANSACTION COMPLETE!!!"); 
     } 
    } 
); 
+0

+1すてきで直接的な答えです。私は、TransactionSynchronizationManagerが存在するかどうかはわかりませんでした。 – krock

+0

実際、このソリューションは、私が持っていたほど良くはありません。 TransactionSynchronizationManagerのコントラクトはより広範囲であり、afterCommit()内からJMSメッセージを送信しようとすると(上のコードは契約に従っているので例外なく)失敗します。 – Justin

+0

ほとんどのSpringメッセージングソリューション(JMS、AMQP)を使用すると、2つのシステム(db、メッセージブローカ)間のトランザクションが同期されるように、トランザクションマネージャにプラグインすることができます。私はRabbitMQ TXが非常に遅いので、これをやりたくありませんでした。その結果、私の解決策を見てみたいかもしれません。 –

2

は、私は、トランザクションがコミットされた後に送られた私のメッセージを望んで(私はRabbitMQのTXを使用することもできましたが、それらは、むしろ遅い)という同様の問題のためにした、より完全なソリューションです。あなたが実際にあなたがコミットした後に何かをする方法を誰かが表示されますよう願っ

@Override 
public void postAfterCommit(Object o) { 
    Optional<MessageBusResourceHolder> holder = MessageBusTxUtils.getTransactionalResourceHolder(this); 
    if (holder.isPresent()) { 
     holder.get().addMessage(o); 
    } 
    else { 
     post(o, false); 
    } 
} 

長いったらしいコーディングサンプルのため申し訳ありませんが行いますメッセージを送る今すぐあなたのクラスで

public class MessageBusUtils { 
    public static Optional<MessageBusResourceHolder> getTransactionalResourceHolder(TxMessageBus messageBus) { 

     if (! TransactionSynchronizationManager.isActualTransactionActive()) { 
      return Optional.absent(); 
     } 

     MessageBusResourceHolder o = (MessageBusResourceHolder) TransactionSynchronizationManager.getResource(messageBus); 
     if (o != null) return Optional.of(o); 

     o = new MessageBusResourceHolder(); 
     TransactionSynchronizationManager.bindResource(messageBus, o); 
     o.setSynchronizedWithTransaction(true); 
     if (TransactionSynchronizationManager.isSynchronizationActive()) { 
      TransactionSynchronizationManager.registerSynchronization(new MessageBusResourceSynchronization(o, messageBus)); 
     } 
     return Optional.of(o); 

    } 

    private static class MessageBusResourceSynchronization extends ResourceHolderSynchronization<MessageBusResourceHolder, TxMessageBus> { 
     private final TxMessageBus messageBus; 
     private final MessageBusResourceHolder holder; 

     public MessageBusResourceSynchronization(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey) { 
      super(resourceHolder, resourceKey); 
      this.messageBus = resourceKey; 
      this.holder = resourceHolder; 
     } 


     @Override 
     protected void cleanupResource(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey, 
       boolean committed) { 
      resourceHolder.getPendingMessages().clear(); 
     } 

     @Override 
     public void afterCompletion(int status) { 
      if (status == TransactionSynchronization.STATUS_COMMITTED) { 
       for (Object o : holder.getPendingMessages()) { 
        messageBus.post(o, false); 
       } 
      } 
      else { 
       holder.getPendingMessages().clear(); 
      } 
      super.afterCompletion(status); 
     } 


    } 
} 

public class MessageBusResourceHolder extends ResourceHolderSupport { 

    private List<Object> pendingMessages = Lists.newArrayList(); 

    public void addMessage(Object message) { 
     pendingMessages.add(message); 
    } 


    protected List<Object> getPendingMessages() { 
     return pendingMessages; 
    } 

} 

+0

私はあなたのコードを使用して、トランザクションがコミットされた後に3つのsolrインデックスを更新しています。また、Springイベントを使用してBusiness Objectsの変更を検出し、ResourceHolderにイベントを格納し、非同期に更新する必要があるインデックスを更新します。 –

0

コミットとロールバックの方法でトランザクションマネージャをオーバーライドすることは意味があります。最初はsuper.commit()と呼んでください。

関連する問題