Безопасно ли применять более одного аспекта?
Предположим, что у нас есть два аспекта:
public aspect TestAspect {
pointcut publicMethodExecuted(): execution(public !static * *(..));
int around() : publicMethodExecuted() {
System.out.println("Test string");
Object[] args = thisJoinPoint.getArgs();
System.out.println(args[0]);
int original_return_value = proceed();
return original_return_value * 100;
}
}
public aspect AnotherTestAspect {
pointcut anotherPublicMethodExecuted(): execution(public !static * *(..));
int around() : anotherPublicMethodExecuted() {
System.out.println("Another Test string");
Object[] args = thisJoinPoint.getArgs();
System.out.println(args[0]);
}
}
Как вы можете видеть, эти два аспекта имеют абсолютно одинаковый смысл. Поэтому они перехватывают один и тот же вызов метода. Насколько это безопасно?
Пожалуйста, не предлагайте мне ставить все это в один и тот же аспект. Я спрашиваю о AspectJ
принципы работы и потенциальные возможности.
2 ответа
Да.
Предположим, у вас есть этот класс:
public class TestClass
{
public static void main( String[] args )
{
new TestClass().print();
}
public void print()
{
System.out.println("Hello World");
}
}
И эти аспекты:
public aspect FirstAspect
{
public pointcut hello1() : execution(void TestClass.print());
void around() : hello1() {
System.out.println("first");
proceed();
System.out.println("first after");
}
}
public aspect SecondAspect
{
public pointcut hello2() : execution(void TestClass.print());
void around() : hello2() {
System.out.println("second");
proceed();
System.out.println("second after");
}
}
Когда вы запускаете, получается следующий вывод:
first
second
Hello World
second after
first after
Ответ прост: да, но...
Проще говоря, AspectJ делает в основном переход от точки к точке и поиск, как сплести его, а затем, ну, вплетает. В случае двух точечных копий к одному и тому же методу, одна из точечных копий будет соткать, а затем другая соткать и будет в основном отменять это первое переплетение. Если второй pointcut продолжается!
Позвольте мне объяснить это на коротком примере. Предположим, у вас есть следующий класс:
public class TestClass {
public static void main(String... args) {
new TestClass().x();
}
public void x() {
System.out.println("Hello, world!");
}
}
Теперь давайте определим аспект:
public aspect FirstAspect {
public pointcut hello1() : execution(void mypackage.TestClass.x());
void around() : hello1() {
System.out.println("first");
}
}
Что произойдет, это то, что аспект будет плести метод x
, в основном замените вызов x на метод, созданный AspectJ.
Теперь давайте предположим, что у нас есть другой аспект:
public aspect SecondAspect {
public pointcut hello2() : execution(void mypackage.TestClass.x());
void around() : hello2() {
System.out.println("second");
}
}
С потерей общности и чтобы быть последовательным, давайте предположим, что FirstAspect
сплетен первым. На тот момент мы получили тот же результат, что и раньше. Но теперь AspectJ продолжается и сплетается SecondAspect
, Он находит метод x() и заменяет его выполнение (или вызов, в зависимости от типа pointcut) новым методом, эффективно переопределяя предыдущее переплетение и оставляя его устаревшим (как ни странно, первый сотканный код остается, но бесполезен).
Однако если SecondAspect
Звонки proceed()
в любой момент часть, на которую он пойдет, на самом деле будет первым переплетением. Это потому, что, поскольку это было сплетено, это то, что x делал в отношении AspectJ.
Попробуйте это. Вы увидите, что на самом деле оставлено только одно из меток: вы получите только один отпечаток: либо first
или же second
но не оба, если вы не продолжите, как таковой:
System.out.println("second");
proceed();
Даже не важно когда ты proceed()
До тех пор, пока вы делаете:
proceed();
System.out.println("second");
Если вы хотите убедиться, какой точечный вырез сплетен первым, вы можете посмотреть на AspectJ precedence declaration
,