自动注册XA Resource Spring Boot

时间:2018-03-21 21:59:38

标签: spring spring-boot hazelcast xa atomikos

我正试图在我的Spring Boot应用程序中实现跨Hazelcast的XA事务,而JPA持续到PostgreSQL。将Atomikos Spring Boot启动器放入我的pom.xml中,可以加载JtaTransactionManager以与@Transactional注释一起使用,但是Hazelcast XA资源没有加入该事务。

如何使用JTA UserTransaction将Spring Boot自动登记到我的XA资源,作为使用JtaTransactionManager的AOP事务拦截器的一部分?

1 个答案:

答案 0 :(得分:0)

我通过使用注释和AspectJ Aspect来解决这个问题,如here所述。另请参阅this以定义与类或方法级别注释匹配的切入点,您可能需要执行此操作:

@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;
  }
}

我还没有对此做过很多测试,但到目前为止它已经开始工作了。