0

我正在尝试在独立应用程序中使用 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

谁能让我知道我做错了什么。

谢谢 AA

4

2 回答 2

2

安迪所说的一切都是正确的。因为您似乎是 AspectJ 和 Java 的初学者,所以我对您的示例代码进行了一些重构以帮助您入门。一路上我注意到的事情:

  • 您使用的是非常旧的 AspectJ 版本 1.6.10。它是从 2010 年开始的,甚至不是最新的 1.6 版本(即 1.6.12)。使用当前的 AspectJ 版本 1.8.6 怎么样?
  • 我是一个干净的代码人,并注意到您的类名相当混淆了您想用示例代码演示的内容。所以我重命名了它们:
    • TestApplication
    • TestAspectHelper
    • AspectTestMyAnnotation
    • AspectJTestMethodInterceptor
  • 我还更改了Helper方法的返回类型,以便返回其他内容,而不是void为了演示下一个问题。
  • 您的@Around建议的返回类型为void. 这样,如果切入点遇到非 void 方法,它就不起作用。我将返回类型更改为Object和返回结果的代码,proceed()以展示如何以更通用的方式完成此操作。
  • 您的@Around建议总是记录相同的消息。我更新了它以记录实际的连接点信息(通话前后proceed()),这样我们就可以看到控制台日志上发生了什么。
  • 正如安迪所说,显然您打算使用注释来将带注释的方法与切入点相匹配。因此,我将保留范围更改为RUNTIME.
  • 您的切入点针对所有方法执行,包括Application.mainHelper.doItWithout。我将切入点更改为仅针对方法@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)。

于 2015-08-30T09:35:48.750 回答
2

一些事情:

  • 您的META-INF/*文件夹是否肯定被复制到您运行应用程序的 bin 文件夹中?
  • 您正在指定一个 include of oata.*,它将仅包含 oata 包中的直接类,如果您想要更多的子包(我认为您需要)oata..*
  • 您是否尝试过指定weaver options="-verbose"- 这是否向您展示了什么?如果它什么也没显示,则找不到 aop.xml 文件。如果它确实向您显示了某些内容,它将告诉您正在打开哪些方面。也许然后对其进行扩充,-debug以了解更多关于正在发生的事情。
  • 我可能会!within(AspectJTest)向您的切入点投诉,或者您最终可能会在开始工作时自我建议并因堆栈溢出而失败。
  • 最后,我知道您现在没有使用它,但是如果您打算使用该注释与 AspectJ 匹配,则需要将其从SOURCE保留中更改,因为 AspectJ 在字节码级别工作并且不会查看它是否具有源保留。
于 2015-08-24T16:30:58.777 回答