Аспект AspectJ не применяется в сценарии LTW

Я пытаюсь использовать AspectJ в отдельном приложении, но, похоже, это не работает.

Вот классы, которые я создал:

package oata.aspect;

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

@Aspect
public class AspectJTest {


    @Around("execution(* *..*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("around fired");
        jp.proceed();
    }
}
package oata;

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

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface AspectTest {
}
package oata;

import oata.AspectTest;

public class TestAspect {
    public void doItWithout(int i) {
        double s = Math.acos(i);
    }

    @AspectTest
    public void doItAnnotated(int i) {
        double s = Math.acos(i);
    }

    public void doItAspect(int i) {
        double s = Math.acos(i);

    }
}
package oata;

import java.util.Date;

public class Test {
    public Test() {
    }

    public static void main(String arg[]) {
        // performance testing
        // invoke method without aspect
        long t1 = new Date().getTime();
        for (int i = 0; i < 1; i++) {
            new TestAspect().doItWithout(i);
        }
        System.out.println("Invoke without aspect:"
                + (new Date().getTime() - t1));
        // invoke method with annotated aspect
        t1 = new Date().getTime();
        for (int i = 0; i < 1; i++) {
            new TestAspect().doItAnnotated(i);
        }
        System.out.println("Invoke annotated aspect method:"
                + (new Date().getTime() - t1));
        // invoke method with aspect but not annotated
        t1 = new Date().getTime();
        for (int i = 0; i < 1; i++) {
            new TestAspect().doItAspect(i);
        }
        System.out.println("Invoke aspect method:"
                + (new Date().getTime() - t1));
    }
}

Также в папке src/META_INF я создал файл aop.xml.

<aspectj>
    <aspects>
        <aspect name="oata.aspect.AspectJTest" />
    </aspects>
    <weaver>
        <include within="oata.*" />
    </weaver>
</aspectj>

Затем из командной строки, когда я пытаюсь запустить Test.java с помощью приведенной ниже команды, System.out.println в совете не печатается:

\TestAspectJ\bin>java -javaagent:D:\Project\workspaces\RCS_3.2.1\TestAspectJ\src\aspectjweaver-1.6.10.jar oata.Test

Может кто-нибудь, пожалуйста, дайте мне знать, что я делаю неправильно.

Спасибо АА


person Ashish Abrol    schedule 22.08.2015    source источник
comment
Я изменил тему, чтобы отразить то, что вы спрашиваете. В частности, я удалил часть приложения, отличного от Spring, потому что AspectJ в любом случае полностью независим от Spring, нет необходимости упоминать об этом. Использование его в дополнение к Spring AOP или вместо него из Spring — это скорее специальность, а не обычный случай.   -  person kriegaex    schedule 30.08.2015


Ответы (2)


