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

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

问题描述

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

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.

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

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

有人能告诉我在调用 jar 文件中的特定函数后如何做特定的事情(调用特定函数)吗?

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?

推荐答案

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

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.

你需要做的是:

  • 使用 AspectJ 编译器编译您的 Aspect 代码ajc.(您也可以用它编译整个应用程序,因为它也是 Java 编译器 javac 的替代品.)
  • 创建一个加载时编织配置aop.xml,以便您的应用程序能够在类加载期间动态地将方面代码编织到第 3 方库中.我把它留给你来弄清楚如何做到这一点,只需查看 LTW 文档.
  • 通过 -javaagent:/path/to/aspectjweaver.jar 开关在命令行上使用 AspectJ 编织代理启动您的 JVM 或应用服务器.
  • 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.

现在你想要的方面是什么样的?让我们尝试一些变体并细化切入点以使其匹配.但首先让我们用几个示例 3rd 方类(FooBar)和一个小驱动程序应用程序(Application):

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

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

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.bar 进行的到 Foo.foo(根据您的问题,这是我们感兴趣的).

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

方面,第 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 的调用.但是如何将拦截限制为从 Bar.bar 的控制流 (cflow) 中进行的那些调用?

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?

方面,第 2 部分:拦截对 Foo.foo 的调用(in-)由 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);
    }
}

控制台日志:

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 直接或间接调用,因此在控制流中.如果我们检查调用堆栈(只需取消代码中的两个日志语句的注释),我们会更清楚地看到这一点:

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)

如果您检查 6 个调用堆栈,您会在每个调用堆栈中找到 Bar.bar.所以 cflow 切入点已经完成了我们告诉它的任务.

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.

我们能做得更好吗?如何告诉方面不仅将被调用者(目标)对象限制为 Foo,还要将调用者(这个)对象限制为 Bar?

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?

方面,第 3 部分:拦截对 Foo.foo 的调用(in-)由 Bar.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);
    }
}

控制台日志:

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 间接触发的我们的目标是只拦截直接呼叫.好的,那么让我们更精确:

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:

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

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

控制台日志:

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(..)) - 只调用 Foo.foo
  • cflow(execution(* Bar.bar(..))) - 仅在控制流中执行 Bar.bar
  • this(barObject) - 调用者必须是 Bar 对象
  • target(fooObject) - 被调用者必须是一个 Foo 对象
  • if("bar".equals(thisEnclosureJoinPointStaticPart.getSignature().getName())) - 动态运行时条件检查直接调用者的方法名称是否真的是 bar
  • 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

我希望这能解决您的问题,而且不会太冗长.我想以教程的方式来做它,以便让您了解如何解决像这样的高级 AOP 问题.享受!

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天全站免登陆