gpt4 book ai didi

java - spectj 跨线程切入点

转载 作者:行者123 更新时间:2023-11-30 02:41:11 26 4
gpt4 key购买 nike

我对 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);
}
}

我想要一个仅在 Connector.getStart() 调用 Plain.getValue() 时触发的切入点。

这可能吗?谢谢。

最佳答案

您错误地认为 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)

在这种情况下,我们可以使用 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.run() 恢复到原始的 h.start() 以恢复多线程情况。我们还可以从 Plain.getStart(..) 中删除 printStackTrace(..) 行。

解决方案:

免责声明:我不喜欢注释式 @AspectJ 语法,因此我切换到 native 语法。它更具表现力,我们可以更轻松地在 ITD(类型间定义)方面实现我们想要的目标,因为

  • 在 native 语法中,我们只需为给定类声明一个附加实例成员变量即可
  • 在 @AspectJ 语法中,我们必须声明目标类来实现具有默认实现的接口(interface),而默认实现又会携带用于手动簿记的成员变量。

让我们修改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(..)");
}
}

控制台日志与方面:

如您所见,从 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

关于java - spectj 跨线程切入点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41649883/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com