我想在調用非公共方法(bar)後執行一些特定操作。此方法在另一個方法(foo)中調用。請注意,「bar」和「foo」都是在第三方jar文件中定義的。在另一個方法內調用方法後執行一些操作
我試圖在使用spring的面向Aspect的編程中使用@before
註釋。 However, I could not do that。
任何人都可以讓我知道如何在從jar文件調用特定函數之後做特定的事情(調用特定函數)?
我想在調用非公共方法(bar)後執行一些特定操作。此方法在另一個方法(foo)中調用。請注意,「bar」和「foo」都是在第三方jar文件中定義的。在另一個方法內調用方法後執行一些操作
我試圖在使用spring的面向Aspect的編程中使用@before
註釋。 However, I could not do that。
任何人都可以讓我知道如何在從jar文件調用特定函數之後做特定的事情(調用特定函數)?
由於傑瓦西奧艾米建議,你需要使用AspectJ,不Spring AOP。如果您處於Spring環境中,則可以使用use AspectJ within Spring而不是Spring AOP,這是沒有問題的。如果你還沒有使用Spring,AOP不是開始使用它的理由,AspectJ可以在沒有Spring的簡單Java SE(或EE)版本中工作。
你所需要的是:
-javaagent:/path/to/aspectjweaver.jar
開關在命令行上使用AspectJ編織代理啓動您的JVM或應用程序服務器。現在你想要什麼樣的樣子?讓我們嘗試一些變體並改進切入點以使其匹配。但首先,讓我們爲我們的實驗階段,幾樣第三方類(Foo
和Bar
)和一個小驅動程序(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
對象和c所有的公共方法Bar.doSomething
。這種方法觸發了一系列其他方法調用,其中一些最終在Foo.foo
間接調用,但只有一個直接調用正在從Bar.bar
到Foo.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
所有呼叫。但是如何將截取內容限制在Bar.bar
的控制流(cflow
)內進行的調用?
看點,部分#2:攔截來電Foo.foo
(活)直接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
是如何發生的,ev雖然我們說我們想限制控制流量到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調用堆棧,你會發現在他們每個人的Bar.bar
。所以cflow
切入點完成了我們所說的。
我們能變得更好嗎?怎麼樣告訴方面不只是限制被調用者(目標)對象Foo
,而且也調用者(這個)對象Bar
?
看點,部分#3:攔截來電Foo.foo
(活)直接Bar.bar
製成,但肯定從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
的那個仍然不受歡迎,因爲它只是間接由觸發,我們的抱負是隻攔截直接呼叫。好吧,那就讓我們得到更精確:
看點,一部分#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)
等voilà!這是我們首先想要的。讓我們重溫一下我們只是爲了縮小切入點進行:
call(* Foo.foo(..))
- 只調用Foo.foo
cflow(execution(* Bar.bar(..)))
- 只與Bar.bar
在控制執行流程this(barObject)
- 主叫必須是酒吧對象target(fooObject)
- 被調用者必須是Foo對象if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName()))
- 動態運行時狀況會檢查直接調用者的m ethod的名字是真的bar
我希望這可以解決您的問題,並不是太冗長。我想做一些教程式的教程,以便你能夠理解如何解決像這樣的高級AOP問題。請享用!
避免使用Spring AOP。它不會允許你這樣做,因爲Spring創建了一個代理bean來進行bean化,因此,只有對「proxy」的調用纔會被調用。這意味着,如果你有一個第三方類的bean(讓它叫做fooBean),當你懷疑fooBean.foo()
時,你實際上是通過具有方面邏輯的代理,但是一旦執行了foo()
方法,那麼內部調用例如撥打bar()
將不再認爲代理人,所以,那裏不會有任何方面。
Mybe運行到一個更復雜的解決方案,使用純AspectJ可以幫助你,因爲它不是基於proxys,它只是提高了編譯字節碼
簡單:在foo()方法中調用bar()之後,執行您的操作。你只需要修改foo()的代碼。 –
我編輯問題。 foo和bar都是在一個jar文件中定義的,我想在調用bar(私有方法)時執行特定的操作。 – Nihamata