Проблемы AspectJ с использованием рекомендаций вокруг и ProceedingJoinPoint

Я новичок в АОП, и мне нужно использовать AspectJ в моем проекте. Мне нужно использовать советы, но у меня есть проблема с его использованием, у меня есть следующий код в моем классе .aj,

pointcut checkUser(ProceedingJoinPoint jp,User user): call(* com.example.UserAccount.MyUI.checkUser(..))&& args(jp,user);

void around(ProceedingJoinPoint jp,User user) throws Throwable : checkUser(jp,user){
    // Condition checks one of the user boolean property
    if(condition){
        jp.proceed();
    }else{
        // Do nothing
    }   
}

но я получаю это предупреждение все время,

advice defined in Aspects.UserAccount has not been applied [Xlint:adviceDidNotMatch]

Кстати, я пробовал без ProceedingJoinPoint и пробовал только proceed();, но потом получил это предупреждение, too few arguments to proceed, expected 1

Я благодарен за любую помощь или подсказку!

Реза


person Reza P.    schedule 22.11.2018    source источник


Ответы (1)


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

Вам не нужно привязывать параметр точки соединения в собственном синтаксисе, потому что он существует неявно и автоматически. Автоматически привязываемая точка соединения всегда называется thisJoinPoint, как наверняка показано во всех учебниках. Только в синтаксисе на основе аннотаций вам нужно привязать точку соединения и назвать ее по своему усмотрению, но даже в этом случае я рекомендую придерживаться thisJoinPoint, потому что тогда рефакторинг от аннотации к собственному синтаксису будет проще, и ваши глаза привыкнут видеть это имя переменной в код вашего аспекта.

Предупреждение, которое вы получаете, означает, что определенный вами pointcut не соответствует какой-либо части вашего кода, по крайней мере, ни одной части, которая видна ткачу аспектов или компилятору. Может быть много причин, почему это может произойти, например. неправильное написание имен пакетов или классов, неправильный тип возвращаемого значения совета (тип возвращаемого значения должен быть Object для непустых методов или, более конкретно, соответствовать тому, что возвращает метод, который вы хотите перехватить). Предполагая, что, например. checkUser(..) возвращает boolean, совет вокруг должен делать то же самое. Я составил пример, используя ваши имена пакетов и классов. Кроме того, имена пакетов должны быть строчными, но я использовал ваши, предполагая, что это действительно имена пакетов, а не внутренние классы:

Вспомогательный класс:

package com.example.UserAccount;

public class User {
  private String name;

  public User(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public String toString() {
    return "User(" + name + ")";
  }
}

Класс, ориентированный на аспект + пример основного метода:

package com.example.UserAccount;

public class MyUI {
  public boolean checkUser(User user) {
    return user.getName().toUpperCase().contains("ADMIN");
  }

  public static void main(String[] args) {
    MyUI ui = new MyUI();
    System.out.println(ui.checkUser(new User("Administrator")));
    System.out.println(ui.checkUser(new User("john")));
    System.out.println(ui.checkUser(new User("xander")));
    System.out.println(ui.checkUser(new User("admiral")));
    System.out.println(ui.checkUser(new User("SySaDmiN")));
  }
}

Как видите, мы ожидаем, что для первой и последней записи будет вывод «истина», а для промежуточных — «ложь» из-за логики проверки, которую я составил для checkUser(..).

Теперь давайте напишем аспект, который также возвращает «true» для пользователя с именем «Xander», например. для того, чтобы дать ему права администратора или что-то еще. Я придумываю это, потому что вы не предоставили MCVE, как вы всегда должны делать в StackOverflow, а просто бессвязный фрагмент кода, который держит всех пытаясь ответить на ваш вопрос, угадывая, какого черта вы хотите достичь и как воспроизвести вашу проблему.

Аспект:

package Aspects;

import com.example.UserAccount.User;
import com.example.UserAccount.MyUI;

public aspect UserAccount {
  pointcut checkUser(User user) :
    execution(boolean MyUI.checkUser(*)) && args(user);

  boolean around(User user) : checkUser(user) {
    System.out.println(thisJoinPoint + " -> " + user);
    if (user.getName().equalsIgnoreCase("xander"))
      return true;
    return proceed(user);
  }
}

Я только что импортировал класс MyUI, поэтому здесь нет необходимости использовать полное имя класса. Опять же, это преимущество собственного синтаксиса, в синтаксисе на основе аннотаций вам придется использовать полное имя.

Я также заменил общий * MyUI.checkUser(..) (который также будет работать) на более явный boolean MyUI.checkUser(*), потому что мы уже знаем, что метод возвращает логическое значение и имеет ровно один параметр, который мы в любом случае предполагаем, возвращая логическое значение из совета вокруг и связывая точно один параметр через args(). Вы также можете быть более конкретным и использовать boolean MyUI.checkUser(User).

Кроме того, я использую execution(), а не call(), потому что это более эффективно, так как код совета вплетается только в исполняемый метод один раз вместо пяти раз для каждого вызова метода в основном методе. Вам нужно использовать call() только в том случае, если класс MyUI находится вне досягаемости ткача/компилятора AspectJ, то есть потому, что он не находится в модуле, который вы компилируете с помощью AspectJ Maven.

Журнал консоли:

execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(Administrator)
true
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(john)
false
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(xander)
true
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(admiral)
false
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(SySaDmiN)
true

И вуаля, аспект работает. Это заставляет целевой метод возвращать «true» для пользователя «xander».

person kriegaex    schedule 25.11.2018
comment
Большое спасибо, @kriegaex за ваш ответ. Я обязательно должен прочитать документацию AspectJ! Ваш ответ помог мне понять, как я должен использовать советы вокруг. - person Reza P.; 28.11.2018