Несколько вещей:

  • Ваша папка META-INF/* определенно копируется в папку bin, откуда вы запускаете приложение?
  • Вы указываете включение oata.*, которое будет включать только прямые классы в пакет oata, если вам нужны дополнительные подпакеты (и я думаю, что вам это нужно), вам нужно oata..*
  • Вы пытались указать weaver options="-verbose" - это вам что-нибудь показывает? Если он ничего не показывает, файл aop.xml не найден. Если он покажет вам что-то, он сообщит вам, какие аспекты включены. Возможно, затем дополните его -debug, чтобы узнать больше о том, что происходит.
  • Я мог бы включить жалобу !within(AspectJTest) в ваш pointcut, или вы могли бы закончить тем, что сами посоветовались и потерпели неудачу с переполнением стека, когда он действительно начнет работать.
  • Наконец, я знаю, что вы не используете его сейчас, но если вы собираетесь использовать эту аннотацию для сопоставления с AspectJ, вам нужно будет изменить ее с SOURCE сохранения, потому что AspectJ работает на уровне байтового кода и не будет видеть, имеет ли он сохранение исходного кода. .
person Andy Clement    schedule 24.08.2015

Энди прав во всем, что он сказал. Поскольку вы, похоже, новичок как в AspectJ, так и в Java, я немного изменил код вашего примера, чтобы помочь вам начать работу. Вещи, которые я заметил по пути:

  • Вы используете очень старую версию AspectJ 1.6.10. Это 2010 года и даже не последняя версия 1.6 (которая будет 1.6.12). Как насчет использования текущей версии AspectJ 1.8.6?
  • I am a clean code guy and noticed that your class names are rather obfuscating what you want to demonstrate with the sample code. So I renamed them:
    • TestApplication
    • TestAspectHelper
    • AspectTestMyAnnotation
    • AspectJTestMethodInterceptor
  • Я также изменил типы возвращаемых значений методов Helper, чтобы они возвращали что-то отличное от void, чтобы продемонстрировать следующую проблему.
  • Ваш совет @Around имеет тип возвращаемого значения void. Таким образом, это не работает, если pointcut попадает в непустой метод. Я изменил тип возврата на Object и код для возврата результата proceed(), чтобы показать, как это можно сделать более общим способом.
  • Ваш совет @Around всегда регистрирует одно и то же сообщение. Я обновил его, чтобы регистрировать фактическую информацию о точке соединения (до и после вызова proceed()), чтобы мы могли видеть, что происходит в журнале консоли.
  • Как сказал Энди, очевидно, вы планируете использовать аннотацию, чтобы сопоставить аннотированные методы с pointcut. Таким образом, я изменил область хранения на RUNTIME.
  • Ваш pointcut нацелен на выполнение всех методов, включая Application.main и Helper.doItWithout. Я изменил pointcut, чтобы использовать только целевые методы либо с @MyAnnotation, либо с подстрокой «Aspect» в имени метода.
  • Похоже, вы хотите профилировать время выполнения метода и сравнивать методы с аспектами, применяемыми к методам, не предназначенным для аспектов. Вместо того, чтобы создавать множество экземпляров Date и вызывать new Date().getTime() (возвращает миллисекунды), вы можете просто использовать System.nanoTime() (возвращает наносекунды).
  • При профилировании вы хотите измерять время выполнения метода, а не время создания объекта. Таким образом, я изменил код, чтобы просто создать один экземпляр Helper, который затем повторно используется в методе main.
  • Классу Application не нужен пустой конструктор по умолчанию, потому что он будет создан автоматически JVM.
  • Чтобы получить значимые результаты профилирования, вы должны использовать большее количество повторений (например, миллион). Я ввел константу с именем LOOP_COUNT, чтобы упростить это для всех трех циклов.
  • Внимание! Если вы хотите измерить время выполнения метода, вы не должны ничего печатать в своем аспекте, потому что тогда вы также будете измерять время, необходимое для записи чего-либо в консоль. Таким образом, я закомментировал операторы печати в аспекте. Вы все еще можете активировать их для меньшего количества повторений, чтобы увидеть, что происходит.

Рефакторинг кода:

package oata;

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

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
package oata;

import oata.MyAnnotation;

public class Helper {
    public double doItWithout(int i) {
        return Math.acos(i);
    }

    @MyAnnotation
    public double doItAnnotated(int i) {
        return Math.acos(i);
    }

    public double doItAspect(int i) {
        return Math.acos(i);
    }
}
package oata;

public class Application {
    private static final int LOOP_COUNT = 100000000;

    public static void main(String arg[]) {
        Helper helper = new Helper();
        System.out.printf(
            "Profiling statistics for %,d repetitions%n%n",
            LOOP_COUNT
        );

        long startTime = System.nanoTime();
        for (int i = 0; i < LOOP_COUNT; i++)
            helper.doItWithout(i);
        System.out.printf(
            "Method not targeted by aspect:%n    %,15d ns%n",
            System.nanoTime() - startTime
        );

        startTime = System.nanoTime();
        for (int i = 0; i < LOOP_COUNT; i++)
            helper.doItAnnotated(i);
        System.out.printf(
            "Method targeted by aspect because it is annotated:%n    %,15d ns%n",
            System.nanoTime() - startTime
        );

        startTime = System.nanoTime();
        for (int i = 0; i < LOOP_COUNT; i++)
            helper.doItAspect(i);
        System.out.printf(
            "Method targeted by aspect because of its name:%n    %,15d ns%n",
            System.nanoTime() - startTime
        );
    }
}
package oata.aspect;

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

@Aspect
public class MethodInterceptor {
    @Around("execution(@oata.MyAnnotation * *(..)) || execution(* *Aspect*(..))")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
//      System.out.println("BEFORE " + jp);
        Object result = jp.proceed();
//      System.out.println("AFTER  " + jp);
        return result;
    }
}

Пример журнала консоли для 1 повторения с включенными операторами журнала аспектов:

Profiling statistics for 1 repetitions

Method not targeted by aspect:
            153.893 ns
BEFORE execution(double oata.Helper.doItAnnotated(int))
AFTER  execution(double oata.Helper.doItAnnotated(int))
Method targeted by aspect because it is annotated:
          3.102.128 ns
BEFORE execution(double oata.Helper.doItAspect(int))
AFTER  execution(double oata.Helper.doItAspect(int))
Method targeted by aspect because of its name:
             55.295 ns

Как вы можете видеть здесь, результаты не очень убедительны с одним вызовом для каждого метода.

Пример журнала консоли для 100 000 000 (сто миллионов) повторений с отключенными операторами журнала аспектов:

Profiling statistics for 100.000.000 repetitions

Method not targeted by aspect:
        843.407.034 ns
Method targeted by aspect because it is annotated:
      1.219.571.173 ns
Method targeted by aspect because of its name:
      1.175.436.574 ns

Теперь результат более убедителен: метод, не предназначенный для какого-либо аспекта, выполняется быстрее, чем следующие два метода, которые имеют примерно одинаковое время выполнения 1,2 секунды, что и следовало ожидать, поскольку используемые точки могут быть определены статически во время компиляции ( для CTW) или время плетения (для LTW).

person kriegaex    schedule 30.08.2015