在另一个方法内调用方法后执行一些操作

时间:2015-11-25 06:32:45

标签: java spring aop aspectj

我希望在调用非公共方法(bar)后执行一些特定操作。在另一个方法(foo)中调用此方法。请注意" bar"和" foo"两者都在第三方jar文件中定义。

我尝试使用spring在面向方面编程中使用@before注释来做到这一点。 However, I could not do that

在调用jar文件中的特定函数后,是否可以让任何人知道如何执行特定的操作(调用特定函数)?

2 个答案:

答案 0 :(得分:3)

正如 Gervasio Amy 建议的那样,你需要使用AspectJ,而不是Spring AOP。如果你在Spring环境中,你可以use AspectJ within Spring而不是Spring AOP,这没问题。如果您还没有使用Spring,AOP不是开始使用它的理由,AspectJ可以在没有Spring的简单Java SE(或EE)版本中工作。

您需要做的是:

  • 使用AspectJ编译器 ajc 编译Aspect代码。 (您也可以使用它编译整个应用程序,因为它也可以替代Java编译器 javac 。)
  • 创建一个加载时编织配置 aop.xml ,以便您的应用程序能够在类加载期间将方面代码编织到第三方库中。我将其留给您了解如何执行此操作,只需查看LTW documentation
  • 即可
  • 通过-javaagent:/path/to/aspectjweaver.jar开关在命令行上使用AspectJ编织代理启动JVM或应用程序服务器。

现在你想要的外观是什么样的?让我们尝试一些变体并改进切入点以使其匹配。但首先让我们为几个示例第三方类(FooBar)和一个小驱动程序应用程序(Application)设置实验阶段:

示例应用程序&第三方代码:

package my.thirdparty.application;

public class Foo {
    void blah() {
        zot();
    }

    void foo() {}

    void zot() {
        foo();
    }
}
package my.thirdparty.application;

public class Bar {
    Foo foo = new Foo();

    public void doSomething() {
        someMethod();
        bar();
        anotherMethod();
    }

    private void someMethod() {
        foo.blah();
        foo.foo();
        foo.zot();
    }

    private void bar() {
        foo.blah();
        // This is the only call we want to intercept, 'foo' called by 'bar'
        foo.foo();
        foo.zot();
        anotherMethod();
    }

    private void anotherMethod() {
        foo.blah();
        foo.foo();
        foo.zot();
    }
}
package de.scrum_master.app;

import my.thirdparty.application.Bar;

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

如您所见,Application.main创建了一个Bar对象,并调用了公共方法Bar.doSomething。此方法触发一系列其他方法调用,其中一些最终在Foo.foo间接调用,但只有一个直接调用从Bar.barFoo.foo(这是什么我们根据你的问题感兴趣。

看点,第1部分:拦截对Foo.foo的所有来电

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut allCalls() :
        call(* Foo.foo(..));

    Object around(Foo fooObject) : allCalls() && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        //new Exception("printing stack trace").printStackTrace(System.out);
        //System.out.println();
        return proceed(fooObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.someMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())

这是一个不错的开始,因为现在我们已经可以拦截对Foo.foo的所有调用。但是如何限制对cflow的控制流(Bar.bar)内的调用的拦截?

Aspect,第2部分:Foo.foo

直接拦截对Bar.bar的调用(in-)
package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut indirectCalls() :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..)));

    Object around(Foo fooObject) : indirectCalls() && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        //new Exception("printing stack trace").printStackTrace(System.out);
        //System.out.println();
        return proceed(fooObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())

现在看起来比以前好多了,我们将截获的12次通话的先前结果缩小到6级。但是如果我们在结果列表中有Foo.zotBar.anotherMethod等来电者怎么样呢? ,即使我们说我们想要将控制流量限制为Bar.bar?答案很简单:这两种方法也由Bar.bar直接或间接调用,因此属于控制流程。如果我们检查调用堆栈(只是取消注释代码中的两个日志语句),我们会更清楚地看到这一点:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Foo.blah(Foo.java:5)
    at my.thirdparty.application.Bar.bar(Bar.java:19)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Bar.bar(Bar.java:22)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Foo.blah(Foo.java:5)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:27)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody5$advice(Bar.java:22)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:28)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:29)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

如果您检查6个callstack,则会在每个中找到Bar.bar。所以cflow切入点已经完成了我们告诉它的目的。

我们可以变得更好吗?如何告诉方面不仅将被调用者(目标)对象限制为Foo,还将调用者(此)对象限制为Bar

Aspect,第3部分:Foo.foo拦截对Bar.bar的调用(in-),但绝对来自Bar对象

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut callsFromBar(Bar barObject) :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..))) && this(barObject);

    Object around(Foo fooObject, Bar barObject) : callsFromBar(barObject) && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        new Exception("printing stack trace").printStackTrace(System.out);
        System.out.println();
        return proceed(fooObject, barObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody5$advice(Bar.java:22)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:28)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

我们越来越好:从6开始减少2次拦截。来自Bar.anotherMethod的那个仍然是不受欢迎的,因为它只是由Bar.bar间接触发,我们的目标是仅拦截直接呼叫。好的,那就让我们更精确了:

Aspect,第4部分:拦截Foo.foo直接拨打的Bar.bar来电,不允许间接

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut directCalls(Bar barObject) :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..))) && this(barObject) &&
        if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName()));

    Object around(Foo fooObject, Bar barObject) : directCalls(barObject) && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        new Exception("printing stack trace").printStackTrace(System.out);
        System.out.println();
        return proceed(fooObject, barObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

Etvoilà!这是我们首先想要的。让我们概括一下我们刚才所做的工作,以缩小切入点:

  • call(* Foo.foo(..)) - 仅拨打Foo.foo
  • cflow(execution(* Bar.bar(..))) - 仅在控制流程中执行Bar.bar
  • this(barObject) - 调用者必须是Bar对象
  • target(fooObject) - 被调用者必须是Foo对象
  • if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName())) - 动态运行时条件检查直接调用者的方法名称是否真的bar

我希望这能解决你的问题并且不会太冗长。我想做教程风格,以便让您了解如何解决像这样的高级AOP问题。享受!

答案 1 :(得分:0)

避免使用Spring AOP。它不会允许你这样做,因为Spring创建了一个代理来指定bean,因此,只有对“代理”的调用才会被指定。这意味着,如果你有一个第三方类的bean(让它称之为fooBean),当你执行fooBean.foo()时,你实际上是通过具有方面逻辑的代理,但是一旦foo()方法是执行,然后是内部调用,例如对bar()的调用将不再被认为是代理,因此,那里没有任何方面。

Mybe遇到更复杂的解决方案,因为使用纯AspectJ可能对您有所帮助,因为它不是基于代理,它只是增强了编译的字节码