在另一个方法内部的方法调用之后执行一些操作 [英] Do some action after a method call inside another method

查看:103
本文介绍了在另一个方法内部的方法调用之后执行一些操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在调用非公共方法(栏)之后执行一些特定的操作.在另一个方法(foo)中调用此方法.请注意,"bar"和"foo"都在第三方jar文件中定义.

我试图在使用spring的面向方面的编程中使用 @before 注释来做到这一点.但是,我做不到.

有人可以让我知道在调用jar文件中的特定功能后该如何做特定的事情(调用特定功能)?

解决方案

按照 Gervasio Amy 的建议,您需要使用AspectJ,而不是Spring AOP.如果您在Spring环境中,则可以在Spring内使用AspectJ 而不是Spring AOP,这没有问题.如果您还没有使用Spring,那么AOP并不是开始使用它的原因,AspectJ可以在没有Spring的简单Java SE(或EE)版本中使用.

您需要做的是:

  • 使用AspectJ编译器 ajc 编译您的Aspect代码.(您还可以使用它编译整个应用程序,因为它也是Java编译器 javac 的替代品.)
  • 创建一个加载时编织配置 aop.xml ,以使您的应用程序可以在类加载过程中将方面代码实时地编织到第三方库中.我由您自己决定如何做,只需查看 LTW文档.
  • 通过 -javaagent:/path/to/aspectjweaver.jar 开关在命令行上使用AspectJ编织代理启动JVM或应用程序服务器.

现在您想要的外观是什么样的?让我们尝试一些变体并完善切入点,以使其匹配.但是首先让我们通过一些示例第三方类( Foo Bar )和一个小的驱动程序应用程序( Application ):

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

 打包my.thirdparty.application;公共课Foo {无效blah(){zot();}无效的foo(){}无效的zot​​(){foo();}} 

 打包my.thirdparty.application;公共类酒吧{Foo foo =新的Foo();公共无效doSomething(){someMethod();酒吧();anotherMethod();}私人无效someMethod(){foo.blah();foo.foo();foo.zot();}私人void bar(){foo.blah();//这是我们要拦截的唯一调用,"bar"调用了"foo"foo.foo();foo.zot();anotherMethod();}私人void anotherMethod(){foo.blah();foo.foo();foo.zot();}} 

 程序包de.scrum_master.app;导入my.thirdparty.application.Bar;公共类申请{公共静态void main(String [] args){新的Bar().doSomething();}} 

如您所见, Application.main 创建一个 Bar 对象,并调用公共方法 Bar.doSomething .该方法触发了一系列其他方法调用,其中一些最终以间接调用 Foo.foo 的方式出现,但是仅通过 Bar.bar 进行了一次直接调用.到 Foo.foo (根据您的问题我们对此感兴趣).

方面,第1部分:拦截对 Foo.foo

的所有调用

  package de.scrum_master.aspect;导入my.thirdparty.application.Foo;导入my.thirdparty.application.Bar;公共方面MethodInterceptor {切入点allCalls():通话(* Foo.foo(..));周围的对象(Foo fooObject):allCalls()&&target(fooObject){System.out.println(thisJoinPointStaticPart +->调用者=" + thisEnclosingJoinPointStaticPart);//new Exception("printing stack trace").printStackTrace(System.out);//System.out.println();返回proced(fooObject);}} 

控制台日志:

  call(避免my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.someMethod())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.bar())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.anotherMethod())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.anotherMethod())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot()) 

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

方面,第2部分:拦截由 Bar.bar

直接在内部进行的 Foo.foo 调用

  package de.scrum_master.aspect;导入my.thirdparty.application.Foo;导入my.thirdparty.application.Bar;公共方面MethodInterceptor {切入点indirectCalls():call(* Foo.foo(..))&&cflow(执行(* Bar.bar(..))));周围的对象(Foo fooObject):indirectCalls()&&target(fooObject){System.out.println(thisJoinPointStaticPart +->调用者=" + thisEnclosingJoinPointStaticPart);//new Exception("printing stack trace").printStackTrace(System.out);//System.out.println();返回proced(fooObject);}} 

控制台日志:

  call(避免my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.bar())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.anotherMethod())呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot()) 

现在看起来比以前好多了,我们将之前截获的12个呼叫的结果缩小到了6个.但是,如何出现像 Foo.zot Bar这样的调用者.anotherMethod 在结果列表中,即使我们说过我们想将控制流限制为 Bar.bar ?答案很简单:这两个方法也被 Bar.bar 直接或间接调用,因此位于控制流之内.如果我们检查调用堆栈(只是取消注释代码中的两个日志语句),我们会更清楚地看到这一点:

  call(避免my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Foo.foo_aroundBody1 $ advice(Foo.java:22)在my.thirdparty.application.Foo.zot(Foo.java:11)在my.thirdparty.application.Foo.blah(Foo.java:5)在my.thirdparty.application.Bar.bar(Bar.java:19)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.bar())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Bar.foo_aroundBody3 $ advice(Bar.java:22)在my.thirdparty.application.Bar.bar(Bar.java:21)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Foo.foo_aroundBody1 $ advice(Foo.java:22)在my.thirdparty.application.Foo.zot(Foo.java:11)在my.thirdparty.application.Bar.bar(Bar.java:22)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Foo.foo_aroundBody1 $ advice(Foo.java:22)在my.thirdparty.application.Foo.zot(Foo.java:11)在my.thirdparty.application.Foo.blah(Foo.java:5)在my.thirdparty.application.Bar.anotherMethod(Bar.java:27)在my.thirdparty.application.Bar.bar(Bar.java:23)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.anotherMethod())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Bar.foo_aroundBody5 $ advice(Bar.java:22)在my.thirdparty.application.Bar.anotherMethod(Bar.java:28)在my.thirdparty.application.Bar.bar(Bar.java:23)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Foo.zot())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Foo.foo_aroundBody1 $ advice(Foo.java:22)在my.thirdparty.application.Foo.zot(Foo.java:11)在my.thirdparty.application.Bar.anotherMethod(Bar.java:29)在my.thirdparty.application.Bar.bar(Bar.java:23)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7) 

