マルチスレッド環境ではがあるため、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