5.4.7. AOP 示例
现在您已经了解了所有组成部分的工作原理,我们可以将它们组合在一起做一些有用的事情。
由于并发问题(例如,死锁失败者),业务服务的执行有时会失败。如果该操作被重试,则很可能在下一次尝试时成功。对于在这种情况下适合重试的业务服务(不需要返回给用户解决冲突的幂等操作),我们希望透明地重试操作以避免客户端看到 PessimisticLockingFailureException
. 这是一个明确跨越服务层中多个服务的要求,因此非常适合通过切面实现。
因为我们要重试操作,所以我们需要使用环绕通知,以便我们可以多次调用proceed
。以下清单显示了基本切面的实现:
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around("com.xyz.myapp.CommonPointcuts.businessService()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
请注意,切面实现了Ordered
接口,以便我们可以将切面的优先级设置为高于事务通知(我们希望每次重试时都有一个新事务)。maxRetries
和order
属性都是由 Spring 配置的。主要动作doConcurrentOperation
发生在环绕通知中。请注意,目前,我们将重试逻辑应用于每个businessService()
. 我们尝试继续,如果我们失败了PessimisticLockingFailureException
,我们再试一次,除非我们用尽了所有的重试尝试。
对应的Spring配置如下:
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
为了改进切面以使其仅重试幂等操作,我们可以定义以下 Idempotent
注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
// marker annotation
}
然后我们可以使用注解来注解服务操作的实现。对仅重试幂等操作切面的更改涉及改进切入点表达式,以便仅@Idempotent
操作匹配,如下所示:
@Around("com.xyz.myapp.CommonPointcuts.businessService() && " +
"@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
}
最后更新于
这有帮助吗?