Исключения, не перехваченные внутри @Around (AspectJ)

Моя идея состоит в том, чтобы использовать AspectJ для перехвата исключений в аннотированных методах, и если возникнут какие-либо исключения, аннотированный метод должен попытаться запуститься снова. В основном я следовал этому руководству (http://zoftware.blogspot.cz/2008/02/using-aspectj-and-java-annotations-to_23.html), но я не могу заставить его работать. Все должно быть хорошо, но это не так. Исключения перехватываются раньше, чем finally, и возникает много исключений, а не одно. Поймать внутри моего аспекта, кажется, вообще не работает. Я использую AspectJ 1.7.3. Код...

Аннотация:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryIfFailed {

    int maxRetries() default 5;
}

Аннотированный метод:

@RetryIfFailed(maxRetries = 3)
private User showUser(String userName) {
    try {
        return twitter.showUser(userName);
    } catch (TwitterException e) {
        System.out.println("I am catch inside showUser");
    }
    return null;
}

Аспект:

    @Around("call(@RetryIfFailed * *..*(..))")
    public Object retryMaxRetriesTimes(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println("Entering retryMax...");
        Method method = ((MethodSignature) thisJoinPoint.getSignature()).getMethod();
        RetryIfFailed annotation = method.getAnnotation(RetryIfFailed.class);
        int retries = annotation.maxRetries();

        Object ret = null;
        while (retries > 0) {
            try {
                System.out.println("Before proceeding... Retries=" + retries);
                ret = thisJoinPoint.proceed();
            } catch (Throwable e) {
                System.out.println("I am catched in RetryMax ");
                retries--;
                if (retries == 0) {
                    System.out.println("Exception caught. Rethrowing..." + e);
                    throw new ConnectionErrorException("Twitter service failed to establish connection", e);
                }
            } finally {
                System.out.println("Finally block..." + retries);
                if (ret != null) {
                    System.out.println("Object returned: " + ret);
                    return ret;
                }

                System.out.println("Decresing retries to" + retries);
                retries--;
                if (retries == 0) {
                    throw new ConnectionErrorException("It should not get here.");
                }
            }
        }

        //should never be reached
        return null;
    }
}

Конфигурация Мавена:

<!-- Build with AspectJ-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.5</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <complianceLevel>1.7</complianceLevel>
                    <verbose>true</verbose>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                </dependencies>
            </plugin>
        </plugins>
    </build>

Вывод:

Entering retryMax...
Before proceeding... Retries=3
I am catch inside showUser
Finally block...3
Decresing retries to3
Before proceeding... Retries=2
I am catch inside showUser
Finally block...2
Decresing retries to2
Before proceeding... Retries=1
I am catch inside showUser
Finally block...1
Decresing retries to1
Exception in thread "main" ...<path>....ConnectionErrorException: It should not get here.
       at ...<stackTrace follows>...

Спасибо за любой совет :).

ИЗМЕНИТЬ

Как предложил mvieghofer, я никогда не выбрасываю исключение повторно. Я ожидал, что @Around поймает исключение внутри twitter.showUser(), но это было не так. Если кого-то заинтересует решение, вот оно:

    @RetryIfFailed
    public static User showUser(String userName) throws ConnectionErrorException {
        try {
            return twitter.showUser(userName);
        } catch (TwitterException e) {
            throw new ConnectionErrorException(exceptionMessage, e);
        }
    }

person Samuel    schedule 14.11.2013    source источник
comment
код выглядит так, как будто он работает. вы никогда не выбрасываете исключение для твиттера. вы должны использовать afterthrowing и rethrow, когда количество повторных попыток равно 0.   -  person aepurniet    schedule 14.11.2013


Ответы (1)


Для AspectJ есть совет об исключении после броска.

У вас может быть что-то вроде этого:

aspect A {
  pointcut publicCall(): call(@RetryIfFailed * *..*(..));
  after() throwing (TwitterExepction e): publicCall() {
  System.out.println("Threw an exception: " + e);
  }

}

Также вы должны повторно создать исключение TwitterException внутри вашего метода showUser. Для получения дополнительной информации о совете по бросанию after() см. эту ссылку

person mvieghofer    schedule 14.11.2013
comment
Да, но это не проблема. Я выбрасываю ConnectionErrorException, но в моем аннотированном методе выбрасывается TwitterException. И он выбрасывается 3 раза - это в основном означает, что ret = thisJoinPoint.proceed(); действительно выполняется 3 раза, но исключение не перехватывается внутри аспекта (catch (Throwable e)) - person Samuel; 14.11.2013
comment
Я надеюсь, это поможет вам :) - person mvieghofer; 14.11.2013
comment
Нет, но спасибо за попытку :). Повторные попытки в порядке, последний цикл while не произойдет с повторными попытками == -1. Я намеренно выбрасываю исключение, отличное от TwitterException, чтобы посмотреть, как оно себя ведет. Я, вероятно, изменю его в производственном коде. Я смотрел на AfterThrowing раньше, но он все равно выдаст исключение, верно? Есть ли способ поймать исключение, отбросить его и снова запустить метод? - person Samuel; 14.11.2013
comment
На самом деле, когда исключение выбрасывается, а затем перехватывается, но не выбрасывается повторно, оно не появится где-то еще. Если вам нужно такое поведение, исключение необходимо сгенерировать повторно. Однако я не знаю, верно ли это для аспектов. Тем не менее, когда внутри twitter.showUser(userName); выбрасывается исключение TwitterException, оно перехватывается вашим методом showUser и никогда не достигнет какого-либо другого метода (поскольку вы его не выбрасываете повторно). - person mvieghofer; 14.11.2013
comment
Да, я понимаю, как работают исключения, но можно ли создать исключение в twitter.showUser, перехватить его в аспекте J и отбросить, чтобы оно не было перехвачено моим методом showUser? - person Samuel; 14.11.2013
comment
Я изучил это подробнее, и на самом деле вы были правы. Я сделал логическую ошибку. Я ожидал поймать свое исключение внутри twitter.showUser, но вместо этого я перехватил его внутри своего собственного showUser. Поскольку вы указали мне правильное направление, я отмечу вас как правильный ответ, просто удалите часть о повторных попытках, пожалуйста, это не имеет отношения к вопросу :). Большое тебе спасибо! :) - person Samuel; 14.11.2013