2015-11-25 40 views
-1

我想在調用非公共方法(bar)後執行一些特定操作。此方法在另一個方法(foo)中調用。請注意,「bar」和「foo」都是在第三方jar文件中定義的。在另一個方法內調用方法後執行一些操作

我試圖在使用spring的面向Aspect的編程中使用@before註釋。 However, I could not do that

任何人都可以讓我知道如何在從jar文件調用特定函數之後做特定的事情(調用特定函數)?

+0

簡單:在foo()方法中調用bar()之後,執行您的操作。你只需要修改foo()的代碼。 –

+0

我編輯問題。 foo和bar都是在一個jar文件中定義的,我想在調用bar(私有方法)時執行特定的操作。 – Nihamata

回答

1

由於傑瓦西奧艾米建議,你需要使用AspectJ,不Spring AOP。如果您處於Spring環境中,則可以使用use AspectJ within Spring而不是Spring AOP,這是沒有問題的。如果你還沒有使用Spring,AOP不是開始使用它的理由,AspectJ可以在沒有Spring的簡單Java SE(或EE)版本中工作。

你所需要的是:

  • 與AspectJ編譯AJC編譯您的看點代碼。 (你也可以用它編譯你的整個應用程序,因爲它也是Java編譯器javac的一個更換。)
  • 創建負載時織構aop.xml文件,以使應用程序能夠編織縱橫碼在課程加載期間即時進入第三方庫。我把它留給你來弄清楚如何做到這一點,只需檢查LTW documentation
  • 通過-javaagent:/path/to/aspectjweaver.jar開關在命令行上使用AspectJ編織代理啓動您的JVM或應用程序服務器。

現在你想要什麼樣的樣子?讓我們嘗試一些變體並改進切入點以使其匹配。但首先,讓我們爲我們的實驗階段,幾樣第三方類(FooBar)和一個小驅動程序(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.barFoo.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.zotBar.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問題。請享用!

0

避免使用Spring AOP。它不會允許你這樣做,因爲Spring創建了一個代理bean來進行bean化,因此,只有對「proxy」的調用纔會被調用。這意味着,如果你有一個第三方類的bean(讓它叫做fooBean),當你懷疑fooBean.foo()時,你實際上是通過具有方面邏輯的代理,但是一旦執行了foo()方法,那麼內部調用例如撥打bar()將不再認爲代理人,所以,那裏不會有任何方面。

Mybe運行到一個更復雜的解決方案,使用純AspectJ可以幫助你,因爲它不是基於proxys,它只是提高了編譯字節碼

相關問題