Вы ошиблись, что Plain.getValue(..)
вызывается Connector.getStart(..)
, потому что в многопоточной среде это не так. Позвольте мне доказать это с небольшим количеством подстройке к методу getValue(..)
, печать трассировки стека:
package de.scrum_master.app;
public class Plain {
public void getValue(String s1) {
System.out.println("Plain getValue: " + s1);
new Exception().printStackTrace(System.out);
}
}
Кстати, я переместил все свои классы для упаковки de.scrum_master.app
, потому что с использованием пакета по умолчанию не рекомендуется в Java, а также AspectJ не нравится, когда пытается совместить точки.
журнала консоли (многопоточный):
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(..)
. Если мы также настроить getStart(..)
так, чтобы позвонить run()
метод Нить непосредственно (т.е. не запускается новый поток, но выполняется в том же потоке) вместо start()
, ситуация меняется:
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)
В этой ситуации мы могли бы использовать динамический cflow()
(поток управления AspectJ,) Pointcut так:
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.run()
назад к оригиналу h.start()
, чтобы восстановить многопоточность. Давайте также удалим линию printStackTrace(..)
от Plain.getStart(..)
.
Решение:
Отказ от ответственности: Я не люблю синтаксис @AspectJ аннотации стиля, поэтому я переключение на родной синтаксис. Это гораздо более выразительным, и мы можем добиться того, что мы хотим, чтобы более легко с точки зрения ВТД (определение межтипового), потому что
- в родной синтаксис мы можем просто объявить дополнительную переменную-член экземпляра для данного класса в то время как
- в синтаксисе @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(..)");
}
}
Консольный журнал с аспектом:
Как вы можете видеть, нить Handler
, начатая с App.main(..)
, игнорируется аспектом, как ожидалось. Handler
, начинающийся с Connector.getStart(..)
, вызывает аспект.
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