在另一个方法中调用方法后执行一些操作 [英] Do some action after a method call inside another method
问题描述
我想在一个非公共方法(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 方类(Foo
和 Bar
)和一个小驱动程序应用程序(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.zot
和 Bar 这样的调用者怎么办.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 toFoo.foo
cflow(execution(* Bar.bar(..)))
- only with the execution ofBar.bar
in the control flowthis(barObject)
- the caller must be a Bar objecttarget(fooObject)
- the callee must be a Foo objectif("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName()))
- a dynamic runtime condition checks if the direct caller's method name is reallybar
我希望这能解决您的问题,而且不会太冗长.我想以教程的方式来做它,以便让您了解如何解决像这样的高级 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屋!