跳到主要内容
版本:7.0.3

一个AOP示例

Hunyuan 7b 中英对照 An AOP Example

现在你已经了解了所有组成部分的工作原理,我们可以把它们组合起来,做一些有用的事情了。

业务服务的执行有时会因为并发问题而失败(例如,死锁)。如果重新尝试操作,下次很可能会成功。对于在这种条件下适合重试的业务服务(不需要返回给用户进行冲突解决的身份验证操作),我们希望能够透明地重试该操作,以避免客户端看到PessimisticLockingFailureException异常。这是一个明显涉及服务层中多个服务的要求,因此,通过切面实现这一功能是理想的选择。

因为我们需要重试该操作,所以必须使用某种机制(比如“around advice”),以便能够多次调用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.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;
}
}

@Around("com.xyz.CommonPointcuts.businessService()") 引用了在 共享命名切点定义 中定义的名为 businessService 的切点。

请注意,该切面实现了Ordered接口,这样我们就可以将切面的优先级设置得高于事务建议(每次重试时我们都希望有一个新的事务)。maxRetriesorder属性都是由Spring配置的。主要操作发生在doConcurrentOperation方法中,该方法位于事务建议(advice)的周围。需要注意的是,目前我们将重试逻辑应用到每一个businessService上。我们尝试继续执行操作,如果遇到PessimisticLockingFailureException异常导致失败,我们会再次尝试,除非我们已经用完了所有的重试次数。

相应的Spring配置如下:

@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfiguration {

@Bean
public ConcurrentOperationExecutor concurrentOperationExecutor() {
ConcurrentOperationExecutor executor = new ConcurrentOperationExecutor();
executor.setMaxRetries(3);
executor.setOrder(100);
return executor;

}
}

为了进一步完善这一机制,使其仅重试幂等操作,我们可以定义以下Idempotent注解:

@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}

然后我们可以使用注释来标注服务操作的实现。将切面(aspect)修改为仅重试幂等操作(idempotent operations),需要精炼点切(pointcut)表达式,以便只有带有@Idempotent注解的操作才能匹配,具体如下:

@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
return pjp.proceed(pjp.getArgs());
}