Arhn - архитектура программирования

AspectJ AOP LTW не работает с динамической загрузкой javaagent

Вот мой пример неработающего проекта.

Он содержит 2 модуля:

  • aop-lib - Aspects used as lib. It contains the following classes
    1. Wrap.java - It's the annotation used to attach advice
    2. WrapDef.java — это определение вышеупомянутой аннотации Wrap.
  • aop-app - Uses the above aspect lib
    1. DynamicLoad.java - class to load javaagent dynamically
    2. Main.java — основной класс, использующий аннотацию Wrap.

Структура каталога следующая:

.
├── README.md
├── aop-app
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── aop
│                       └── app
│                           ├── DynamicLoad.java
│                           └── Main.java
└── aop-lib
    ├── pom.xml
    └── src
        └── main
            └── java
                └── com
                    └── aop
                        └── app
                            └── lib
                                ├── Wrap.java
                                └── WrapDef.java

Я пытаюсь использовать библиотеку аспектов aop-lib (библиотека советов AOP) внутри aop-app через Load Time Weaving (LTW) путем динамической загрузки javaagent, как указано в официальные документы. Но это не работает.

Ниже приведено содержимое Wrap.java

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Wrap { }

Ниже приведено содержимое WrapDef.java

@Aspect
public class WrapDef {
    private static final Logger logger = LoggerFactory.getLogger(WrapDef.class);

    public static boolean loaded = false;

    @Around("@annotation( wrapAnnotation ) && execution(* *(..))")
    public Object processSystemRequest(final ProceedingJoinPoint pjp, Wrap wrapAnnotation)
            throws Throwable {
        logger.debug("before wrap");
        Object o = pjp.proceed();
        logger.debug("after wrap");
        return o;
    }

    static {
        System.out.println("Loading");
        WrapDef.loaded = true;
    }
    public static void reportLoaded() {
        System.out.println("loaded : " + loaded);
    }
}

Ниже приведено содержимое Main.java:

public class Main {

    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    @Wrap
    public void myFunc(){
        logger.debug("inside myFunc");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        boolean dynamicLoad = Boolean.getBoolean("dynamicLoad");
        if(dynamicLoad){
            Main.isAdviceClassLoaded();           //To see if WrapDef.java is loaded or not.
            if(!DynamicLoad.isAspectJAgentLoaded()) {
                logger.error("AspectJ Not Loaded. Existing.");
                System.exit(0);
            }
            Main.isAdviceClassLoaded();           //To see if WrapDef.java is loaded or not.
        }

        new Main().myFunc();
    }

    private static void isAdviceClassLoaded() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
        m.setAccessible(true);
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Object test1 = m.invoke(cl, "com.aop.app.lib.WrapDef");
        boolean loaded = test1 != null;
        System.out.println("com.aop.app.lib.WrapDef Loaded : " + loaded);
    }

}

С javaagent в качестве аргумента строки cmd он отлично работает:

