Определение того, какой метод вызвал аспект - PullRequest
2 голосов
/ 15 апреля 2011

Мне было интересно, есть ли способ определить, какой метод был активен, когда этот аспект был запущен. Я нашел метод JointPoint.getSourceLocation (), который возвращает строку исходного кода. Я понял, что мог бы попытаться разобрать этот исходный файл и попытаться определить метод из этого ... но кажется, что должен быть лучший способ.

В основном, если имеет следующий код:

 class Monkey{
      public void feed( Banana b ){
           b.eat()
      }
 }
 class Banana{
    private static int bananaIdGen;
    public final int bananaId = ++bananaIdGen;
    private boolean eaten = false;

    public void eat(){
         if( eaten )
              throw IllegalStateException( "Already eaten." );
         else
              eaten = true;
    }
 }

Я бы хотел иметь аспект, подобный

 @After("call(void Banana.eat()) && target(bbb)") 
 public void whereEaten( Banana bbb ){
      ....
 }

Где в теле я мог распечатать что-то вроде `" Банан 47, съеденный org.example.Monkey (org.example.Banana) ".

Причина в том, что я хотел бы выдать ошибку, если метод был вызван методом без определенной аннотации. Для этого мне нужно иметь метод метода.

1 Ответ

2 голосов
/ 15 апреля 2011

Полагаю, этот вопрос с его thisEnclosingJoinPointStaticPart может вам помочь.

Но лучший способ решить вашу проблему - это построить правильную точку соединения, используя withincode и call pointcut, как в примере здесь (см. Contract Enforcement раздел). Таким образом вы предотвращаете вызовы методов с определенной аннотацией или без нее.

Список точек доступен здесь здесь .

Что касается вашего примера кода, давайте введем аннотацию:

package com.riapriority.test;

public @interface CanEat {
}

Наш Banana класс:

package com.riapriority.test;

public class Banana {
    private static int bananaIdGen;
    private final int bananaId = ++bananaIdGen;
    private boolean eaten = false;

    public void eat() {
        if (eaten)
            throw new IllegalStateException("Already eaten.");
        else
            eaten = true;
    }

    public int getBananaId() {
        return bananaId;
    }
}

Наш Monkey класс с соответствующей аннотацией:

package com.riapriority.test;

public class Monkey {
    @CanEat
    public void feed(Banana b) {
        b.eat();
    }
}

Наш Airplane класс, который, конечно, не может есть и поэтому не имеет @CanEat аннотации:

package com.riapriority.test;

public class Airplane {
    public void feed(Banana b) {
        b.eat();
    }
}

Наш простой основной класс для тестирования:

package com.riapriority.test;

public class WithincodeTest {
    public static void main(String[] args) {
        Banana monkeyBanana = new Banana();
        Monkey monkey = new Monkey();
        monkey.feed(monkeyBanana);
        try {
            monkey.feed(monkeyBanana);
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
        Banana airplaneBanana = new Banana();
        Airplane airplane = new Airplane();
        try {
            airplane.feed(airplaneBanana);
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
    }
}

Так что нам нужно избегать употребления бананов самолетом. И соответствующий аспект для получения этого:

package com.riapriority.test;

import java.text.MessageFormat;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EatingAspect {
    @Pointcut("call(void Banana.eat()) && target(banana)")
    public void eatCall(Banana banana) {
    }

    @Pointcut("@withincode(CanEat)")
    public void canEat() {
    }

    @AfterReturning("eatCall(banana) && canEat()")
    public void whereEaten(Banana banana,
                           JoinPoint.EnclosingStaticPart thisEnclosingStaticPart) {
        System.out.println(MessageFormat.format("Banana {0} eaten by {1}", banana.getBananaId(),
                thisEnclosingStaticPart.getSignature()));
    }

    @Before("eatCall(banana) && !canEat()")
    public void forbidEating(Banana banana,
                             JoinPoint.EnclosingStaticPart thisEnclosingStaticPart) {
        throw new IllegalStateException(MessageFormat.format("Can''t eat {0} by {1}", banana.getBananaId(),
                thisEnclosingStaticPart.getSignature()));
    }
}

Итак, теперь наш основной тестовый класс выдает правильный вывод:

Banana 1 eaten by void com.riapriority.test.Monkey.feed(Banana)
Already eaten.
Can't eat 2 by void com.riapriority.test.Airplane.feed(Banana)

Надеюсь, это решит вашу проблему.

...