# 5.4.7. AOP 示例

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

由于并发问题（例如，死锁失败者），业务服务的执行有时会失败。如果该操作被重试，则很可能在下一次尝试时成功。对于在这种情况下适合重试的业务服务（不需要返回给用户解决冲突的幂等操作），我们希望透明地重试操作以避免客户端看到 `PessimisticLockingFailureException`. 这是一个明确跨越服务层中多个服务的要求，因此非常适合通过切面实现。

因为我们要重试操作，所以我们需要使用环绕通知，以便我们可以多次调用`proceed`。以下清单显示了基本切面的实现：

```java
@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配置如下：

```xml
<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`注解：

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

然后我们可以使用注解来注解服务操作的实现。对仅重试幂等操作切面的更改涉及改进切入点表达式，以便仅`@Idempotent`操作匹配，如下所示：

```java
@Around("com.xyz.myapp.CommonPointcuts.businessService() && " +
        "@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
    // ...
}
```