$ java -javaagent:deploy/lib/aspectjweaver-1.9.1.jar -classpath aop-app-1.0.jar:deploy/lib/* com.aop.app.Main
14:02:45.384 [main] DEBUG com.aop.app.lib.WrapDef - before wrap
14:02:45.391 [main] DEBUG com.aop.app.Main - inside myFunc
14:02:45.391 [main] DEBUG com.aop.app.lib.WrapDef - after wrap

Но с динамической загрузкой javaagent это дает следующий результат:

$ java -DdynamicLoad=true -DAGENT_PATH=deploy/lib/aspectjweaver-1.9.1.jar -classpath aop-app-1.0.jar:deploy/lib/* com.aop.app.Main
com.aop.app.lib.WrapDef Loaded : false                   //The WrapDef is NOT loaded before JAVAAGENT is Loaded - which is correct
java.lang.UnsupportedOperationException: AspectJ weaving agent was neither started via '-javaagent' (preMain) nor attached via 'VirtualMachine.loadAgent' (agentMain)
loading javaAgent deploy/lib/aspectjweaver-1.9.1.jar
loaded javaAgent deploy/lib/aspectjweaver-1.9.1.jar      //The JAVAAGENT is Dynamically Loaded - which is correct
com.aop.app.lib.WrapDef Loaded : false                   //The WrapDef is STILL NOT loaded even AFTER JAVAAGENT is Loaded - THIS IS THE ISSUE
15:53:08.543 [main] DEBUG com.aop.app.Main - inside myFunc

В официальной документации говорится, что any classes loaded before attachment will not be woven. Но, наоборот, как видно из приведенного выше вывода, класс WrapDef вообще не загружается.

Также обратите внимание, что я использую aspectj-maven-plugin в своем aop -lib/pom.xml со следующими параметрами:

<outxml>true</outxml>                           //creates META-INF/aop-ajc.xml
<showWeaveInfo>true</showWeaveInfo>             //supposed to create <weaver options="-showWeaveInfo"/> BUT DOES NOT WORK
<verbose>true</verbose>                         //supposed to create <weaver options="-verbose"/> BUT DOES NOT WORK

Итак, он создает META-INF/aop-ajc.xml внутри aop-lib-1.0.jar со следующим содержимым:

<aspectj>
<aspects>
<aspect name="com.aop.app.lib.WrapDef"/>
</aspects>
</aspectj>

но другие теги, соответствующие showWeaveInfo и verbose, не создаются в META-INF/aop-ajc.xml. Это еще одна вещь, которая здесь не работает.

Если вам нужна какая-то другая информация - я ее предоставлю.

Любая помощь приветствуется.


Ответы:


1

Объяснение очень простое: вы тестируете агента ткачества непосредственно в классе Main, который уже загружен до того самого агента из этого класса. Таким образом, вы должны избегать слишком ранней загрузки классов, которые вам нравятся. Я предлагаю вам поместить метод myFunc() (кстати, ужасное имя) в другой класс. Как насчет этого?

package com.aop.app;

import com.aop.app.lib.Wrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Application {
  private static final Logger logger = LoggerFactory.getLogger(Application.class);

  @Wrap
  public void myFunc(){
    logger.debug("inside myFunc");
  }

  public static void main(String[] args) {
    new Application().myFunc();
  }
}

Затем в последней строке Main.main(..) вы запускаете приложение, которое хотите создать:

Application.main(null);

Это даст следующий результат:

com.aop.app.lib.WrapDef Loaded : false
java.lang.UnsupportedOperationException: AspectJ weaving agent was neither started via '-javaagent' (preMain) nor attached via 'VirtualMachine.loadAgent' (agentMain)
loading javaAgent aop-app/target/deploy/lib/aspectjweaver-1.9.1.jar
loaded javaAgent aop-app/target/deploy/lib/aspectjweaver-1.9.1.jar
com.aop.app.lib.WrapDef Loaded : false
Loading
07:56:21.703 [main] DEBUG com.aop.app.lib.WrapDef - before wrap
07:56:21.716 [main] DEBUG com.aop.app.Application - inside myFunc
07:56:21.716 [main] DEBUG com.aop.app.lib.WrapDef - after wrap

P.S.: Вы действительно думаете, что пользователям вашей библиотеки аспектов проще указать два свойства в командной строке JVM, чем просто использовать -javaagent:/path/to/aspectweaver.jar? В любом случае, у вас, вероятно, есть свои причины использовать динамическое приложение Weaver. В некотором смысле я рад, что кто-то использует функциональность, которую я сам недавно добавил в AspectJ. ;-)

06.08.2018
  • Спасибо за ответ. Можете ли вы также ответить, почему <showWeaveInfo>true</showWeaveInfo> не создает тег <weaver options="-showWeaveInfo"/> в aop.xml и что я могу сделать, чтобы это произошло? 06.08.2018
  • Кроме того, у меня возникают проблемы с использованием com.sun.tools.jar, потому что он не загружен в путь к классам jre на сервере. Я задал вопрос об этом здесь - stackoverflow.com/questions/51707812/ . Можете ли вы помочь мне и здесь? 06.08.2018
  • Вопрос 1: AspectJ Maven предназначен для плетения во время компиляции, он не имеет никакого влияния на LTW. Вы должны настроить свой aop.xml так, как вы хотите, например. вы можете использовать плагин Maven Resources или что-то другое. Было бы проще просто предоставить полный файл в качестве ресурса и вообще не использовать <outxml>, если вас не устраивает файл по умолчанию. Вопрос 2: Вам нужен JDK, чтобы реализовать этот трюк с динамическим подключением Weaver, а не только JRE. Я все же рекомендую использовать стандартный механизм агента Java вместо этой магии (хотя я как бы сам его изобрел). 07.08.2018
  • Я использую сам jdk, а не jre (я упомянул об этом в вопросе, который я связал). Даже тогда он не выбирает банку из папки lib/. Я также упомянул команду, которую я использую для запуска проекта Java, т.е. /usr/local/java/jdk1.8.0_161/bin/java 07.08.2018
  • Тогда удачи вам в другом вопросе. Здесь это выходит за рамки. Это проблема пути к классам, я уверен, что вы разберетесь с ней. P.S.: И вы все еще не верите мне, что вам следует использовать агент Java из командной строки, чтобы избавить себя от проблем, не так ли? Вы по-прежнему предпочитаете добавлять другие переключатели командной строки, возиться с путем к классам JDK и т. д., вместо того, чтобы просто полагаться на JRE и установленный механизм агента Java. Хочешь, теперь решай. 07.08.2018
  • Да, я не хотел сдаваться, не попробовав. Я думаю, что в конце концов я соглашусь с вашим советом 07.08.2018
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..