Reputation: 57
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?
Upvotes: 0
Views: 2678
Reputation: 295
Avoid using Spring AOP. It will not allow you to do that since Spring creates a proxy to aspectize a bean, thus, only calls to the "proxy" will be aspectized. It means, if you have a bean of that third party class (let call it fooBean), when you execeute fooBean.foo()
you are actually going through the proxy which has the aspect logic, but once foo()
method is executed, then internal calls, as for example the call to bar()
will no longer go thought that proxy, so, there will be no aspect there.
Mybe running into a more complex solution as using pure AspectJ might help you, since it is not based on proxys, it just enhances compiled bytecodes
Upvotes: 0
Reputation: 67297
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:
-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 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 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!
Upvotes: 3