如果检查6个调用堆栈,则会在每个堆栈中找到 Bar.bar .因此, cflow 切入点已经完成了我们告诉它的工作.

我们能变得更好吗?怎样告诉方面不仅将被调用方(目标)对象限制为 Foo ,而且还将调用方(此)对象限制为 Bar ?

方面,第3部分:拦截由 Bar.bar 直接(in-)进行的 Foo.foo 的调用,但肯定是来自条形码对象

  package de.scrum_master.aspect;导入my.thirdparty.application.Foo;导入my.thirdparty.application.Bar;公共方面MethodInterceptor {切入点callsFromBar(Bar barObject):call(* Foo.foo(..))&&cflow(execution(* Bar.bar(..)))&&this(barObject);周围的对象(Foo fooObject,Bar barObject):callsFromBar(barObject)&&target(fooObject){System.out.println(thisJoinPointStaticPart +->调用者=" + thisEnclosingJoinPointStaticPart);新的Exception("printing stack trace").printStackTrace(System.out);System.out.println();返回proced(fooObject,barObject);}} 

控制台日志:

  call(避免my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.bar())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Bar.foo_aroundBody3 $ advice(Bar.java:22)在my.thirdparty.application.Bar.bar(Bar.java:21)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7)呼叫(void my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.anotherMethod())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Bar.foo_aroundBody5 $ advice(Bar.java:22)在my.thirdparty.application.Bar.anotherMethod(Bar.java:28)在my.thirdparty.application.Bar.bar(Bar.java:23)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7) 

我们越来越好:从6拦截到2次拦截. Bar.anotherMethod 的拦截仍然是不需要的,因为它只是由 Bar.bar 间接触发的我们的目标是仅拦截直接呼叫.好的,接下来让我们变得更加精确:

方面,第4部分:拦截由 Bar.bar 直接进行的对 Foo.foo 的调用,不允许间接访问

  package de.scrum_master.aspect;导入my.thirdparty.application.Foo;导入my.thirdparty.application.Bar;公共方面MethodInterceptor {pointcut directCalls(Bar barObject):call(* Foo.foo(..))&&cflow(execution(* Bar.bar(..)))&&this(barObject)&&if("bar" .equals(thisEnclosingJoinPointStaticPart.getSignature().getName()));周围的对象(Foo fooObject,Bar barObject):directCalls(barObject)&&target(fooObject){System.out.println(thisJoinPointStaticPart +->调用者=" + thisEnclosingJoinPointStaticPart);新的Exception("printing stack trace").printStackTrace(System.out);System.out.println();返回proced(fooObject,barObject);}} 

控制台日志:

  call(避免my.thirdparty.application.Foo.foo())->调用者=执行(避免my.thirdparty.application.Bar.bar())java.lang.Exception:打印堆栈跟踪在my.thirdparty.application.Bar.foo_aroundBody3 $ advice(Bar.java:22)在my.thirdparty.application.Bar.bar(Bar.java:21)在my.thirdparty.application.Bar.doSomething(Bar.java:8)在de.scrum_master.app.Application.main(Application.java:7) 

等等!这是我们首先要的.让我们概括一下我们为缩小切入点所做的工作:

  • 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问题.享受吧!

I want to do some specific actions after a non public method (bar) is called. This method is called inside another method (foo). Note that "bar" and "foo" both are defined in a third-party jar file.

I tried to do that using @before annotation in Aspect oriented programming using spring. However, I could not do that.

Could any one let me know how can I do specific thing (call a specific function) after a specific function from a jar file is called?

解决方案

As Gervasio Amy suggested, you need to use AspectJ, not Spring AOP. If you are in a Spring environment, you can use AspectJ within Spring instead of Spring AOP, this is no problem. If you are not using Spring yet, AOP is not a reason to start using it, AspectJ works in simple Java SE (or EE) versions without Spring.

What you need to to is:

  • Compile your Aspect code with the AspectJ compiler ajc. (You can also compile your whole application with it because it is also a replacement for the Java compiler javac.)
  • Create a load-time weaving configuration aop.xml so as to enable your application to weave aspect code into 3rd party libraries on the fly during class-loading. I leave it up to you to figure out how to do that, just check the LTW documentation.
  • Start your JVM or application server with the AspectJ weaving agent on the command line via -javaagent:/path/to/aspectjweaver.jar switch.

Now what would the aspect you want look like? Let us try a few variants and refine the pointcut so as to make it match. But first let us set the stage for our experiments with a few sample 3rd party classes (Foo and Bar) and a little driver application (Application):

Sample application & 3rd party code:

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();
    }
}

As you can see, Application.main creates a Bar object and calls a public method Bar.doSomething. This method triggers a series of other method calls, some of which end up in Foo.foo being called indirectly, but only one single direct call is being made from Bar.bar to Foo.foo (which is what we are interested in according to your question).

Aspect, part #1: intercept all calls to 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);
    }
}

