自动注册 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;
}
}
我还没有对此做很多测试,但到目前为止它是有效的。
我正在尝试在我的 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;
}
}
我还没有对此做很多测试,但到目前为止它是有效的。