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


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

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

  • aop-lib - аспекты, используемые в качестве lib. Он содержит следующие классы
    1. обернуть.java - это аннотация, используемая для прикрепления советов
    2. WrapDef.java - это определение вышеупомянутой аннотации Wrap.
  • aop-app - использует вышеупомянутый аспект lib
    1. динамическая нагрузка.java - класс для динамической загрузки javaagent
    2. Главная.java - основной класс, использующий аннотацию Wrap.

Структура Dir выглядит следующим образом:

.
├── 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

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

Ниже приводится содержание обернуть.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 в качестве командной строки arg, он работает отлично:

$ 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 3

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? В любом случае, у вас, вероятно, есть свои причины использовать динамическую привязанность Ткача. В каком-то смысле я даже рад, что кто-то использует функциональность, которую я сам недавно добавил в AspectJ. ;-)