Console log:

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())

This is a nice start, because now we can already intercept all calls to Foo.foo. But how about limiting the interceptions to those calls which are being made from within a control flow (cflow) of Bar.bar?

Aspect, part #2: intercept calls to Foo.foo made (in-)directly by Bar.bar

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);
    }
}

Console log:

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())

Now this looks much better than before, we narrowed down our previous result of intercepted 12 calls down to 6. But how does it happen that we have callers like Foo.zot and Bar.anotherMethod in the result list, even though we said we wanted to limit the control flow to Bar.bar? The answer is simple: Those two methods were also directly or indirectly called by Bar.bar and are thus within the control flow. We see this more clearly if we inspect the call stacks (just uncomment the two log statements in the code):

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)

If you inspect the 6 callstacks, you find Bar.bar in each of them. So the cflow pointcut has done just what we told it to.

Can we get even better? How about telling the aspect not just limit the callee (target) object to Foo but also the to also the caller (this) object to Bar?

Aspect, part #3: intercept calls to Foo.foo made (in-)directly by Bar.bar, but definitely from a Bar object

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);
    }
}

Console log:

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)

We are getting better and better: down to 2 interceptions from 6. The one from Bar.anotherMethod is still unwanted because it was only indirectly triggered by Bar.bar and our ambition is to only intercept direct calls. Okay, then let us get even more precise:

Aspect, part #4: intercept calls to Foo.foo made directly by Bar.bar, no indirection permitted

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);
    }
}

Console log:

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)

Et voilà! This is what we wanted in the first place. Let us recapitulate what we have just done in order to narrow down the pointcut:

  • call(* Foo.foo(..)) - only calls to Foo.foo
  • cflow(execution(* Bar.bar(..))) - only with the execution of Bar.bar in the control flow
  • this(barObject) - the caller must be a Bar object
  • target(fooObject) - the callee must be a Foo object
  • if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName())) - a dynamic runtime condition checks if the direct caller's method name is really bar

I hope this solves your problem and was not too verbose. I wanted to do it tutorial-style so as to enable you to understand how to solve advanced AOP problems like this one. Enjoy!

这篇关于在另一个方法内部的方法调用之后执行一些操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