2017-01-14 11 views
0

私はAspectJアノテーションfor Javaで新しく、クロススレッド呼び出しにポイントカットを付けることが可能かどうか疑問に思っています。aspectjクロススレッドのポイントカット

public class App { 
    public static void main(String[] args) { 
     new Connector().getStart("testtest"); 
    } 
} 
public class Connector { 
    public void getStart(String s1) { 
     Handler h = new Handler(s1); 
     h.start(); 
    } 
} 
public class Handler extends Thread { 
    String s1; 

    public Handler(String s1) { 
     this.s1 = s1; 
    } 

    public void run() { 
     new Plain().getValue(s1); 
    } 
} 
public class Plain { 
    public void getValue(String s1) { 
     System.out.println("Plain getValue: " + s1); 
    } 
} 

私はPlain.getValue()Connector.getStart()によって呼び出されたときにのみトリガーポイントカットを持っていると思います。ここでは

はコードです。

可能ですか?ありがとう。

答えて

1

マルチスレッド環境ではがあるため、Plain.getValue(..)Connector.getStart(..)によって呼び出されていると誤解しています。私は、スタックトレースを印刷し、getValue(..)方法に少し微調整でそれを証明してみましょう:

​​

ちなみに、私は、デフォルトのパッケージを使用すると、Javaで推奨されているのでde.scrum_master.appをパッケージ化するために、すべてのクラスを移動しても、AspectJのきpointcutsにマッチさせようとしているときに好きではありません。

コンソールログ(マルチスレッド):

Plain getValue: testtest 
java.lang.Exception 
    at de.scrum_master.app.Plain.getValue(Plain.java:4) 
    at de.scrum_master.app.Handler.run(Handler.java:9) 

参照してください?ログにはConnector.getStart(..)というトレースはありません。我々はまた、代わりにstart()のように直接スレッドのrun()方法(すなわち、新しいスレッドを開始するが、同じスレッドで実行していない)を呼び出すようgetStart(..)を微調整した場合、状況は変わる:

package de.scrum_master.app; 

public class Connector { 
    public void getStart(String s1) { 
     Handler h = new Handler(s1); 
     h.run(); 
    } 
} 

コンソールログ(シングルスレッド化):このような状況で

Plain getValue: testtest 
java.lang.Exception 
    at de.scrum_master.app.Plain.getValue(Plain.java:4) 
    at de.scrum_master.app.Handler.run(Handler.java:9) 
    at de.scrum_master.app.Connector.getStart(Connector.java:4) 
    at de.scrum_master.app.App.main(App.java:3) 

我々はこのようにAspectJのダイナミックcflow()(制御フロー)ポイントカットを使用することができます。

package de.scrum_master.aspect; 

import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 

@Aspect 
public class SingleThreadAspect { 
    @Before("execution(* de.scrum_master.app.Plain.getValue(..)) && cflow(execution(* de.scrum_master.app.Connector.getStart(..)))") 
    public void interceptControlFlow(JoinPoint thisJoinPoint) { 
     System.out.println(thisJoinPoint); 
    } 
} 

アドバイスはあなたが望むように起動されます。 私の答えcflow()の最初に説明された理由から、スレッド間での直接的な制御フローなどの問題はないため、スレッド間では機能しません(またできません)。各スレッドの制御フローは、run()メソッドで始まります。これがマルチスレッドの全体概念です。

疑いの余地があってもクロススレッドコントロールフローのようなものを実際にエミュレートしたい場合は、手動簿記を実行する必要があります。

最初に、を元のh.start()に戻して、マルチスレッドの状況を元に戻しましょう。 からprintStackTrace(..)行を削除します。

ソリューション:

免責事項:私は注釈スタイルの@AspectJ構文を好きではないので、私はネイティブの構文に切り替えています。それははるかに表現力であり、我々は、我々はちょうど ながら、与えられたクラスの追加インスタンスのメンバ変数を宣言することができますネイティブの構文で

  • ので、ITD(インタータイプ定義)の面でより簡単に欲しいものを達成することができます
  • @AspectJ構文では、ターゲットクラスを宣言して、デフォルトの実装を持つインタフェースを実装する必要があります。このインタフェースは、マニュアルブックキーピングのメンバー変数を持ちます。

Appを変更して、Handlerスレッドを直接開始するようにしてください。我々は、スレッドがPlain.getValue(..)の外で開始されたとしてそこに私たちのアドバイスをトリガーにしたくないので、これは私たちの負のテストケースである:

package de.scrum_master.app; 

public class App { 
    public static void main(String[] args) throws InterruptedException { 
     // The aspect should ignore this thread 
     new Handler("foo").start(); 
     // Wait a little while so as not to mess up log output 
     Thread.sleep(250); 
     new Connector().getStart("testtest"); 
    } 
} 

コンソールログ様相なし:

Plain getValue: foo 
Plain getValue: testtest 

アスペクト比:

package de.scrum_master.aspect; 

import de.scrum_master.app.*; 

public aspect CrossThreadAspect { 
    // Declare a new instance member for our bookkeeping 
    private boolean Handler.cflowConnectorGetStart = false; 

    // If handler thread is started from Connector.getStart(..), set a mark 
    before(Handler handler) : 
     call(void Handler.start()) && 
     cflow(execution(* Connector.getStart(..))) && 
     target(handler) 
    { 
     System.out.println(thisJoinPoint + "\n doing bookkeeping"); 
     handler.cflowConnectorGetStart = true; 
    } 

    // If current thread is a marked Handler, log it 
    before() : 
     execution(* Plain.getValue(..)) && 
     if(Thread.currentThread() instanceof Handler) && 
     if(((Handler) Thread.currentThread()).cflowConnectorGetStart) 
    { 
     System.out.println(thisJoinPoint + "\n triggered from parent thread via Connector.getStart(..)"); 
    } 
} 

コンソールログaspect:

ご覧のとおり、App.main(..)から開始されたHandlerスレッドは、期待通りに無視されます。 Connector.getStart(..)から開始されたHandlerは、この側面をトリガします。

Plain getValue: foo 
call(void de.scrum_master.app.Handler.start()) 
    doing bookkeeping 
execution(void de.scrum_master.app.Plain.getValue(String)) 
    triggered from parent thread via Connector.getStart(..) 
Plain getValue: testtest 
関連する問題