如何在 Spring Boot aop @Around 函数中创建事务?
How to create transaction in Spring Boot aop @Around function?
我想使用Spring Boot AOP 来实现授权方法。最初的想法是,如果来自 REST 调用的 return 对象 return 没有通过授权检查,它将抛出一个未经授权的异常。
我是这样做的:
@Aspect
@Component
public class AuthAspect {
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
但是,问题是,如果此 REST 服务将对我的数据库执行一些插入或更新,它将在我的授权检查之前提交。因此,UnauthException
将被抛出,但事务仍会提交。
第一次尝试我想在proceed()
调用之前手动创建事务并在return之前提交,但是失败了。
@Aspect
@Component
public class AuthAspect {
private final EntityManager em;
@Autowired
public AuthAspect(EntityManager em) {
this.em = em;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
em.getTransaction().begin();
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
em.getTransaction().commit();
return returnObject;
}
}
它会导致java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
。
我在网上搜索了一些答案需要修改web.xml
文件,但是我不想用xml来配置
从您使用的标签判断您正在使用 SpringBoot. Spring Boot 提供了一个预配置的 TransactionTemplate
如果您想要手动控制事务,您可以使用它。
而不是 EntityManger
将其注入您的方面并将您的代码包装在其中。
@Aspect
@Component
public class AuthAspect {
private final TransactionTemplate tx;
public AuthAspect(TransactionTemplate tx) {
this.tx = tx;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint pjp) throws Throwable {
return tx.execute(ts -> this.executeAuth(pjp));
}
private Object executeAuth(ProceedingJoinPoint pjp) {
Object returnObject;
try {
returnObject = pjp.proceed();
} catch (Throwable t) {
throw new AopInvocationException(t.getMessage(), t);
}
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
这将执行事务内的逻辑。我将实际逻辑移至一个方法,以便 lambda 可以是单个方法而不是代码块。 (个人preference/best实践)。
查看您的代码,您需要更新一些内容:
如果要调用EntityManager,需要通过以下方式调用,在持久化上下文中使用。
@PersistenceContext
private EntityManager em;
为了在事务上下文中进行查询或其他操作,您只需要在方法头上添加 Transactional 标记并且不要从 EntityManager 调用 getTransaction。
@Transactional
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
em.createNativeQuery(query) //If it is necessary.
...
}
我想使用Spring Boot AOP 来实现授权方法。最初的想法是,如果来自 REST 调用的 return 对象 return 没有通过授权检查,它将抛出一个未经授权的异常。
我是这样做的:
@Aspect
@Component
public class AuthAspect {
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
但是,问题是,如果此 REST 服务将对我的数据库执行一些插入或更新,它将在我的授权检查之前提交。因此,UnauthException
将被抛出,但事务仍会提交。
第一次尝试我想在proceed()
调用之前手动创建事务并在return之前提交,但是失败了。
@Aspect
@Component
public class AuthAspect {
private final EntityManager em;
@Autowired
public AuthAspect(EntityManager em) {
this.em = em;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
em.getTransaction().begin();
Object returnObject = point.proceed();
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
em.getTransaction().commit();
return returnObject;
}
}
它会导致java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
。
我在网上搜索了一些答案需要修改web.xml
文件,但是我不想用xml来配置
从您使用的标签判断您正在使用 SpringBoot. Spring Boot 提供了一个预配置的 TransactionTemplate
如果您想要手动控制事务,您可以使用它。
而不是 EntityManger
将其注入您的方面并将您的代码包装在其中。
@Aspect
@Component
public class AuthAspect {
private final TransactionTemplate tx;
public AuthAspect(TransactionTemplate tx) {
this.tx = tx;
}
@Around("AllRestExecPoint()")
public Object auth(ProceedingJoinPoint pjp) throws Throwable {
return tx.execute(ts -> this.executeAuth(pjp));
}
private Object executeAuth(ProceedingJoinPoint pjp) {
Object returnObject;
try {
returnObject = pjp.proceed();
} catch (Throwable t) {
throw new AopInvocationException(t.getMessage(), t);
}
if (!checkAuthorization(returnObject)) {
throw new UnauthException();
}
return returnObject;
}
}
这将执行事务内的逻辑。我将实际逻辑移至一个方法,以便 lambda 可以是单个方法而不是代码块。 (个人preference/best实践)。
查看您的代码,您需要更新一些内容:
如果要调用EntityManager,需要通过以下方式调用,在持久化上下文中使用。
@PersistenceContext private EntityManager em;
为了在事务上下文中进行查询或其他操作,您只需要在方法头上添加 Transactional 标记并且不要从 EntityManager 调用 getTransaction。
@Transactional @Around("AllRestExecPoint()") public Object auth(ProceedingJoinPoint point) throws Throwable { em.createNativeQuery(query) //If it is necessary. ... }