Как задать значение свойства с помощью пользовательской аннотации Java и Spring AOP?

0

Вопрос

Я хотел бы использовать пользовательскую аннотацию Java для вставки значения в свойство частного класса с помощью Spring AOP (и/или AspectJ). Краткий пример:

MyAnnotation.java:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface MyAnnotation {
}

MyController.java:

public class MyControllerImpl implements MyController {

    ...
    
    @MyAnnotation
    private String var1;

    @Override
    public String getVarExample() {
       // imagine this is a REST API that gets called on @GET
       // request and returns a string

       System.out.println(this.var1); // <-- I'd like this to be "helloworld"
                                    // this is just for illustration
                                    // of course, I will want to do 
                                    // something more meaningful with
                                    // the 'var1' variable
       return "ok"; <- unimportant for this example
    }
    ...

MyAspect.java:

@Aspect
@Component
public class MyAspect {

    @Pointcut("@annotation(com.mypackage.annotation.MyAnnotation)")
    public void fieldAnnotatedWithMyAnnotation() {
        
    }

    @Around("fieldAnnotatedWithMyAnnotation()")
    public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
        
        // problem #1 - the program never enters here
        // problem #2 - I need to figure out how to set up the var1 here
        //              to "helloworld" , how?
        return pjp.proceed();
    }
    ...
}

Что бы я хотел, чтобы произошло?

Я позвоню и войду в getVarExample() и после того, как он вернется, я хотел бы увидеть "helloworld" в консоли или журнале. Я хотел бы как-то установить var1 к пользовательскому значению с помощью AOP. Любая переменная свойства, которая будет помечена @MyAnnotation будет установлено значение "адский мир". Я надеюсь, что приведенный выше пример понятен.

Что я пробовал?

Я убедился, что в названиях пакетов нет опечаток, а также повозился с различными аннотациями советов AOP, такими как @Around и @Before. Я также пробовал разные цели в MyAnnotation и все закончилось тем, что ElementType.FIELD что должно быть правильно.

Можете ли вы помочь мне заставить его работать?

Я знаю, что это можно сделать, но не смог найти ни одного рабочего примера в Интернете. Опять же, я хотел бы увидеть 2 ответа:

1. Как заставить точечный разрез срабатывать при входе в MyController? Я хочу поймать точку останова внутри enrichVar1(..) способ проведения MyAspect класс.

2. Как я могу изменить аннотированный var1 значение вenrichVar1(..) способ проведения MyAspect класс?

Я не знаю, что я делаю не так. Любая помощь будет очень признательна. Спасибо!

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

Обновление №1:

Пожалуйста, обратите внимание, что для var1 закрытая переменная. Переменная будет использоваться только в MyControllerImpl. Чтобы лучше проиллюстрировать это, я изменил возвращаемое значение getVarExample.

annotations aspectj java spring
2021-11-22 12:02:25
2

Лучший ответ

3

Как я уже сказал в своем комментарии:

Обозначение точечного разреза @annotation() перехватывает аннотированные методы, а не аннотированные поля. Для этого у native AspectJ есть get() и set(). То есть, точечный разрез также необходимо будет изменить при переходе на AspectJ. Но я согласен, что здесь, вероятно, достаточно придерживаться весеннего AOP и аннотировать методы получения вместо полей.

Но поскольку вы настаиваете на том, что хотите сохранить класс контроллера неизменным, вот собственное решение AspectJ. Пожалуйста, прочитайте главу Использование AspectJ с приложениями Spring, чтобы узнать, как настроить это с помощью @EnableLoadTimeWeaving и параметр JVM -javaagent:/path/to/aspectjweaver.jar.

Чтобы продемонстрировать, что это решение действительно работает независимо от Spring, я вообще не использую классы Spring или аннотации, только POJOs и собственный AspectJ. Вы можете просто сделать то же самое в своем приложении Spring. Пожалуйста, обратите внимание, что собственные аспекты AspectJ не нуждаются @Component аннотации, в отличие от аспектов Spring AOP.

package de.scrum_master.app;

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface MyAnnotation {}
package de.scrum_master.app;

public interface MyController {
  String getVarExample();
}
package de.scrum_master.app;

public class MyControllerImpl implements MyController {
  @MyAnnotation
  private String var1;

  @Override
  public String getVarExample() {
    System.out.println(this.var1);
    return "ok";
  }
}
package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    MyController myController = new MyControllerImpl();
    myController.getVarExample();
  }
}
package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {

  @Pointcut("get(@de.scrum_master.app.MyAnnotation * *)")
  public void fieldAnnotatedWithMyAnnotation() {}

  @Around("fieldAnnotatedWithMyAnnotation()")
  public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println(pjp);
    return "helloworld";
  }
}

При запуске Application, журнал консоли будет:

get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld

В руководстве AspectJ объясняется синтаксис сигнатур получения и установки точек соединения полей и шаблонов полей.


Примечание: Я думаю, что ваш вариант использования может быть взломом, а не допустимым дизайном приложения. Вам следует провести рефакторинг, а не взламывать подобное приложение.

2021-11-24 21:34:25

Я хочу поблагодарить вас! Это то, что я искал, и мне потребовалось очень много экспериментов, чтобы заставить это работать. Я все еще не знаю, как правильно включить его в Spring, но это уже другая тема. Спасибо вам за ваше время, и щедрость ваша!
user3732445
0

Как следует из документов Spring, Spring AOP поддерживает точки соединения выполнения метода Spring beans. Чтобы точки соединения доступа к полю работали, вам нужно использовать серверную часть AspectJ с привязкой времени загрузки для AOP.

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

2021-11-24 20:53:57

Точно. Кстати, обозначение точечного разреза @annotation() перехватывает аннотированные методы, а не аннотированные поля. Для этого у native AspectJ есть get() и set(). То есть, точечный разрез также необходимо будет изменить при переходе на AspectJ. Но я согласен, что здесь, вероятно, достаточно придерживаться весеннего AOP и аннотировать методы получения вместо полей.
kriegaex

@geobreze Я ценю ваш ответ, но это бесполезно. Я сам нашел ссылку, и для моего случая нет никакого рабочего примера.
user3732445

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................