В любом случае, чтобы создать pointcut для методов членов класса?

Имеется ли класс с множеством членов, каждый из которых имеет свои собственные методы getter/setter/etc, есть ли способ создания pointcut, который будет запускаться только для методов членов, если он содержится в родительском классе?

Например:

public MyClass{
   List myList = new ArrayList<String>();
}

Если я хочу создать pointcut для рекомендации myList.add(), есть ли способ сделать это? Я не хочу советовать все вызовы ArrayList.add(). Только для Collections.add(), которые являются членами MyClass.

Я пытался поиграть с within а также cflow, но безрезультатно:

pointcut addPointcut() : cflow( execution( * *.getMyList() ) ) && call( * *.add(..));

но это не похоже на работу. Я предполагаю, что, учитывая, что add() вызовы на самом деле не являются частью потока управления get(), похоже, он не запускается должным образом.

После еще одной игры, я заметил, что работает следующее решение:

pointcut addPointcut(): within( MyClass ) && call( * *.add(..) );

Это правильная реализация?

Я пытался ограничить pointcut только советами вызывать add () при передаче объекта @Entity, но он не работает. Пример:

pointcut addEntityPointcut(): within( MyClass ) && call( * *.add(@javax.persistence.Entity *) );

и все же addPointcut() работает при вызове с параметром @Entity.

Тип аргумента, основанный на фактическом вызывающем методе, или основанный на add() подпись?

РЕДАКТИРОВАТЬ

Я был слишком быстр, чтобы сделать неверный вывод. После сна я пришел к выводу, что мой pointcut не будет работать.

public class FirstClass{
   List<String> strings = new ArrayList<>();
   // getters and setters
}

public class Execute{

    public main(){
      FirstClass fc = new FirstClass();
      fc.getStrings().add( "This call is advised" );   // <---- Is there any way to advise this add() method?

      List<String> l = new ArrayList<>();
      l.add( "This call is not advised" );   // <---- this one should not be advised
    }
}

Я ищу способ посоветовать метод add (), вызываемый из любого класса. Тем не менее, я только хочу посоветовать метод add () в списке членов, содержащемся в FirstClass, даже если он вызывается извне FirstClass.

1 ответ

Решение

Тип аргумента, основанный на фактическом вызывающем методе, или основанный на add() подпись?

В AspectJ для call() pointcut вам нужно указать сигнатуры метода или конструктора. add() Метод в этом случае не имеет никаких параметров, аннотированных @EntityТаким образом, то, что вы пытаетесь сделать, не работает. Это обходной путь с использованием отражения:

Образец аннотации:

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {}

Образец объекта:

package de.scrum_master.app;

@Entity
public class MyEntity {}

Приложение для водителя:

package de.scrum_master.app;

import java.util.ArrayList;
import java.util.List;

public class Application {
    List<Object> myList = new ArrayList<>();

    public static void main(String[] args) {
        Application application = new Application();
        application.myList.add("foo");
        application.myList.add(new MyEntity());
        application.myList.add("bar");
        application.myList.add(new MyEntity());
    }
}

аспект:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;

public aspect EntityAddInterceptor {
    pointcut addEntity(Object addedObject) :
        within(Application) && call(* *.add(*)) && args(addedObject);

    before(Object addedObject) : addEntity(addedObject) {
        if (addedObject.getClass().isAnnotationPresent(Entity.class))
            System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
    }
}

Выход:

call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@19dc6592
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@54906181

Что касается варианта согласования потока управления, я думаю, что с точки зрения именования имеет смысл предположить, что getMyList() ничего не добавляет, а просто возвращает список. Вероятно, вы скорее делаете что-то вроде application.getMyList().add("foo")и в этом случае add() действительно вне (после) потока управления getMyList() потому что он действует на свой результат.

Если OTOH у вас есть гипотетический метод addToList(Object element) который действительно зовет add() ты можешь использовать cflow(), Давайте изменим пример кода:

Модифицированный драйвер приложения:

package de.scrum_master.app;

import java.util.ArrayList;
import java.util.List;

public class Application {
    List<Object> myList = new ArrayList<>();

    public void addToMyList(Object element) { reallyAddToMyList(element); }
    private void reallyAddToMyList(Object element) { myList.add(element); }

    public static void main(String[] args) {
        Application application = new Application();
        application.myList.add("foo");
        application.myList.add(new MyEntity());
        application.addToMyList("bar");
        application.addToMyList(new MyEntity());
    }
}

Модифицированный аспект:

package de.scrum_master.aspect;

import de.scrum_master.app.Entity;

public aspect EntityAddInterceptor {
    pointcut addEntity(Object addedObject) :
        cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject));

    before(Object addedObject) : addEntity(addedObject) {
        if (addedObject.getClass().isAnnotationPresent(Entity.class))
            System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
    }
}

Новый вывод:

call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@323ba00

Как видите, зарегистрирован только один звонок. Это один из reallyAddToMyList()а не тот из main(),


Обновление 2014-07-21 - лучшая модификация аспекта:

Авторы этого более элегантного решения получают Энди Клемент (сопровождающий AspectJ), который упомянул об этом в списке рассылки AspectJ. Он показывает оба моих варианта сверху, но использует && @args(Entity) вместо if (addedObject.getClass().isAnnotationPresent(Entity.class)):

package de.scrum_master.aspect;

import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;

public aspect EntityAddInterceptor {
    pointcut addEntity(Object addedObject) :
        within(Application) && call(* *.add(*)) && args(addedObject) && @args(Entity);

    before(Object addedObject) : addEntity(addedObject) {
        System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
    }

    pointcut addEntitySpecial(Object addedObject) :
        cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject))  && @args(Entity);

    before(Object addedObject) : addEntitySpecial(addedObject) {
        System.out.println(thisJoinPointStaticPart + " -> " + addedObject + " [special]");
    }
}

Вывод с активными обоими вариантами выглядит так:

call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@229ff6d1
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity@1976bf9e [special]
Другие вопросы по тегам