自动注册 XA Resource Spring Boot

Automatically register XA Resource Spring Boot

我正在尝试在我的 Spring 启动应用程序中跨 Hazelcast 和 JPA 实现 XA 事务,持久保存到 PostgreSQL。将 Atomikos Spring Boot starter 放入我的 pom.xml 使其加载 JtaTransactionManager 以与 @Transactional 注释一起使用,但 Hazelcast XA 资源未被事务征用。

如何让 Spring 引导自动使用 JTA UserTransaction 作为使用 JtaTransactionManager 的 AOP 事务拦截器的一部分征用我的 XA 资源?

我通过使用注解和 AspectJ Aspect 来解决这个问题,如 here. Also see this 所述,用于定义切入点以匹配 class 或方法级注解,您可能需要这样做:

@EnableTransactionManagement(order = Ordered.HIGHEST_PRECEDENCE) 

在调用此代码之前让事务拦截器发生。

@Aspect
@Component
public class XAResourceAspect {
  @Autowired
  JtaTransactionManager jtaTransactionManager;

  @Autowired
  ApplicationContext applicationContext;

  @Pointcut("within(@XAResource *)")
  public void beanAnnotatedWithAnnotation() {}

  @Pointcut("execution(public * *(..))")
  public void publicMethod() {}

 @Pointcut("publicMethod() && beanAnnotatedWithAnnotation()")
  public void publicMethodInsideAnnotatedClass() {}

  private ThreadLocal<Map<Transaction, Set<String>>> enlistedResources = new ThreadLocal<>();

  @Around("@annotation(ppi.nestup.v3.annotation.XAResource) || publicMethodInsideAnnotatedClass()")
  public Object enlistResources(ProceedingJoinPoint joinPoint) throws Throwable {
    boolean setThreadLocal = false;
    Transaction transaction = jtaTransactionManager.getTransactionManager().getTransaction();
    if (transaction != null) {
      Map<Transaction, Set<String>> transactionMap = enlistedResources.get();
      LOG.info("Enlisting resources for joinpoint " + joinPoint + " and transaction " + transaction);
      if (transactionMap == null) {
        transactionMap = new HashMap<>();
        enlistedResources.set(transactionMap);
        setThreadLocal = true;
        LOG.info("Created new ThreadLocal for transaction " + transaction);
      } else {
        LOG.info("Found existing ThreadLocal " + transactionMap);
      }
      transactionMap.computeIfAbsent(transaction, k -> new HashSet<>());
      MethodSignature signature = (MethodSignature) joinPoint.getSignature();
      Method method = signature.getMethod();
      Class withinType = joinPoint.getSourceLocation().getWithinType();

      XAResource annotation = method.getAnnotation(XAResource.class);
      if (annotation == null) {
        annotation = (XAResource) withinType.getAnnotation(XAResource.class);
      }
      String[] resourceNames = annotation.value();
      for (String name : resourceNames) {
        if (!transactionMap.get(transaction).contains(name)) {
          javax.transaction.xa.XAResource resource =
            (javax.transaction.xa.XAResource) applicationContext.getBean(name);
          try {
            transaction.enlistResource(resource);
          } catch (IllegalStateException e) {
            LOG.error("Caught exception trying to enlist resource " + name + " for transaction " + transaction + " and joinpoint " + joinPoint);
            e.printStackTrace();
          }
          transactionMap.get(transaction).add(name);
        }
      }
    }

    Object proceed = joinPoint.proceed();
    if (setThreadLocal) {
      LOG.info("Removing threadlocal");
      enlistedResources.remove();
    }
    return proceed;
  }
}

我还没有对此做很多测试,但到目前为止它是有效的。