在 Spring 中定义自定义 TransactionInterceptor。 bean 重写为何/如何工作?

发布于 2025-01-16 04:02:13 字数 2408 浏览 4 评论 0 原文

我想要实现的

目标是拥有一个事务 bean,实例化两次(或多次),并且每次使用不同的 TransactionManager (即不同的数据库)。

@Transactional
class MyReusableService {
...
}

@Configuration
class MyConfig {

  // here I want to use `transactionManagerForDb1`
  @Bean
  public MyReusableService myReusableServiceForDb1() { ... }

  // here I want to use `transactionManagerForDb2`
  @Bean
  public MyReusableService myReusableServiceForDb2() { ... }
}

我观察到的

TransactionInterceptor 是委托给 TransactionManager (TM) 的部分。我发现它有两种使用方式:

  1. 通过 @EnableJpaRepositories(... transactionManagerRef="...")。这个机制与我想要的有点相关。但它根本不使用@Transactional。它找到所有的存储库。它为每个存储库实例化一个 TransactionInterceptor

  2. 通过@Transactional。这里有一个 TransactionInterceptor (TI) 的单个实例。在每次调用时,它都会通过查看注释来决定使用哪个 TM(当然使用缓存)。此 TI 定义为 ProxyTransactionManagementConfiguration

我所做的

就是定义我自己的 TI:

@Configuration
public class MyConfiguration {

    @SuppressWarnings("serial")
    public static class MyTransactionInterceptor extends TransactionInterceptor {
        ...
    }
    
    @Bean
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new MyTransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        return interceptor;
    }
    
}

这似乎有效。 Spring定义的TI不再被实例化。我的是。并且它被使用了。

我的问题

  1. 为什么上述方法有效? Spring 如何知道我想要覆盖它是 TI?我原本期望有 2 个 TI,并努力使用正确的 TI,也许使用 @Primary。例如这里
  2. 有没有更干净的方法来实现我的目标?我不能说我完全满意,因为我发现这个解决方案有点侵入性。 MyReusableService 实际上驻留在一个库中。并且强迫用户覆盖 TI 并不是自包含的。

What I want to achieve

My objective is to have a transactional bean, instantiated twice (or more), and each time w/ a different TransactionManager (i.e. a different DB).

@Transactional
class MyReusableService {
...
}

@Configuration
class MyConfig {

  // here I want to use `transactionManagerForDb1`
  @Bean
  public MyReusableService myReusableServiceForDb1() { ... }

  // here I want to use `transactionManagerForDb2`
  @Bean
  public MyReusableService myReusableServiceForDb2() { ... }
}

What I observed

TransactionInterceptor is the piece that delegates to a TransactionManager (TM). I found it used in 2 ways:

  1. Via @EnableJpaRepositories(... transactionManagerRef="..."). This mechanism relates a bit w/ what I want. But it doesn't use @Transactional at all. It finds all the repos. And it instantiates one TransactionInterceptor per repo.

  2. Via @Transactional. Here, there is a single instance of TransactionInterceptor (TI). And at each invocation, it decides which TM to use by looking at the annotations (using caching of course). This TI is defined ProxyTransactionManagementConfiguration.

What I did

I defined my own TI:

@Configuration
public class MyConfiguration {

    @SuppressWarnings("serial")
    public static class MyTransactionInterceptor extends TransactionInterceptor {
        ...
    }
    
    @Bean
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new MyTransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        return interceptor;
    }
    
}

This seem to work. The TI defined by Spring is no more instantiated. Mine is. And it is used.

My questions

  1. Why does the above work? How did Spring know that I wanted to override it's TI? I was expecting to have 2 TIs, and fight to use the right one, maybe using @Primary. E.g. like here.
  2. Is there maybe a cleaner way to achieve my objective? I cannot say I'm entirely satisfied, because I find the solution a bit intrusive. MyReusableService resides actually in a lib. And forcing the user to override the TI is not self contained.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文